c++ - 优化四重嵌套 "for"循环

标签 c++ performance for-loop nested

我正在用 C++ 开发一个 2D 数值模型,我想加速一个正在减慢我的代码速度的特定成员函数。该函数需要遍历模型中的每个 i,j 网格点,然后在 lm 上的每个网格点执行双重求和>。函数如下:

int Class::Function(void) {
    double loadingEta;
    int i,j,l,m;

    //etaLatLen=64, etaLonLen=2*64
    //l_max = 12

    for (i=0; i<etaLatLen; i++) {
        for (j=0; j < etaLonLen; j++) {
            loadingEta = 0.0;
            for (l=0; l<l_max+1; l++) {
                for (m=0; m<=l; m++) {
                    loadingEta += etaLegendreArray[i][l][m] * (SH_C[l][m]*etaCosMLon[j][m] + SH_S[l][m]*etaSinMLon[j][m]);
                }
            }
            etaNewArray[i][j] = loadingEta;
        }
    }

    return 1;
}

我一直在尝试更改循环顺序以加快速度,但无济于事。任何帮助将非常感激。谢谢!

编辑 1:

所有五个数组在我的类的构造函数中分配如下:

etaLegendreArray = new double**[etaLatLen];
for (int i=0; i<etaLatLen; i++) {
    etaLegendreArray[i] = new double*[l_max+1];
    for (int l=0; l<l_max+1; l++) {
        etaLegendreArray[i][l] = new double[l_max+1];
    }
}

SH_C = new double*[l_max+1];
SH_S = new double*[l_max+1];
for (int i=0; i<l_max+1; i++) {
    SH_C[i] = new double[l_max+1]; 
    SH_S[i] = new double[l_max+1];
}

etaCosMLon = new double*[etaLonLen];
etaSinMLon = new double*[etaLonLen];
for (int j=0; j<etaLonLen; j++) {
    etaCosMLon[j] = new double[l_max+1];
    etaSinMLon[j] = new double[l_max+1];
}

也许如果这些是一维数组而不是多维数组会更好?

最佳答案

在这里跳入 X-Y 领域。让我们尝试加速数据访问,而不是加速算法。

etaLegendreArray = new double**[etaLatLen];
for (int i=0; i<etaLatLen; i++) {
    etaLegendreArray[i] = new double*[l_max+1];
    for (int l=0; l<l_max+1; l++) {
        etaLegendreArray[i][l] = new double[l_max+1];
    }
}

不创建 double 的 3D 数组。它创建一个指针数组,指针数组指向 double 数组的指针。每个数组都是它自己的内存块,谁知道它在存储中的位置。这导致一个数据结构具有所谓的“poor spacial locality”。结构的所有部分可能散落在各处。在 3D 阵列中,您会跳到三个不同的位置,只是为了找出您的值(value)所在。

由于模拟 3D 阵列所需的许多存储 block 可能彼此相距甚远,因此 CPU 可能无法提前有效加载缓存(高速内存)而不得不停止有用的工作做并等待访问较慢的存储,可能更频繁地访问 RAM。这是一个很好的高级article on how much this can hurt表现。

另一方面,如果整个数组在一个内存块中,是“连续的”,CPU 可以读取更大的内存块,也许全部,它需要一次全部读入缓存。此外,如果编译器知道程序将使用的内存都在一个大块中,它可以执行各种常规优化,使您的程序更快。

那么我们如何得到一个全是一个内存块的 3D 数组呢?如果尺寸是静态的,这很容易

double etaLegendreArray[SIZE1][SIZE2][SIZE3];

这看起来不是你的情况,所以你要做的是分配一个一维数组,因为它将是一个连续的内存块。

double * etaLegendreArray= new double [SIZE1*SIZE2*SIZE3];

然后手工计算数组索引

etaLegendreArray[(x * SIZE2 + y) * SIZE3 + z] = data;

看起来所有额外的数学运算应该更慢,是吗?事实证明,每次您使用 [] 时,编译器都会向您隐藏看起来很像的数学。你几乎没有损失任何东西,而且肯定没有你失去一个不必要的东西那么多 cache miss .

但是到处重复这个数学是很疯狂的,迟早你会搞砸的,即使可读性的下降并没有让你先想死,所以你真的想把一维数组包装在一个帮助你处理数学的类(class)。一旦你这样做了,你还不如让那个类处理分配和释放,这样你就可以利用 all that RAII goodness .不再到处都是 newdeletefor 循环。全部包裹起来并系上蝴蝶结。

Here is an example of a 2D Matrix class easily extendable to 3D.它将以一种可预测且缓存友好的方式处理您可能需要的基本功能。

关于c++ - 优化四重嵌套 "for"循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42564432/

相关文章:

c++ - 为什么 C++ 类有两个名字?

计算 C 数组( vector )中数字的出现次数

c++ - 为什么使用较大数组的 SIMD 内在函数可以获得比标量更大的相对加速比?

performance - Pytorch 中的 Titan XP 与 Quadro P400 GPU

c - 我的 for 循环序列没有以周长结束结束

javascript - 为循环构建一个带有括号中数字的变量

c++ - 从指针转换为引用

c++ - scoped_ptr 所有权

c++ - 使用共享指针来自另一个线程的纯虚拟调用

performance - 很少的 CSS 属性及其解析性能