ANSI C 中的索引内存指针数组与间接整数索引数组



语句

a[i] += a[j] * a[k];

将在循环中执行数千次,该循环可能执行数千到数百万次。索引ijk,表示对a条目的随机访问,可以通过语句设置。

i = i_index[l];
j = j_index[l];
k = k_index[l];

其中lfor循环的索引。整数数组i_indexj_indexk_index在程序开始时设置,可以偶尔更改。

内存指针数组是一种替代方法。例如

*ap1[l] += *ap2[l] * (*ap3[l]);

其中内存指针数组ap1ap2ap3是预先设置的,指向最初由i_indexj_indexk_index数组标识的位置。它们也可能偶尔更换。

第一种方法看起来比第二种方法更干净,但除非有某种方法向编译器提供其他信息,否则它似乎更慢。XCode中的GCC编译器似乎没有办法提前发现i_indexj_indexk_indexap1ap2ap3大多数时候是不变的。有什么方法可以让 gcc 编译器提醒提高性能?

您不太可能找到一个选项来让编译器有效地缓存在代码的第一个版本中,您正在显式计算并保存在代码的第二个版本中。 这是因为编译器需要发出代码来生成和保存非常大的数据结构来缓存这些值,这不是典型的编译器行为。

但是,根据您针对的体系结构,这可能无关紧要。 许多架构都有"间接基+索引"寻址模式,这是用于访问a[i] += a[j] * a[k];中的a的模式,并且在现代内核上,这些架构对普通的"间接"寻址模式没有任何性能损失(也就是说 - 一条指令将乘以a[0]的大小i,将其添加到a的基址并取消引用结果)。 分析您的目标架构并查看。

对于任一版本,有一件事可能会很好地改善情况,那就是使用 struct 的数组而不是三个单独的数组,以便每个 l 值所需的三个值连续保存在内存中:

i = index[l].i;
j = index[l].j;
k = index[l].k;

*ap[l].i += *ap[l].j * *ap[l].k;

这意味着您的代码正在通过 indexap 数组进行一次线性遍历,而不是同时进行三次线性遍历,这应该有助于预取器识别您在做什么。

最新更新