Performance 在这种内存访问模式中,硬件预取器是否有好处?

Performance 在这种内存访问模式中,硬件预取器是否有好处?,performance,hardware,ram,prefetch,Performance,Hardware,Ram,Prefetch,我有两个数组:A带有N\u A随机整数和B带有N\u B介于0和(N\u A-1)之间的随机整数。我使用B中的数字作为以下循环中A的索引: for(i = 0; i < N_B; i++) { sum += A[B[i]]; } (i=0;i

我有两个数组:
A
带有
N\u A
随机整数和
B
带有
N\u B
介于
0
(N\u A-1)
之间的随机整数。我使用
B
中的数字作为以下循环中
A
的索引:

for(i = 0; i < N_B; i++) {
    sum += A[B[i]];
}
(i=0;i{ 总和+=A[B[i]; } 在英特尔i7-3770上进行试验,
N_A
=2.56亿,
N_B
=6400万,此循环仅需.62秒,相当于大约9纳秒的内存访问延迟


由于这个延迟太小,我想知道硬件预取器是否在起作用。有人能解释一下吗?

由于这些元素是连续的,因此硬件预取程序可以看穿您的第一级间接寻址(
B[i]
)。它能够提前发出多个预取,因此您可以假设对B的平均访问将命中缓存(L1或L2)。但是,预取器无法预测随机地址(存储在B中的数据)并从A中预取正确的元素。您仍然必须在对A的几乎所有访问中执行内存访问(忽略由于重复使用行而偶尔出现的幸运缓存命中)

您看到如此低延迟的原因是,对的访问是非序列化的,CPU可以同时访问A的多个元素,因此时间不仅仅是累积的。实际上,您在这里测量内存BW,检查访问64M个元素的总时间,而不是内存延迟(访问单个元素的时间)

CPU内存单元的合理“快照”应显示几个未完成的请求-对
B[i]
B[i+64]
。。。(当每个请求获取一个64字节的行时,中间访问应该简单地合并),所有这些可能都是反映
i
未来值的预取,与根据先前获取的
B
元素对
a
元素的随机访问混合在一起


要测量延迟,您需要每次访问都取决于前一次访问的结果,例如,将A中每个元素的内容作为下一次访问的索引。

CPU在指令流中提前充电,并将同时处理多个未完成的负载。该流如下所示:

load b[0]
load a[b[0]]
add
loop code

load b[1]
load a[b[1]]
add
loop code

load b[1]
load a[b[1]]
add
loop code

...
迭代只由循环代码序列化,循环代码运行得很快。所有加载都可以并发运行

我怀疑您想要对随机、不可预测、序列化的内存加载进行基准测试。在现代CPU上,这实际上相当困难。尝试引入一个牢不可破的依赖链:

int lastLoad = 0;
for(i = 0; i < N_B; i++) {
    var load = A[B[i] + (lastLoad & 1)]; //be sure to make A one element bigger
    sum += load;
    lastLoad = load;
}
int lastLoad=0;
对于(i=0;i
这要求执行最后一次加载,直到可以计算下一次加载的地址