大型3D阵列的性能:连续1D存储与T ***

时间:2014-05-08 12:45:30

标签: c++ c arrays performance optimization

我想知道是否有人可以建议存储用于有限差分离散计算的大型(比如2000 x 2000 x 2000)3D阵列。在现代CPU架构上,连续存储float*是否比float***提供更好的性能? 这是一个简化的计算示例,它在整个数组上完成:

for i ...
    for j ...
        for k ...
            u[i][j][k] += v[i][j][k+1] + v[i][j][k-1]
                + v[i][j+1][k] + v[i][j-1][k] + v[i+1][j][k] + v[i-1][j][k];

Vs的

    u[i * iStride + j * jStride + k] += ...

PS: 考虑到问题的大小,存储T***是一个非常小的开销。访问不是随机的。此外,我做循环阻塞以最小化缓存未命中。我只是想知道T***案例中的三重解引用如何与一维数组中的索引计算和单一解除引用相比较。

4 个答案:

答案 0 :(得分:2)

这些不是苹果对苹果的比较:扁平数组就是这样 - 一个扁平数组,根据线性化矩形3D数组的逻辑,您的代码将其分成若干段。您可以使用单个解除引用访问数组的元素,以及一些数学运算。

另一方面,

float***允许您保留“锯齿状”的数组或数组数组,因此您可以在这样的数组中表示的结构更加灵活。当然,您需要为解除指向指针指针的指针所需的额外CPU周期付费,然后指向指针指针,最后指针(代码中的三对方括号)。

当然,如果您以真正随机的顺序访问它们,对float***的各个元素的访问速度会慢一些。但是,如果顺序不是随机的,那么您看到的差异可能很小,因为指针的值将被缓存。

float***还需要更多内存,因为你需要分配两个额外的指针级别。

答案 1 :(得分:2)

简短的回答是:基准吧。如果结果不确定,则意味着无关紧要。做什么使你的代码最具可读性。

正如@dasblinkenlight指出的那样,结构也是等价的,因为T ***可能是锯齿状的。

在最基础的层面上,这归结为算术和内存访问操作。

对于你的1D数组,正如你已经(几乎)写的那样,计算是:

 ptr = u + (i * iStride) + (j * jStride) + k
 read *ptr

用T ***:

 ptr = u + i
 x = read ptr
 ptr = x + j
 y = read ptr
 ptr = y + k
 read ptr

所以你要进行两次乘法进行两次存储器访问。

答案 2 :(得分:1)

在计算机去,人们对性能非常敏感,每个人(AFAIK)使用T [361]而不是T [19] [19](*)。这一决定基于基准测试,无论是孤立还是整个计划。 (很可能每个人都会在几年前和几年前做过这些基准测试,并且从未在最新的硬件上再次完成它们,但我的预感是单个1-D阵列仍然会更好。)

然而,相比之下,你的阵列是巨大的。由于每个案例中涉及的代码都很容易编写,我肯定会尝试两种方式和基准。


*:旁白:我认为它实际上是T [21] [21]与t [441],在大多数程序中,因为添加了额外的行以加速板边缘检测。

答案 3 :(得分:0)

尚未提及的一个问题是aliasing

您的编译器是否支持某种类型的关键字,例如restrict,以表明您没有别名? (它不是C ++ 11的一部分,因此必须是一个扩展。)如果是这样,性能可能非常接近相同。如果不是,在某些情况下可能会有显着差异。问题将出现在:

for (int i = ...) {
    for (int j = ...) {
        a[j] = b[i];
    }
}

每个外循环迭代可以加载b[i]一次并存储在整个内循环的寄存器中吗?在一般情况下,仅当数组不重叠时。编译器如何知道?它需要某种类型的restrict关键字。