我需要用另一个数组(readArray)计算一个数组(writeArray),但问题是两个数组之间的索引映射不同(写入数组的索引x处的值必须使用读取数组的索引y处的值计算),因此它不太适合缓存。
不过,我可以选择让循环顺序遍历readArray或者writeArray。
下面是一个简化的代码:
int *readArray = new int[ARRAY_SIZE]; // Array to read
int *writeArray = new int[ARRAY_SIZE]; // Array to write
int *refArray = new int[ARRAY_SIZE]; // Index mapping between read and write, could be also array of pointers instead indexes
// Code not showed here : Initialization of readArray with values, writeArray with zeroes and refArray with random indexes for mapping between readArray and writeArray (values of indexes between 0 and ARRAY_SIZE - 1)
// Version 1: Random read (browse writeArray/refArray sequentially)
for (int n = 0; n < ARRAY_SIZE; ++n) {
writeArray[n] = readArray[refArray[n]];
}
// Version 2: Random write (browse readArray/refArray sequentially)
for (int n = 0; n < ARRAY_SIZE; ++n) {
writeArray[refArray[n]] = readArray[n];
}
我曾认为缓存读取缺失比写入缺失更慢(因为CPU需要等待读取完成,如果下一条指令依赖于读取数据,但对于写入它不需要等待处理下一条指令),但是通过剖析,似乎版本1比版本2更快(版本2比版本1慢约50%)。
我也尝试了这个:
// Version 3: Same as version 2 but without polluting cache
for (int n = 0; n < ARRAY_SIZE; ++n) {
_mm_stream_si32(&writeArray[refArray[n]], readArray[n]);
}
因为我不需要读取writeArray的值,所以没有理由用写入的值来污染缓存,但是这个版本比其他版本更慢(比第1个版本慢了6700%)。
为什么写入未命中比读取未命中要慢? 为什么即使我们在之后不读取这些写入的数据,绕过缓存进行写入也比使用缓存更慢?