Optimizing a 3D-Loop (C++)

Optimizing a 3D-Loop (C++)

本文关键字:C++ 3D-Loop Optimizing      更新时间:2023-10-16

我正在用c++开发一个多网格求解器,现在我正在努力提高串行性能。其中最耗时的部分是平滑,在我的例子中是一个连续的过度松弛解算器。它看起来像这样(我希望它是不言自明的):

int idx; 
int strideY = stride_[level][0];
int strideZ = stride_[level][1];
for(int i = 0; i < steps; ++i) {
    for(int z = 1; z <= innerGridpoints_[level][2]; ++z) {
        for(int y = 1; y <= innerGridpoints_[level][1]; ++y) {
            idx = getIndexInner(level, 1,y,z);
            for(int x = 1; x <= innerGridpoints_[level][0]; ++x, ++idx) {
                grid[idx] = (1. - omega)  * grid[idx] + omega * 1./6. * (grid[idx+1] + grid[idx-1] +
                                    grid[idx + strideY]  + grid[idx - strideY] + 
                                    grid[idx + strideZ]  + grid[idx - strideZ] - 
                                    spacing_[level] * spacing_[level] * rhs[idx]);
            }
        }
    }
}

我已经做了一些优化:循环的定位使得内部循环提供最多的局部条目(即相邻元素沿着x维),以及idx的预计算(尽管这是一个内联函数,但它这样节省了相当多的时间)。我也尝试过阻塞,即不是在整个网格上迭代,而是只在小块上迭代以增加局部性,但这没有任何影响。我的最后一个想法是尝试一些循环展开,但实际上我并不期望从中得到很大的改进。我在想,也许在内存访问方面有一些可能的改进。欢迎任何提示:)

仅供参考:网格大小从非常小到255x255x255不等。此外,网格在每个维度上都有一些边界,由少量的行组成,即迭代不是在整个网格上。

一个好的优化编译器无论如何都会为您做大多数简单的事情,所以总是衡量您所做的更改是否确实改善了事情。并且,检查(并学会理解)生成的汇编代码,看看编译器实际上在做什么。

但是有一些事情我会尝试,因为表达式很复杂,甚至好的优化器有时也需要一些帮助:-

首先,将内部循环中的不变子表达式提升到周围循环中。在您的示例中,明显的是spacing_[level] * spacing_[level]omega * 1./6.

另一种尝试是使idx成为指针而不是数组索引,并在循环中增加指针。

 int *idx = &grid[getIndexInner(level, 1,y,z)];  // assuming grid is array of ints.

你的表达式开始看起来像这样

*idx = (1. - omega)  * *idx + omega * 1./6. * (idx[1] + idx[-1] +
                                idx[strideY]  + idx[- strideY] + // etc...

你的优化器(假设它已经打开了??)可能已经在这样做了。但值得一试。正如我所说,没有测量,这是一个毫无意义的练习。

并且,正如@AkiSuihkonen在上面的评论中提到的"首先让它工作"。调试高度优化的代码要困难得多,所以在开始担心性能问题之前,请确保你的算法完全按照应该的方式执行。