背景问题
如果要进行1-10000的累乘,该如何实现?
最常见的做法就是使用for循环,那么for循环是否能进行优化呢,提升到极致的性能会是什么样子呢。
实现
为了避免编译器过度,我们使用Debug模式进行性能分析。
- 方法一
1 | int sum = 1; |
times | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
duration (ms) | 17.929 | 17.434 | 17.804 | 17.816 | 16.706 | 17.201 | 18.129 | 17.049 | 17.368 | 17.823 |
这是最基础的for循环执行10次的性能。接下来我们开始对其进行优化。
- 方法二
方法一
中每次计算需要进行一次循环判断,总的指令数量为10000次循环控制指令+10000次计算指令。
通常对for循环进行性能优化的办法是循环展开,即在一次循环中进行多次计算,减少循环控制相关指令的数量,使有效的计算指令的占比提升。因此在 方法二
中我们减少循环指令的占比,在一次循环中进行4次计算。
1 | int sum = 1; |
times | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
duration (ms) | 16.979 | 17.056 | 16.99 | 17.047 | 17.025 | 17.04 | 16.979 | 17.032 | 17.017 | 17.013 |
这次的循环指令数量为2500次,10000次计算。
可以看到,这次的for循环性能相比于 方法一
有了一定的提升,但是总体来说提升并不大,我们先不进行过度深入的理解,就当作循环指令的开销比较小,所以单纯减少循环指令对性能的提升并不明显。
这里首先理解一下循环展开是为了什么,单纯的减少本身就开销比较小的控制指令,以此来优化性能?也许并不是!
这里就需要了解下现在处理器的一个特性,就是能够在一个始终周期内执行多条指令,本文中不做详细展开,可以先通过这篇文章了解一下这一特性 <<指令流水线>>.
- 方法三
现在我们来实现一个可以利用处理器指令流水线特性的for循环代码。
1 | int sum0=1, sum1=1, sum2=1, sum3=1; |
times | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
duration (ms) | 5.038 | 5.073 | 5.755 | 4.924 | 5.231 | 5.56 | 5.59 | 4.717 | 5.782 | 5.017 |
可以看到,利用了指令流水线后,程序的性能是大幅度提升的,并且这是没有进行编译器优化的代码。
总结
所有软件都是在硬件的基础上运行的,所以,如果想要写出非常高效的代码,那么就必须得熟悉或者说对计算机体系结构有所了解,否则就没法使代码达到极致的性能。
同样利用硬件特性的程序,在多线程程序中也有体现,可以看看下一篇 多线程程序性能优化.