基于条件数据传送比基于条件控制转移性能高。因为流水线的执行方式,使用基于条件控制可能导致预测错误,导致指令数目飙高。而基于条件数据传送的方式不依赖条件数据,让流水线一直可以满载正常执行。但要考虑计算不同条件结果的成本,如果成本高就不划算了。
64 位 cpu 能将调动过程信息和参数记录在寄存器中(比 x86 多了 8 个寄存器),不需要对这些信息读写栈,大大提高了性能。
流水线的并行模式增加了系统的吞吐量,但也带来了少量延迟。若流水线深度过深,延迟增大,导致吞吐量可能下降。
流水线的示例:一堆车经过洗车机,不同的步骤同时执行。
组合电路显示出一种 React 响应式的特性。
消除循环的低效率。即尽可能将成本高的东西在循环外做,循环内只使用结果。
减少过程调用,内联。过程调用需要储存返回地址、传递参数等开销,需要很多指令。
消除不必要的存储器引用。使用局部值变量,以便让它能够存储到寄存器中,比访问内存快得多。使用指针和数组都会导致读内存。
循环展开。将本来一个个轮着来的改成每次多个,减少次数。利用了流水线来处理展开的操作,提高了并行度,并且某种方面减少了操作之间的依赖性。
提高并行性。通过更改多个累积变量的结合方式,减少依赖性,能提高流水线的并行处理效率。
使用 SIMD,直接提高并行度。
//基于条件控制转移的示例
void minmax1(int a[], int b[], int n)
{
int i;
for(i = 0; i < n; i++)
{
if (a[i] > b[i])
{
int t = a[i];
a[i] = b[i];
b[i] = t;
}
}
}
// 基于条件数据传送的实例
void minmax2(int a[], int b[], int n)
{
int i;
for (i = 0; i < n; i++)
{
int min = a[i] < b[i] ? a[i] : b[i];
int max = a[i] < b[i] ? b[i] : a[i];
a[i] = min;
b[i] = max;
}
}
打开 -o2 或更高级别的 -o3 等优化,能够使用更适合的指令和代码结构改变。
不要因为提高效率损害正确性,引入足够的测试。
使用均匀的哈希函数
局部性原理:倾向于引用邻近于其他最近引用过的数据项的数据项(空间局部性),或者最近引用过的数据项本身(时间局部性)。
具有良好局部性的程序比局部性差的程序性能高。因为缓存是基于局部性的。
取指令局部性:循环体代码会被执行很多次,因此它具有良好的时间局部性。循环体越小,循环迭代次数越多,局部性就越好。
由于层次结构中较低层次离 cpu 较远,访问慢,为了补偿这些较长的时间,倾向于使用较大的缓存块。
//会抖动的版本,虽然具有良好的局部性,但性能不高 float dotprod(float x[8],float y[8]) { float aum = 0.0; int i; for( i = 0; i < 8; i++) sum += x[i] * y[i]; return sum; }
//只需要将 x 定义为 float x[12],给它后面增加填充即可以避免这个冲突不命中 (p415) ```
.NET 的线程池代码貌似用了尾部工作偷取的方法来避免缓存失效,具体如何需要再去看看,到时候更新上来。
建议使用写回和写分配模型。
使用分块技术提高局部性(p433)。