为何使用__mm_prefetch的基准测试运行速度更慢?
为何使用__mm_prefetch的基准测试运行速度更慢?
嘿,我最近在啃C++性能优化的知识点,试着用__mm_prefetch来优化数组求和的代码,结果跑基准测试的时候傻了——居然比不用预取的时候还慢!
我用的基准测试代码框架大概是这样的:
#include <benchmark/benchmark.h> #include <vector> #if defined(__GNUC__) || defined(__clang__) #define PREFETCH(addr, hint) __builtin_prefetch(addr, 0, hint) #elif defined(_MSC_VER) #include <xmmintrin.h> #define PREFETCH(addr, hint) _mm_prefetch(reinterpret_cast<const char*>(addr), hint) #endif // 不带预取的数组求和基准测试 static void SumWithoutPrefetch(benchmark::State& state) { std::vector<int> vec(state.range(0)); for (auto _ : state) { int sum = 0; for (int x : vec) { sum += x; } benchmark::DoNotOptimize(sum); } } BENCHMARK(SumWithoutPrefetch)->Range(1<<10, 1<<20); // 带预取的数组求和基准测试 static void SumWithPrefetch(benchmark::State& state) { std::vector<int> vec(state.range(0)); for (auto _ : state) { int sum = 0; const int* data = vec.data(); size_t size = vec.size(); for (size_t i = 0; i < size; ++i) { // 尝试提前预取下4个位置的元素 if (i + 4 < size) { PREFETCH(&data[i + 4], 0); } sum += data[i]; } benchmark::DoNotOptimize(sum); } } BENCHMARK(SumWithPrefetch)->Range(1<<10, 1<<20);
为啥会出现这种反效果?我来给你掰扯几个核心原因:
- 硬件预取器比你手动操作更懂节奏:现代CPU自带的硬件预取器(比如顺序预取、跨步预取)对数组这种规律的顺序访问模式识别度拉满,会自动提前把后续数据加载到缓存里。你手动加的
__mm_prefetch完全是画蛇添足——不仅抢了硬件预取的资源,还多出来一堆额外指令,CPU要花时间执行这些无用的预取操作,自然拖慢了整体速度。 - 手动预取的时机/距离全靠蒙:你写的预取逻辑是固定提前4个元素,但实际场景里,这个距离要么太近(数据已经在缓存里了,预取白做),要么太远(预取的数据还没用到就被缓存淘汰了)。硬件预取器能根据实时的访问情况动态调整预取的时机和步长,比你写死的逻辑灵活一万倍。
- 缓存污染反而拖后腿:如果数组大小刚好能塞进L1缓存,预取完全没必要;如果数组太大,手动预取可能把缓存里还需要的有效数据挤出去,反而导致更多缓存未命中,速度自然就慢了。
当然,不是说__mm_prefetch没用,它的用武之地是那些硬件预取器识别不了的非规律访问场景——比如跳着访问数组元素、遍历链表这种散列的结构,这时候手动预取才能真正发挥加速的作用。
备注:内容来源于stack exchange,提问作者Tom McLean
相关产品推荐
相关产品推荐

