预编译的多维数组访问

Posted

技术标签:

【中文标题】预编译的多维数组访问【英文标题】:Precompiled Multi-Dimensional Array Access 【发布时间】:2019-06-01 07:55:04 【问题描述】:

想象一下像这样计算一个三维数组:

for (int i = 0; i < I; i++)

    for (int j = 0; j < J; j++)
    
        for (int k = 0; k < K; k++)
        
            array[k + j * K + i * K * J] = someValue(i, j, k);
        
    

但是k + j * K + i * K * J 部分有点贵。是否可以告诉编译器将循环转换成这样的东西?

array[0] = someValue(0, 0, 0);
array[1] = someValue(0, 0, 1);
array[2] = someValue(0, 0, 2);
array[3] = someValue(0, 1, 0);
...

这当然会使二进制文件变大,但也会提高性能,如果此代码执行很多。是否有可能做到这一点?还是我必须自己生成代码并将其粘贴到源文件中?

【问题讨论】:

您询问循环展开,一种优化,但没有指定编译器。没有适合所有编译器的答案。它将是特定于编译器的。无论如何,这是一个 GCC 特定的问答 ***.com/questions/4071690/… 您是否测量、基准测试和分析过这是您程序中的主要瓶颈?您是否启用了编译器优化? 我还没有写程序,目前正在计划中。我想为至少 GCC 和 MSVC 提供一个解决方案。 无需担心。编译器足够聪明,可以优化循环,并且经常用指针增量代替乘法。在你的情况下only a single multiplication is used 【参考方案1】:

我相信在您的特殊情况下,我们可以将循环重写为:

auto* scan = array;
for (int i = 0; i < I; i++)

    for (int j = 0; j < J; j++)
    
        for (int k = 0; k < K; k++)
        
            *scan++ = someValue(i, j, k);
        
    

这是一个微优化,你通常不必担心。原因如下。

原因 1:整数乘法非常便宜。计算k + j * K + i * K * J 比从计算机的 RAM 中检索值要便宜,而且与从 CPU 最快的缓存中检索值差不多(如果不是便宜的话)。

原因 2: 编译器非常聪明。他们可以识别哪些值发生变化,哪些值保持不变,并优化循环外的公共子表达式(这样他们就不会多次执行相同的计算)。

原因 3: 编译器能够利用矢量化指令。根据someValue 所做的事情,它可以利用这一点在同一个内核上并行计算多个值。这对于索引到array 的任何一种方法都是正确的。

C++ 代码并非严格要求。编译器可以并且确实进行了主要和复杂的优化以使代码更高效,并且像您示例中的代码这样的代码很容易对其进行优化。

【讨论】:

以上是关于预编译的多维数组访问的主要内容,如果未能解决你的问题,请参考以下文章

c中多维数组的编译器错误

Json C# 多维数组访问

连续数组多维表示的快速元素访问

动态访问多维数组值

使用 Vue JS 访问多维数组

使用 typedef、多维数组和指针时出现编译器错误