Another bug report on the nvfortran compiler + openmp in GPUs. Take a look, thanks!
nvfortran_private_callee_bug.tar.gz (3.9 KB)
nvfortran 26.9: wrong results on GPU for a private() scalar passed as an INTENT(OUT)
actual argument to a “declare target” subroutine inside “!$omp target teams loop”
(plus an observation: “target teams loop” runs one thread per team when the body
calls a declare-target routine)
Summary
Reproducer 1 (priv_callee.F90). A scalar h is listed in private(h) on
“!$omp target teams loop collapse(3)” and is passed as the INTENT(OUT) actual
argument of a “declare target” subroutine (call callee(a(i,j,k), h)). The value
read back from h afterwards is garbage. With a(:) random in [0,1) the expected
result is b = (2a+1)*a; at -O2 all 4096 of 4096 elements are wrong.
Correct in each of these variants of the same loop:
- no private clause (h then shared/implicit),
- a declare-target FUNCTION returning the value instead of an INTENT(OUT) argument,
- “target teams distribute parallel do” with private(h),
- -O2 with -Minline (callee inlined) in this small case.
Wrong at -O1 and -O2 without -Minline. At -O0 the file does not compile (see
“Other observation” below), so -O0 could not be used as a reference here.
In the full application kernel the result is wrong with -Minline as well.
Reproducer 2 (slow_teams_loop.F90). Same loop body, 192^3 cells, 20 timed
repetitions. -Minfo=mp shows “Loop parallelized across teams collapse(3) !
blockidx%x” with NO threads(128) for the teams-loop kernel that calls a
declare-target routine, i.e. one thread per block. Best-of-20 kernel time
6.7 ms versus 0.5-0.6 ms for “target teams distribute parallel do” (about 12-13x).
A teams-loop kernel with a private local array w(3) and no call was NOT slow in
this small test (0.5 ms, threads(128) generated), so the local-array case seen in
the full application is not reproduced here; only the callee case is.
Environment
nvfortran 26.9-0 64-bit, target x86-64 Linux (-tp alderlake), bundled CUDA 13.3
NVIDIA GeForce RTX 4070 Laptop GPU, compute capability 8.9, driver 580.173.02
Pop!_OS 24.04 LTS (Ubuntu 24.04 base)
Files
priv_callee.F90 reproducer 1: module with callee/fcallee and 4 loop variants, test program
slow_teams_loop.F90 reproducer 2: timing of teams loop vs teams distribute parallel do
build_run.sh builds and runs both (edit NV= if the SDK is installed elsewhere;
CC_=89 selects the GPU architecture)
output.txt complete compiler -Minfo output and program output from build_run.sh
To reproduce
bash build_run.sh
or by hand:
nvfortran -O2 -mp=gpu -gpu=cc89,nofma priv_callee.F90 -o priv_callee && ./priv_callee
nvfortran -O2 -mp=gpu -gpu=cc89,nofma slow_teams_loop.F90 -o slow_teams_loop && ./slow_teams_loop
Expected (reproducer 1): every variant prints “0 of 4096 wrong OK”.
Actual at -O2 (from output.txt):
variant 1: teams loop + private(h) + intent(out) actual (subroutine) : 4096 of 4096 wrong WRONG
variant 2: teams loop, no private clause (subroutine) : 0 of 4096 wrong OK
variant 3: teams loop + private(h), function result : 0 of 4096 wrong OK
variant 4: teams distribute parallel do + private(h) (subroutine) : 0 of 4096 wrong OK
(same at -O1; with -O2 -Minline variant 1 prints “0 of 4096 wrong OK”)
Expected (reproducer 2): teams loop and teams distribute parallel do take about the same time.
Actual at -O2 (from output.txt):
teams loop + callee : 6.658 ms (best of 20)
teams distribute parallel do + callee : 0.562 ms (best of 20)
teams loop + local array: 0.564 ms (best of 20)
teams distribute parallel do + local array: 0.568 ms (best of 20)
slowdown teams loop / dpf: callee 11.8x local array 1.0
(timings vary a little run to run; an earlier run gave 6.657 / 0.495 ms, 13.4x)
Other observation
Compiling priv_callee.F90 with -O0 -mp=gpu fails in the device compiler:
nvvmCompileProgram: failed with error 9: NVVM_ERROR_COMPILATION
nvnvvmd: error: … parse invalid cast opcode for cast from ‘double’ to ‘i8*’
NVFORTRAN-W-0155-Compiler failed to translate accelerator region …
(build_run.sh does not run -O0 for this reason.) Not investigated further.
Workaround
Use “target teams distribute parallel do” with private(h), or avoid the INTENT(OUT)
argument by using a declare-target function result, or drop the private clause
under “teams loop” (only safe where no thread-sharing of the scalar can occur).