Skip to content

PathMSDBase: drop four omp simd pragmas that cannot vectorize - #1437

Merged
carlocamilloni merged 1 commit into
plumed:masterfrom
Smith-Group:pathmsd-drop-dead-simd-pragmas
Jul 30, 2026
Merged

PathMSDBase: drop four omp simd pragmas that cannot vectorize#1437
carlocamilloni merged 1 commit into
plumed:masterfrom
Smith-Group:pathmsd-drop-dead-simd-pragmas

Conversation

@aalhossary

Copy link
Copy Markdown
Contributor
Description

Building PLUMED with clang and OpenMP emits this four times:

PathMSDBase.cpp:160:19: warning: loop not vectorized: the optimizer was unable to perform the
requested transformation; the transformation might be disabled or specified as part of an
unsupported transformation ordering [-Wpass-failed=transform-warning]

All four are attributed to calculate()'s opening line, which hides which loops are responsible.
Keeping one #pragma omp simd at a time and recompiling isolates them exactly:

pragma loop warnings
line 176 imgVec[i].property = indexvec[i]; imgVec[i].index = i; 1
line 234 tmp_derivs2[i*nat+j] = tmp_derivs[j]; 1
line 244 the same loop again 1
line 266 the same loop again 1
line 335 derivs_s[i] += tmp*it.distder[i]; 0 — vectorizes
line 340 derivs_z[i] += ...; 0 — vectorizes

Neither failing case can be vectorized, and requesting it is misleading:

  • Line 176 is not merely hard to vectorize, it is meaningless. ImagePath::property is a
    std::vector<double>, so imgVec[i].property = indexvec[i] is a vector copy-assignment that
    allocates. There is no SIMD work in that loop.
  • The other three are the same contiguous copy of Vector elements, written out by hand.
    std::copy_n states the intent and lets the compiler emit a memmove rather than an element loop
    the vectorizer then declines to transform.

Fixing the source rather than silencing the diagnostic means the warning disappears for every
compiler, and a build that adds -Werror no longer fails here. Behaviour is unchanged: std::copy_n
over [0, nat) is exactly the loop it replaces, and the two pragmas that do vectorize are untouched.

Net change: 7 insertions, 13 deletions in one file.

Target release

I would like my code to appear in release 2.11 (master).

Type of contribution
  • changes to code or doc authored by PLUMED developers
  • changes to a module not authored by you
  • new module contribution or edit of a module authored by you
Copyright
  • I agree to transfer the copyright of the code I have written to the PLUMED developers or to the
    author of the code I am modifying.
  • the code I am contributing is not mine and I am making it available under LGPL
Tests
  • I added a new regtest or modified an existing regtest to validate my changes.
  • I verified that all regtests are passed successfully on GitHub Actions.

No new regtest is added: this changes neither behaviour nor interface, so there is nothing new to
assert. The existing PathMSD regtests are the check, and they already encode the expected colvar and
derivative values. Locally, with the change applied, all seven that this build can run pass:

basic/rt35  rt37  rt39  rt39-mpi  rt57  rt58  rt59      7 passed, 0 failed
mapping/rt39, mapping/rt39-mpi                          skipped (mapping module off in that build)

Verified with clang 20.1.1:

clang++ -std=c++17 -O3 -fopenmp -march=skylake-avx512 -Wall -c colvar/PathMSDBase.cpp -I.

4 warnings before, 0 after. GCC is unaffected either way.

Building with clang and OpenMP emits, four times:

    PathMSDBase.cpp:160:19: warning: loop not vectorized: the optimizer was
    unable to perform the requested transformation ... [-Wpass-failed=transform-warning]

All four remarks are attributed to calculate()'s opening line, which hides which
loops are at fault. Isolating them by keeping one pragma at a time shows exactly
four contributors, and two pragmas that are fine:

    line 176  imgVec[i].property = ... ; imgVec[i].index = i;      1 warning
    line 234  tmp_derivs2[i*nat+j] = tmp_derivs[j];                1 warning
    line 244  (the same loop again)                                1 warning
    line 266  (the same loop again)                                1 warning
    line 335  derivs_s[i] += tmp*it.distder[i];                    0 - vectorizes
    line 340  derivs_z[i] += ...;                                  0 - vectorizes

Neither failing case can be vectorized, and asking for it is misleading:

- Line 176 is not merely hard to vectorize, it is meaningless. ImagePath::property
  is a std::vector<double>, so `imgVec[i].property = indexvec[i]` is a vector
  copy-assignment that ALLOCATES. There is no SIMD work in that loop.

- The other three are the same contiguous copy of Vector elements written out by
  hand. std::copy_n says what is meant, and lets the compiler emit a memmove
  instead of an element loop the vectorizer then declines to transform.

Fixing the source rather than silencing the diagnostic means the warning is gone
for every compiler, and a build using -Werror no longer fails here. Behaviour is
unchanged: std::copy_n over [0,nat) is exactly the loop it replaces, and the
remaining two pragmas, which do vectorize, are untouched.

Verified with clang 20.1.1:
  clang++ -std=c++17 -O3 -fopenmp -march=skylake-avx512 -Wall \
      -c colvar/PathMSDBase.cpp -I.
4 warnings before, 0 after. GCC is unaffected either way (its 17 warnings here
are pre-existing -Wunknown-pragmas from `#pragma acc` in headers, which PLUMED's
own build silences).
@carlocamilloni
carlocamilloni merged commit e44cef6 into plumed:master Jul 30, 2026
25 of 26 checks passed
carlocamilloni pushed a commit that referenced this pull request Jul 31, 2026
…#1438)

* RMSD: reformulate two loops so their omp simd pragmas can be honoured

A plain `./configure && make` fails on any machine whose default C++ compiler is
clang: configure enables OpenMP automatically, and src/tools/Makefile appends
-Werror, so the vectorizer's refusal is fatal.

    RMSD.cpp:1443:35: error: loop not vectorized: the optimizer was unable to
    perform the requested transformation; the transformation might be disabled or
    specified as part of an unsupported transformation ordering
    [-Werror,-Wpass-failed=transform-warning]
    RMSD.cpp:1487:35: error: (the same, in getDDistanceDReference)

Both are attributed to the opening line of the enclosing function, which hides
the loop at fault. Each of the two functions contains exactly one
`#pragma omp simd`, so the attribution is unambiguous: the loops at lines 1479
and 1530.

The pragma is kept. The loop body is rewritten so the compiler can act on it:
the loop-invariant `(ddist_dc*-csum)` is hoisted into a named Vector, and the
Vector expression is expanded into its three components. Both loops now
vectorize at width 4, confirmed with -Rpass=loop-vectorize, instead of being
refused.

Alternatives measured with clang 20.1.1, all on the same tree:

    braces around the if body                       still fails
    hoisting the invariant only                     still fails
    binding `Vector& dv=derivatives[iat]`           still fails
    flatten to `double*` + manual 3-way unroll      vectorizes, but the pointer
                                                    is UB when n==0
    per-component `derivatives[iat][k]`             vectorizes   <- chosen

The Vector arithmetic creates temporaries the vectorizer cannot see through, and
naming the element through a reference reintroduces them; only the direct
per-component form works.

No other pragma in the file is touched. The other eight active ones already
vectorize. Two more, on identically shaped std::vector<Vector> loops at lines
1339 and 1426, were commented out long ago because they gave wrong results with
the Intel compiler; these two had the same shape.

Behaviour is unchanged, and the 17 regtests that exercise these paths pass,
including rt-rmsd-displace, which is the align != displace case that actually
enters the modified branch.

This is the same class of problem as #1437. It escapes CI because no job builds
clang with OpenMP enabled: the Linux workflow uses gcc and icpx, and macsimple
uses AppleClang without libomp, where `#pragma omp simd` is inert.

* RMSD: also unswitch getDistance's reduction loop

The first commit fixed the two loops that fail at -O3. Building PLUMED with
mpicxx at -O2 shows a third one, which -O3 happens to vectorize:

    RMSD.cpp:1361:22: error: loop not vectorized: the optimizer was unable to
    perform the requested transformation [-Werror,-Wpass-failed=transform-warning]

The remark is attributed to getDistance()'s opening line; the pragma is the
`omp simd reduction(+:localDist)` at line 1374. Its body branches on alEqDis
and safe, both of which are loop-invariant members, so the loop carries a
per-iteration test the vectorizer will not always unswitch by itself.

Hoisting the two branches out of the loop makes it vectorize at -O2 as well.
This is a pure unswitching: both conditions are invariant, the summation order
is unchanged, and the arithmetic is untouched, so results are bit-identical.

Measured on this file with clang 20.1.1, counting -Wpass-failed diagnostics
(-march makes no difference at any level):

    -O level     before    after
    -O0            0         0
    -O1            5         4
    -Os            8         7
    -O2            3         0
    -O3            2         0

-O1 and -Os remain non-clean, but not for a reason any source change can fix:
clang's loop vectorizer is effectively disabled there, so every `omp simd` in
the file is refused, including the ones that are perfectly good at -O2. Making
PLUMED buildable at those levels would mean not combining -Werror with
-Wpass-failed in the module makefiles; that is a build-system decision and is
deliberately left out of this PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants