提问人:nh2 提问时间:10/30/2023 更新时间:10/30/2023 访问量:34
clang OpenMP 循环继续计数超出循环条件限制
clang OpenMP loop continues counting beyond loop condition limit
问:
我有以下有趣的问题:
#include <iostream>
#include <omp.h>
using namespace std;
int main(const int, const char **) {
const size_t n = 13; // triggers the issue
// const uint32_t n = 13; // fixes the issue
#pragma omp parallel for
for (uint32_t i = 0; i < n; i += 2) { // `+= 1` fixes the issue
cerr << "i = " << i << ", n = " << n << endl;
}
}
尽管我的 ,这在 上打印了无限系列的数字:i < n
n = 13
clang++ -fopenmp
i = 0, n = 13
i = 2, n = 13
i = 4, n = 13
i = 6, n = 13
i = 8, n = 13
i = 10, n = 13
i = 12, n = 13 <- this should be impossible
i = 14, n = 13
...
这怎么可能?
clang++ 11、14 和 16 会发生这种情况,但 GCC 不会。如果被删除,它也会被修复。-fopenmp
如果我这样做而不是,问题就会消失。+= 1
+= 2
根据 OPenMP 循环增量,“大于 1 的循环增量”是合法的。
如果我将类型从 更改为 ,问题也会消失。n
size_t
uint32_t
关于警告的观察
使用 和 打印以下警告:-Wall
+= 2
clang++
clang-openmp-for-loop-test.cpp:49:3: warning: implicit conversion from 'unsigned long' to 'unsigned int' changes value from 9223372034707292167 to 2147483655 [-Wconstant-conversion]
for (uint32_t i = 0; i < n; i += 2) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
clang-openmp-for-loop-test.cpp:49:3: warning: implicit conversion from 'unsigned long' to 'unsigned int' changes value from 9223372034707292167 to 2147483655 [-Wconstant-conversion]
for (uint32_t i = 0; i < n; i += 2) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
clang-openmp-for-loop-test.cpp:49:3: warning: implicit conversion from 'unsigned long' to 'unsigned int' changes value from 9223372034707292167 to 2147483655 [-Wconstant-conversion]
for (uint32_t i = 0; i < n; i += 2) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.
关于这些警告,我不明白很多事情:
- 为什么相同的警告(在同一代码位置)发出 3 次?
- 价值从何而来?这是一个警告,当然它应该警告我实际使用的一些常量。该值为 .
9223372034707292167
-Wconstant-conversion
2147483655
maximum 32-bit-signed integer + 8
- 当我更改为 ?这两个文字的类型肯定是一样的吗?
+= 2
+= 1
环境
Ubuntu 22.04 中。
sudo apt install clang14 libomp-14-dev gcc+12 libstdc++-12-dev
(只是为了工作而需要的,因为 clang 使用 GCC 的 stdlib 标头。gcc+12 libstdc++-12-dev
#include <iostream>
编译:
clang++ myprogram.cpp -o myprogram -fopenmp -O2 -Wall -Wextra
跑:
OMP_NUM_THREADS=1 ./myprogram 2>&1 | head -n20
编译器错误,还是我遗漏了什么?
答:
将测试更改为显式强制转换可修复 clang (17) 的问题。i < (uint32_t)n
但是显式强制转换会导致编译错误:(size_t)i < n
condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'
.
我不太清楚,但我对 OpenMP 5.2 规范(最新的规范,但我不知道使用哪个 clang)的理解是 C/C++ OpenMP 循环的测试表达式应单独涉及 lhs 或 rhs 上的变量(规范的第 88 页):i
因为 和 是不同的类型,编译器必须隐式强制转换其中之一,并且因为范围更广,所以这就是强制转换。那么测试不再单独涉及,而是 的函数。i
n
n
i
i
i
因此,最初的问题可能是这是一个非法的 OpenMP 代码,即使 gcc 将其作为 OpenMP 规范的扩展进行处理。不过,这也是一个 clang 编译器错误:clang 最初进入隐式强制转换路由,然后搞砸了循环(它应该因为非法代码而产生编译错误,或者像 gcc 一样作为扩展正确处理它)。
评论
size_t
uint32_t
+= 2
2lu
2llu
2u