Hello,
I encountered a suspected optimisation-dependent issue with an OpenMP sum reduction on a volatile long. With NVIDIA nvc 26.5-0, the volatile counter remains zero at -O2 and -O3, while an otherwise identical non-volatile counter produces the expected result.
Both counters work at -O0. GCC 12.2.0 produces the expected results at all three optimisation levels.
Minimal reproducer:
Save as volatile_reduction.c:
int main(void)
{
volatile long v = 0;
long p = 0;
#pragma omp parallel for reduction(+:v)
for (long i = 0; i < 1000; i++)
v++;
#pragma omp parallel for reduction(+:p)
for (long i = 0; i < 1000; i++)
p++;
printf("volatile: %ld plain: %ld (both should be 1000)\n",
v, p);
return (v != 1000 || p != 1000);
}
Compile and Run:
nvc -O3 -mp volatile_reduction.c -o reduction_nvc
OMP_DYNAMIC=FALSE OMP_NUM_THREADS=1 ./reduction_nvc
OMP_DYNAMIC=FALSE OMP_NUM_THREADS=4 ./reduction_nvc
Actual output for both thread counts:
volatile: 0 plain: 1000 (both should be 1000)
Both runs return exit status 1.
For Comparison:
gcc -O3 -fopenmp volatile_reduction.c -o reduction_gcc
OMP_DYNAMIC=FALSE OMP_NUM_THREADS=1 ./reduction_gcc
OMP_DYNAMIC=FALSE OMP_NUM_THREADS=4 ./reduction_gcc
Both GCC runs print the expected output and return exit status 0:
volatile: 1000 plain: 1000 (both should be 1000)
Optimisation-level comparison:
I compiled the same source separately at each optimisation level. Each row below was tested with both one and four threads, with identical results for both thread counts.
| Compiler | Optimisation | Volatile counter | Plain counter |
|---|---|---|---|
| nvc 26.5-0 | -O0 |
1000 | 1000 |
| nvc 26.5-0 | -O2 |
0 | 1000 |
| nvc 26.5-0 | -O3 |
0 | 1000 |
| GCC 12.2.0 | -O0 |
1000 | 1000 |
| GCC 12.2.0 | -O2 |
1000 | 1000 |
| GCC 12.2.0 | -O3 |
1000 | 1000 |
Environment:
- System: NCI Gadi
- OS: Rocky Linux 8.10 (Green Obsidian)
- Architecture: x86_64
- CPU: Intel Xeon Platinum 8268 @ 2.90 GHz
- NVIDIA module:
nvidia-hpc-sdk/26.5 - GCC module:
gcc/12.2.0 - CPU OpenMP execution, without GPU offloading
OMP_DYNAMIC=FALSE
The original teaching example uses volatile to make unsynchronised accesses observable before introducing a reduction. The reproducer above includes the reduction, so I would expect both counters to produce 1000.
Is this a known optimisation issue involving volatile reduction variables? Please let me know if additional compiler diagnostics would help.
I also reproduced this with 25.1,5,7,9
Thank you.