声明
a[i] += a[j] * a[k];
会在一个可能执行几千到几百万次的循环中执行几千次。索引 i
、j
和 k
表示对 a
中条目的随机访问,可以通过语句设置
i = i_index[l];
j = j_index[l];
k = k_index[l];
其中 l
是 for
循环的索引。整数数组 i_index
、j_index
和 k_index
在程序开始时设置,可能会偶尔更改。
内存指针数组是另一种选择。例如
*ap1[l] += *ap2[l] * (*ap3[l]);
其中内存指针数组ap1
、ap2
和ap3
是预先设置的,指向最初由i_index
、j_index
和 k_index
数组。它们也可能会偶尔更改。
第一种方法看起来比第二种方法更干净,但它似乎更慢,除非有某种方法可以向编译器提供额外的信息。 XCode 中的 GCC 编译器似乎无法提前发现 i_index
、j_index
和 k_index
或 ap1
、ap2
和 ap3
大多数时候是不变的。有什么方法可以提醒 gcc 编译器提高性能?
最佳答案
您不太可能找到一个选项让编译器有效地缓存您显式计算并保存在第二个版本代码中的第一个版本代码中的指针值。这是因为编译器需要发出代码来生成和保存一个非常大的数据结构来缓存这些值,这不是典型的编译器行为。
但是,根据您的目标架构,这可能无关紧要。许多体系结构具有“间接基+索引”寻址模式,这将用于访问 a[i] += a[j] * a[k] 中的
,并且在现代内核上,这些与普通的“间接”寻址模式相比没有任何性能损失(即 - 一条指令将 a
;i
乘以 a 的大小[0]
,将其添加到a
的基地址并解引用结果)。分析您的目标架构并查看。
对于任何一个版本,可能都可以改进的一件事是使用一个 struct
数组而不是三个单独的数组,以便l
的每个值都连续保存在内存中:
i = index[l].i;
j = index[l].j;
k = index[l].k;
或
*ap[l].i += *ap[l].j * *ap[l].k;
这意味着您的代码正在通过 index
或 ap
数组进行一次线性遍历,而不是同时进行三次线性遍历,这应该有助于预取器识别什么你在做什么。
关于c - 索引内存指针数组与 ANSI C 中的间接整数索引数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10875071/