Skip to content

declare(parallel()) can generate incorrect results for scalar accumulation loops #98

Description

@t-kalinowski

Summary

declare(parallel()) can emit an OpenMP loop with a shared scalar accumulator update (total <- total + x[i]), which is a data race and produces incorrect/nondeterministic results.

Original report and discussion: #88 (comment)

Verified on

  • Branch: main
  • Local reproduction date: 2026-02-17

Minimal reproducible example

library(devtools)
load_all(quiet = TRUE)

loop_sum_unsafe <- function(x) {
  declare(type(x = double(n)))
  total <- 0.0
  declare(parallel())
  for (i in seq_along(x)) {
    total <- total + x[i]
  }
  total
}

Sys.setenv(OMP_NUM_THREADS = "4", OMP_THREAD_LIMIT = "4", OMP_DYNAMIC = "false")
q_unsafe <- quick(loop_sum_unsafe)

x <- rep(1, 100000L)
vapply(1:6, function(i) q_unsafe(x), numeric(1))

Expected

Each run should return 100000.

Observed

Repeated calls return incorrect and varying results (example from local run):
30961, 31102, 25000, 31544, 30804, 31507

Generated code shape

r2f(loop_sum_unsafe) emits:

!$omp parallel do
do i = 1, size(x)
  total = (total + x(i))
end do
!$omp end parallel do

This loop updates shared total without a reduction/private strategy.

Notes

declare(parallel(private = total)) is not currently accepted on main (parallel() does not accept arguments yet.), so users cannot express a reduction/private fix in the current interface.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions