这是我用来创建不同排序数组的代码:
const unsigned int height = 1536;
const unsigned int width = 2048;
uint32_t* buffer1 = (uint32_t*)malloc(width * height * BPP);
uint32_t* buffer2 = (uint32_t*)malloc(width * height * BPP);
int i = 0;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
buffer1[x+y*width] = buffer2[i++];
任何人都可以解释为什么使用以下作业:
buffer1[i++] = buffer2[x+y*width];
而不是代码中的那个花费两倍的时间?
答案 0 :(得分:4)
这可能取决于CPU缓存行为(12MB时,你的图像远远超过了iphone3gs内ARM Cortex A8中的256KB L2缓存)。
第一个示例按顺序访问读取数组,这很快,但必须按顺序访问写入数组,这很慢。
第二个例子恰恰相反 - 写入数组以快速,连续的顺序写入,读取数组以较慢的方式访问。在这种工作量下,写入未命中的成本明显低于读取失误。
如果你想了解更多关于此类事情的话,建议阅读Ulrich Drepper的文章What Every Programmer Should Know About Memory。请注意,如果将此操作包含在函数中,那么如果在指针参数上使用restrict
限定符,则可以帮助优化器生成更好的代码,如下所示:
void reorder(uint32_t restrict *buffer1, uint32_t restrict *buffer2)
{
int i = 0;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
buffer1[x+y*width] = buffer2[i++];
}
(restrict
限定符向编译器承诺两个指针指向的数据不重叠 - 在这种情况下,无论如何这对于函数来说都是必要的。)
答案 1 :(得分:2)
第一个中的每个像素访问都有一个线性locality of reference,第二个像素访问会在每次读取时打开缓存,必须为每个读取主内存。
如果写入必须进入主存储器,则处理器可以更有效地处理具有错误位置的写入而不是读取,该写入可以与另一个读取/算术操作并行发生。如果读取错过了缓存,它可以完全停止处理器,等待更多数据通过缓存层次结构进行过滤。