3

该声明

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

将在可能执行数千到数百万次的循环中执行数千次。表示对 中的条目的随机访问的索引ij和可以由语句设置ka

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

哪里lfor循环的索引。整数数组i_index, j_index, 和k_index是在程序开始时设置的,可能偶尔会更改。

内存指针数组是另一种选择。例如

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

其中内存指针数组ap1ap2ap3被预先设置为指向最初由 、 和 数组标识i_indexj_index位置k_index。它们也可能偶尔更改。

第一种方法看起来比第二种方法更干净,但它似乎更慢,除非有某种方法可以向编译器提供额外的信息。似乎 XCode 中的 GCC 编译器无法提前发现i_index, j_index, 和k_indexor ap1, ap2, 并且ap3大部分时间是不变的。有什么方法可以提醒 gcc 编译器以提高性能?

4

1 回答 1

3

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

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

对于任一版本,可能会改进的一件事是使用structs 的数组而不是三个单独的数组,以便每个值所需的三个值l连续保存在内存中:

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

或者

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

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

于 2012-06-04T00:50:46.923 回答