在教科书Computer Systems: a Programmer's Perspective
中,有一些令人印象深刻的基准测试来优化行主顺序访问。
我创建了一个小程序来测试自己,从行主要访问到列主要访问的简单更改是否会在我自己的机器上产生巨大的影响。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define N 30000
int a[N][N] = { 0 };
int main() {
srand(time(NULL));
int sum = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
a[i][j] = rand() % 99;
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
sum += a[i][j];
}
}
}
平均而言,行主订单访问在我的系统上需要8.42s
(n=5
次试验(,而列主订单访问在我的系统上需要30.12s
(n=5
次试验(,这非常重要。
从表面上看,优化应该是一件非常简单的事情。
为什么现代编译器不优化这些方案?
大多数循环不是由简单的 sum 语句组成,而是在循环迭代之间具有副作用和依赖关系。
并非您在循环中执行的所有操作都是可交换的,因此优化器必须实际理解作为循环的一部分发生的所有操作,以确保它不会改变其含义,包括调用的任何系统 API 的内容、动态加载库中的代码等。
现在这只是一个猜测,但我希望有人尝试过,意识到优化没有足够的关于正在运行的代码的信息来触发大多数时间,然后专注于并行执行优化,这可能是大多数代码库中更大的优化机会。