前面说的是数据缓存,现在看看指令缓存命中率该如何提高。
有一个数组array
,数组元素内容为0-255
之间的随机数:
int array[N];
for (i = 0; i < TESTN; i++)
array[i] = rand() % 256;
现在,要把数组中数字小于128的元素置为0,并且对数组排序。
大家应该都能想到,有两种方法:
for(i = 0; i < N; i++) {
if (array [i] < 128)
array[i] = 0;
}
sort(array, array +N);
先排序后遍历的速度会比较快,为什么?
因为在for
循环中会执行很多次if
分支判断语句,而CPU
拥有分支预测器。
如果分支预测器可以预测接下来要执行的分支(执行if
还是执行else
),那么就可以提前把这些指令放到缓存中,CPU执行的时候就会很快了。
如果一个数组的内容完全随机的话,那么分支预测器就很难进行正确的预测。但如果数组内容是有序的,它就会根据历史命中数据的情况对未来进行预测,那命中率就会很高,所以先排序后遍历的速度会比较快。
怎么验证指令缓存命中率的情况呢?
在Linux
下,可以使用Perf
性能分析工具进行验证。通过-e
选项,指定branch-loads
和branch-loads-misses
事件,可以分别统计出分支预测成功的次数和 分支预测失败的次数 ,通过L1-icache-load-misses
事件也能统计一级缓存中指令未命中的次数。但是,这些性能事件都属于硬件事件,perf
工具能否统计这些事件取决于CPU
是否支持以及芯片原厂是否去实现了该接口,我看很多都是不支持或者没实现的。
另外,在Linux内核中,可以看到大量的likely
和unlikely
宏,并且它们都出现if
语句中,这 两个宏的作用就是为了提高性能 。
这是显示预测概率的宏,如果你觉得CPU的分支预测不准,但if
中条件为"真"的概率很高,那么你就可以使用likely()
括起来,以此提升性能。
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
if (likely(a == 1)) …
全部0条评论
快来发表一下你的评论吧 !