我们写程序的目的就是使它在任何情况下都可以稳定工作,一个运行速度很快但是结果错误的程序并没有任何用处。
在程序开发和优化的过程中,我们必须考虑代码使用的方式,以及影响它的关键因素。通常,我们必须在程序的简洁性与它的运行速度之间做出权衡,今天我们就来聊一聊如何优化程序的性能。
-
1. 减小程序计算量
-
1.1 示例代码
-
1.2 分析代码
-
1.3 改进代码
-
-
2. 提取代码中的公共部分
-
2.1 示例代码
-
2.2 分析代码
-
2.3 改进代码
-
-
3. 消除循环中低效代码
-
3.1 示例代码
-
3.2 分析代码
-
3.3 改进代码
-
-
4. 消除不必要的内存引用
-
4.1 示例代码
-
4.2 分析代码
-
4.3 改进代码
-
-
5. 减小不必要的调用
-
5.1 示例代码
-
5.2 分析代码
-
5.3 改进代码
-
-
6. 循环展开
-
6.1 示例代码
-
6.2 分析代码
-
6.3 改进代码
-
-
7. 累计变量,多路并行
-
7.1 示例代码
-
7.2 分析代码
-
7.3 改进代码
-
-
8. 重新结合变换
-
8.1 示例代码
-
8.2 分析代码
-
8.3 改进代码
-
-
9 条件传送风格的代码
-
9.1 示例代码
-
9.2 分析代码
-
9.3 改进代码
-
1. 减小程序计算量
1.1 示例代码
for (i = 0; i < n; i++) {
int ni = n*i;
for (j = 0; j < n; j++)
a[ni + j] = b[j];
}
1.2 分析代码
代码如上所示,外循环每执行一次,我们要进行一次乘法计算。i = 0,ni = 0;i = 1,ni = n;i = 2,ni = 2n。因此,我们可以把乘法换成加法,以n为步长,这样就减小了外循环的代码量。
1.3 改进代码
int ni = 0;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++)
a[ni + j] = b[j];
ni += n; //乘法改加法
}
计算机中乘法指令要比加法指令慢得多。
2. 提取代码中的公共部分
2.1 示例代码
想象一下,我们有一个图像,我们把图像表示为二维数组,数组元素代表像素点。我们想要得到给定像素的东、南、西、北四个邻居的总和。并求他们的平均值或他们的和。代码如下所示。
up = val[(i-1)*n + j ];
down = val[(i+1)*n + j ];
left = val[i*n + j-1];
right = val[i*n + j+1];
sum = up + down + left + right;
2.2 分析代码
将以上代码编译后得到汇编代码如下所示,注意下3,4,5行,有三个乘以n的乘法运算。我们把上面的up和down展开后会发现四格表达式中都有i*n + j。因此,可以提取出公共部分,再通过加减运算分别得出up、down等的值。
leaq 1(%rsi), %rax # i+