如何最好地通知编译器处理C中大型数组上具有复杂索引的嵌套循环,以获得最佳性能?

问题描述

我正在研究我们可能考虑的3 + 1维空间中的科学优化问题,其中多余维数由程序中用系数或角度分段表示的张量以及仰角和方位角坐标表示。在典型情况下,当同时涉及和系数表示时,我可能需要有效地处理沿75 x 75 x 75 x 16线的多个(双精度)数组,以及可能像75 x 75 x 75 x 16 x 16一样大的中间数组。我必须以合理的效率进行大部分实数和复杂的算术和三角运算,以完成诸如上采样,从二维投影到二维以及数值微分的工作。将来我也许可以做一个更方便的算法,但是目前,这就是我的方法。

要做这些事情,我写了一个大约1500行的C实现,在适用的情况下我使用MKL函数的组合,否则使用嵌套循环。这里的问题是,循环必须处理大型数组,这些数组的大小在编译时在带有复杂索引的嵌套循环中是未知的(现在是未知的)。因此,当我尝试使用性能报告分析性能时,得到的结果似乎是某些操作的瓶颈(实际上是一致的)(例如,以某种稍微复杂的方式对上述两个维度的向量进行相乘和求和)扩展到最里面的维度),但没有类似瓶颈。这似乎与各种与存储相关的错误有关,这些错误提示我编译后的代码并未真正针对如此大的数组进行优化。作为参考,我目前在AMD Ryzen 3900上进行计算,每个线程有一个物理内核;该算法基本上可以在“最高”级别进行并行化(所有重要的算法都由每个内核单独完成)。

从本质上讲,问题是,我的绩效改进策略应该是什么?我有两个基本想法:一个是将维度变量替换为我通过计划的主脚本在运行时设置和编译的宏。就我而言,这在实现方面不是问题。这样,至少编译器将获得有关大小的数字信息,尽管我不知道编译器将使用多少这种类型的信息。感谢您在这里的任何输入。

我可以想到的第二种方法基本上是,如果我有任何其他方法告诉编译器,“嘿,这是一个很大的数字,所以这个数组将很大,请相应地处理”,使它可以采用这考虑在内?我目前正在使用GCC,但可以使用其他编译器。

或者也欢迎采用其他针对大型阵列的一般缓解策略。

插入通用参数的代码片段示例如下

for(int j = 0;j<ALL_3D_SPACE;j++)
{
    for (int k = 0; k< DIM_4_1; k++)
    {
        for (int l = 0; l< DIM_4_2; l++)
        {
            REDUCED_SUM[j*DIM_4_1 +k] += EXPANDED_SUM[j*DIM_4_2*DIM_4_1+k*DIM_4_2+l]*COEFFS[j*DIM_4_2+l];
        }   
    }
}   

各种数据结构以标准指针-malloc方式声明,例如按照上面的模拟示例:

double * EXPANDED_SUM,*COEFFS,REDUCED_SUM;
EXPANDED_SUM = (double*) malloc(sizeof(double)*ALL_3D_SPACE*DIM_4_2*DIM_4_1);
COEFFS = (double*) malloc(sizeof(double)*ALL_3D_SPACE*DIM_4_2);
REDUCED_SUM = (double*) malloc(sizeof(double)*ALL_3D_SPACE*DIM_4_1);

我尽可能地重用结构,并在不再需要它们时释放它们。

预期用例中,用户输入了约100个2D投影集,大小约为nX*nY*DIM_4_1,尺寸和其他参数,其中ALL_3D_SPACE = nX*nY*nZ和DIM_4_1是阶数为10-20,nX,nY和nZ均为50-100。预期的输出将是大小为ALL_3D_SPACE*(DIM_4_2+2)的(3 + 1)D重构,其中DIM_4_2的阶数为5-10。当前,输入和输出都作为文本文件处理。请注意,与输入的任何比较都只是代码的一小部分,其中绝大部分是上述几行的算术/数字微分。处理所有处理的实际核心误差函数将预测和输出的当前估计作为输入,并在需要时输出误差,以及用于更好地估计输出的梯度,以供优化例程使用。

解决方法

如果使用大数组,建议将其存储到堆中或作为全局变量存储。那种尺寸的烟囱会爆炸

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...