使用 GCC/ICC 在 C++ 中使用模板参数展开便携式循环

Portable loop unrolling with template parameter in C++ with GCC/ICC

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

通过使用编译器指令#pragma GCC unroll (16) is recognized by both compilers I am aiming for, the Intel C++ compiler ICC and GCC, while #pragma unroll (16) is sadly ignored by GCC. I can also use template parameters or pre-preprocessor directives as as limits with 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 (-Wall -pedantic) 的补充语法 #pragma GCC unroll (N) 会在 [= 中的 GCC 9.2.1 20191102 中抛出错误41=] 18.04:

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 存在或多或少复杂的策略,但在我的应用程序中,循环可能是嵌套的并且可以包含更复杂的循环体,我觉得这样的策略会过于复杂我的代码并降低可读性。

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

我最终使用了 pre-processor 宏 结合 and a stringification macro (similar to this) 决定是否根据模板参数展开,如果英特尔 C++ 编译器ICC 或 Clang 可用,使用常数因子展开 GCC,而忽略任何其他编译器。这里有一个小例子:

/// Helper macros for stringification
#define TO_STRING_HELPER(X)   #X
#define TO_STRING(X)          TO_STRING_HELPER(X)

// Define loop unrolling depending on the compiler
#if defined(__ICC) || defined(__ICL)
  #define UNROLL_LOOP(n)      _Pragma(TO_STRING(unroll (n)))
#elif defined(__clang__)
  #define UNROLL_LOOP(n)      _Pragma(TO_STRING(unroll (n)))
#elif defined(__GNUC__) && !defined(__clang__)
  #define UNROLL_LOOP(n)      _Pragma(TO_STRING(GCC unroll (16)))
#elif defined(_MSC_BUILD)
  #pragma message ("Microsoft Visual C++ (MSVC) detected: Loop unrolling not supported!")
  #define UNROLL_LOOP(n)
#else
  #warning "Unknown compiler: Loop unrolling not supported!"
  #define UNROLL_LOOP(n)
#endif

/// Example usage
template <int N>
void exampleContainingLoop() {
  UNROLL_LOOP(N)
  for (int i = 0; i < N; ++i) {
    // ...
  }
    
  return;
}