我最近被问及一段代码来“就地”抽取/下采样数组。这个“抽取”函数接受一个整数数组,并将一个条目存储i
在数组中索引处的偶数索引处i/2
。它对数组中的所有条目执行此操作。
这会将原始数组中的所有偶数索引条目移动到数组的前半部分。然后可以将数组的其余部分初始化为 0。总体结果是一个数组,它保留了原始数组中的所有偶数索引条目(通过将它们移动到前半部分),而数组的后半部分为 0。这显然用于在信号处理中对信号进行下采样。
代码看起来像这样:
void decimate (vector<int>& a) {
int sz = a.size();
for (int i =0; i < sz; i++) {
if (i%2 == 0) {
a[i/2] = a[i];
}
}
for (int i =(sz-1)/2; i < sz; i++) a[i] = 0;
}
在提出将某些变量保留在寄存器中的基本改进建议后,我找不到任何进一步的优化方法,但不确定是否无法完成。
有没有办法可以优化循环中的内存访问模式以获得更好的缓存性能? 或者任何其他方法来优化将数组压缩/下采样到前半部分的主要复制操作?(例如,通过支持它的平台的矢量化)
for (int i =0; i < sz; i++) {
if (i%2 == 0) {
a[i/2] = a[i];
}
}
是否有任何循环转换(例如平铺/条带挖掘)可以为这种抽取循环产生高效的代码?
编辑:下面的答案中建议了几种不同的方法,这些方法似乎利用 memset/fill 或指针算法来提高速度效率。这个问题主要关注是否有明确定义的循环转换可以显着改善局部性或缓存未命中(例如,如果它是具有两个循环的循环嵌套,则可能会考虑循环平铺以优化缓存未命中)