使用GCC / ICC在C ++中使用模板参数进行可移植循环展开

问题描述

我正在研究一个high-performance parallel computational fluid dynamics code,它涉及许多轻量级循环,因此,如果所有重要的循环都完全展开,则性能大约提高30%。

使用编译器指令,可以很容易地在固定数量的循环中完成此操作:#pragma GCC unroll (16)被我所针对的两个编译器Intel C ++编译器ICC和GCC识别,而#pragma unroll (16)被GCC不幸地忽略了。例如,我还可以将模板参数或预处理器指令用作限制,例如ICC(similar to what you can do with nvcc

template <int N>
// ...
#pragma unroll (N)
for (int i = 0; i < N; ++i) {
// ...
}

#define N 16

#pragma unroll (N)
for (int i = 0; i < N; ++i) {
// ...
}

使用ICC进行编译时,使用-Wall -w2 -w3不会引发任何错误或警告,而使用GCC(#pragma GCC unroll (N))的互补语法-Wall -pedantic会在Ubuntu 18.04的GCC 9.2.1 20191102中引发错误:

error: ‘#pragma GCC unroll’ requires an assignment-expression that evaluates to a non-negative integral constant less than 65535
#pragma GCC unroll (N)

有人知道一种方法,可以使基于模板参数的循环展开与编译器指令一起以可移植的方式工作(至少与GCC和ICC一起使用)吗?实际上,我只需要整个循环的完全展开,因此#pragma GCC unroll (all)之类的东西已经对我有很大帮助。

我知道unroll loops with template meta-programming存在或多或少的复杂策略,但是在我的应用程序中,循环可能是嵌套的,并且可能包含更复杂的循环体,我觉得这种策略会使我的代码过于复杂并降低可读性。

解决方法

遗憾的是,目前似乎没有一致的方法。

我最后使用了预处理器指令,该指令决定是否根据模板参数进行解压缩(如果可以使用Intel C ++编译器ICC的话),或者使用常数因子对其他所有编译器进行解压缩(例如GCC)。可以将此思想与user dfri的注释结合起来,并通过相应的预处理器指令禁用push / pop选项。这是一个小例子:

template <int N>
void exampleContainingLoop()
{
    #if defined(__ICC) || defined(__ICL)
    #pragma unroll (N)
    #else
    #pragma GCC unroll (4)
    #endif
    for (int i = 0; i < N; ++i)
    {
        // ...
    }
    
    return;
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...