我已经更加注意为缓存优化代码。我很好奇以下哪一项是添加两个数组的更便于缓存的方法。代码很快。
struct A {
var x, y, z: [Int]
}
func add1(a: inout [A]) {
for i in 0 ..< a.count {
a[i].z = a[i].x + a[i].y
}
}
func add2(x: [Int], y:[Int], z: inout [Int]) {
for i in 0 ..< x.count {
z[i] = x[i] + y[i]
}
}
我担心的是,在add2
中,由于x
,y
和z
在内存中不必彼此靠近,因此本地性的好处可能会减少。例如,假设将x[0]
加载到缓存中,然后将y[0]
加载到缓存中。 y[0]
附近的数据是否可以在缓存中覆盖x[0]
附近的数据,以便需要从ram进行新的读取来加载x[1]
?如果可以,add1
是否可以解决此问题?
答案 0 :(得分:0)
在具有直接映射的缓存的处理器上,像add2
这样的访问模式可能是一个问题,并且仅在阵列的地址完全错误的情况下仍然如此。使用典型的4或8路集关联缓存,即使使用最大不吉利的数组地址,这里也没有问题:如果包含x[0]
和y[0]
和z[0]
的块全部映射到同一集合,它们仍然适合并且不会互相弹出。直接映射的缓存确实遭受了您担心的冲突遗漏,这是为什么它们现在很少见的一部分,但是还有更多的原因。
实际上,像add2
这样的访问模式非常好,因为根据所执行的操作,它也可以自动向量化。这不是通过溢出检查的加法来完成的(检查的加法很难矢量化),而是通过包装的加法&+
和compiler can use movdqu
来在同一位置加载和存储两个Int时间,然后paddq
同时添加两个Int。