使用Google Benchmark来对以下函数进行基准测试:
基准测试结果:
我尝试使用Google Benchmark对这些函数进行基准测试,并在-O0和-O1下获得了明显的好处。我在几个地方学到,这可能是由于打破依赖链,如果我理解正确的话,这会导致CPU在执行我的指令时减少流水线停顿。看着-O1下的生成的汇编代码,这是有道理的:展开版本为每个求和变量使用不同的寄存器,打破了依赖链。
问题:
我尝试使用
int sumElements(int *arr, int count)
{
int sum = 0;
for (int i = 0; i < count; ++i) {
sum += arr[i];
}
return sum;
}
int sumElementsUnrolled(int *arr, int count)
{
int sumA = 0;
int sumB = 0;
for (int i = 0; i < count; i += 2) {
sumA += arr[i];
sumB += arr[i + 1];
}
return sumA + sumB;
}
基准测试结果:
--------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------
sumElements 965 ns 963 ns 723250
sumElementsUnrolled 641 ns 641 ns 1091406
我尝试使用Google Benchmark对这些函数进行基准测试,并在-O0和-O1下获得了明显的好处。我在几个地方学到,这可能是由于打破依赖链,如果我理解正确的话,这会导致CPU在执行我的指令时减少流水线停顿。看着-O1下的生成的汇编代码,这是有道理的:展开版本为每个求和变量使用不同的寄存器,打破了依赖链。
问题:
- 这个解释正确吗,还是我完全错了?
- 如果是这样,我如何在这里测量流水线停顿?运行
perf stat
显示展开版本的stalled-cycles-backend
的速率要高得多,但我不确定这是否相关。 - 如果不是这样,那么加速的原因是什么?在CPU级别上打破依赖链会产生什么效果?
#define SIZE 4096
int *initArray(int size)
{
int *arr = (int*)malloc(size * sizeof(int));
for (int i = 0; i < size; ++i) {
arr[i] = rand();
}
return arr;
}
void doBenchmark(benchmark::State &s)
{
int *arr = initArray(SIZE);
for (auto _ : s) {
int sum = sumElements(arr, SIZE);
benchmark::DoNotOptimize(sum);
}
free(arr);
}
BENCHMARK(doBenchmark);
我尝试使用
perf stat
来确定加速的原因。但是我不确定如何解释结果。