数组的迭代方向

iteration direction on an array

本文关键字:方向 迭代 数组      更新时间:2023-10-16

假设我们有两个基本类型的数组ab(例如,float),我们需要为每个有效索引i计算a[i] + b[i],并存储结果。遍历数组以最大化缓存命中率的最佳方法是什么?是正面对反面,还是正面对反面?

对于这种操作,您应该使用编译器的自动向量化。迭代小i到大i。此外,答案取决于您所说的"存储结果"的含义以及您将要迭代的项目的数量n

如果你的意思是c[i] = a[i] + b[i]n不是太小,那么你的编译器的自动矢量化器将在没有任何更改的情况下进行最佳优化。即使是MSVC也会得到正确的答案(至少对于SSE)。你的编译器将不得不对n做一些调整,而不是4的倍数(或AVX的8)和对齐,但这个成本将分摊到n上,除了很小的n,这个开销的影响可以忽略不计。如果n很小,那么你可能需要考虑对齐。到底有多小还有待确定,但我猜要比100小得多。

如果你指的是sum + = a[i] + b[i],减少,那么你确实需要考虑这个。这有一个依赖链,所以你需要展开你的循环3-10次。此外,您需要使用一个宽松的浮点模型,因为浮点算术不是关联的,没有它就无法进行自动向量化,因此将-ffast-math添加到GCC(将/fp:fast添加到MSVC)。如果展开循环并使用一个宽松的浮点模型,那么GCC、ICC、Clang和MSVC应该有效地自动向量化您的缩减。

为了利用缓存预取功能,您需要从前到后顺序读取数组。

此外,数组应该是SSE对齐的(16字节)。更重要的是,项(例如浮点数)将按其大小对齐(浮点数为4字节)。这一点很重要,这样数据就不会跨越缓存线(读取速度较慢)。

数组对齐后,您可以使用SSE/AVX在单个指令中执行4或8次操作来读取,添加和存储结果。

编辑:

你可以在这里阅读更多关于缓存预取的内容,也可以在Intel SW开发者手册中阅读更深入的描述。