Documentation 6b54b78464
Loading...
Searching...
No Matches
omp_structure.hpp
Go to the documentation of this file.
1/*!
2 * \file omp_structure.hpp
3 * \brief OpenMP interface header, provides compatibility functions
4 * if the code is built without OpenMP support.
5 * Parallel pragmas are defined here so that they can be
6 * completely "disabled" when compiling without OpenMP.
7 * \note Do not include omp.h explicitly anywhere, use this header instead.
8 * \note If you use an omp_*** function define a compatibility version here,
9 * if that is not practical use define "HAVE_OMP" to guard that function.
10 * \note Always use the macro "SU2_OMP" to create OpenMP constructs, this is so
11 * we can disable pragmas. Other convenient pragmas are also defined here
12 * e.g. SU2_OMP_PARALLEL. Exotic pragmas of limited portability should be
13 * defined here with suitable fallback versions to limit the spread of
14 * compiler tricks in other areas of the code.
15 * \author P. Gomes, J. Blühdorn
16 * \version 8.5.0 "Harrier"
17 *
18 * SU2 Project Website: https://su2code.github.io
19 *
20 * The SU2 Project is maintained by the SU2 Foundation
21 * (http://su2foundation.org)
22 *
23 * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md)
24 *
25 * SU2 is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU Lesser General Public
27 * License as published by the Free Software Foundation; either
28 * version 2.1 of the License, or (at your option) any later version.
29 *
30 * SU2 is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33 * Lesser General Public License for more details.
34 *
35 * You should have received a copy of the GNU Lesser General Public
36 * License along with SU2. If not, see <http://www.gnu.org/licenses/>.
37 */
38
39#pragma once
40
41#include <cstddef>
42#include <algorithm>
43
44#include "../code_config.hpp"
45
46#if defined(HAVE_OMP)
47#include <omp.h>
48
49#if defined(HAVE_OPDI)
50#if defined(HAVE_OMPT)
51#include "opdi/backend/ompt/omptBackend.hpp"
52#else
53#include "opdi/backend/macro/macroBackend.hpp"
54#endif
55#include "opdi.hpp"
56#endif
57
58/*--- The generic start of OpenMP constructs. ---*/
59#define SU2_OMP(ARGS) PRAGMIZE(omp ARGS)
60
61#else // Compile without OpenMP
62#include <ctime>
63
64/*--- Disable pragmas to quiet compilation warnings. ---*/
65#define SU2_OMP(ARGS)
66
67/*!
68 * \brief Maximum number of threads available.
69 */
70inline constexpr int omp_get_max_threads() { return 1; }
71
72/*!
73 * \brief Number of threads in current team.
74 */
75inline constexpr int omp_get_num_threads() { return 1; }
76
77/*!
78 * \brief Set the maximum number of threads.
79 */
80inline void omp_set_num_threads(int) {}
81
82/*!
83 * \brief Index of current thread, akin to MPI rank.
84 */
85inline constexpr int omp_get_thread_num() { return 0; }
86
87/*!
88 * \brief Returns true if inside a parallel section.
89 */
90inline constexpr bool omp_in_parallel() { return false; }
91
92/*!
93 * \brief Return the wall time.
94 */
95inline passivedouble omp_get_wtime() { return passivedouble(clock()) / CLOCKS_PER_SEC; }
96
97/*!
98 * \brief Dummy lock type and associated functions.
99 */
100struct omp_lock_t {};
103 inline omp_lock_t& operator[](int) { return l; }
104};
105inline void omp_init_lock(omp_lock_t*) {}
106inline void omp_set_lock(omp_lock_t*) {}
109
110#endif // end OpenMP detection
111
112/*--- Initialization and finalization ---*/
113
114void omp_initialize();
115void omp_finalize();
116
117/*--- Detect SIMD support (version 4+, after Jul 2013). ---*/
118#ifdef _OPENMP
119#if _OPENMP >= 201307
120#define HAVE_OMP_SIMD
121#define SU2_OMP_SIMD PRAGMIZE(omp simd)
122#endif
123#endif
124#ifndef SU2_OMP_SIMD
125#define SU2_OMP_SIMD
126#endif
127
128#if !defined(CODI_FORWARD_TYPE) && !defined(CODI_REVERSE_TYPE)
129#define SU2_OMP_SIMD_IF_NOT_AD SU2_OMP_SIMD
130#else
131#define SU2_OMP_SIMD_IF_NOT_AD
132#endif
133
134/*--- Convenience macros (do not use excessive nesting). ---*/
135
136#define SU2_OMP_ATOMIC SU2_OMP(atomic)
137#define SU2_OMP_ATOMIC_WRITE SU2_OMP(atomic write)
138
139#ifndef HAVE_OPDI
140
141#define SU2_OMP_MASTER SU2_OMP(master)
142#define SU2_OMP_BARRIER SU2_OMP(barrier)
143#define SU2_OMP_CRITICAL SU2_OMP(critical)
144
145#define SU2_OMP_PARALLEL SU2_OMP(parallel)
146#define SU2_OMP_PARALLEL_(ARGS) SU2_OMP(parallel ARGS)
147#define SU2_OMP_PARALLEL_ON(NTHREADS) SU2_OMP(parallel num_threads(NTHREADS))
148
149#define SU2_OMP_FOR_(ARGS) SU2_OMP(for ARGS)
150#define SU2_OMP_FOR_DYN(CHUNK) SU2_OMP(for schedule(dynamic,CHUNK))
151#define SU2_OMP_FOR_STAT(CHUNK) SU2_OMP(for schedule(static,CHUNK))
152
153#define SU2_NOWAIT nowait
154
155#define END_SU2_OMP_MASTER
156#define END_SU2_OMP_CRITICAL
157#define END_SU2_OMP_PARALLEL
158#define END_SU2_OMP_FOR
159
160#else
161
162#define SU2_OMP_MASTER OPDI_MASTER()
163#define SU2_OMP_BARRIER OPDI_BARRIER()
164#define SU2_OMP_CRITICAL OPDI_CRITICAL()
165
166#define SU2_OMP_PARALLEL OPDI_PARALLEL()
167#define SU2_OMP_PARALLEL_(ARGS) OPDI_PARALLEL(ARGS)
168#define SU2_OMP_PARALLEL_ON(NTHREADS) OPDI_PARALLEL(num_threads(NTHREADS))
169
170#define SU2_OMP_FOR_(ARGS) OPDI_FOR(ARGS)
171#define SU2_OMP_FOR_DYN(CHUNK) OPDI_FOR(schedule(dynamic, CHUNK))
172#define SU2_OMP_FOR_STAT(CHUNK) OPDI_FOR(schedule(static, CHUNK))
173
174#define SU2_NOWAIT OPDI_NOWAIT
175
176#define END_SU2_OMP_MASTER OPDI_END_MASTER
177#define END_SU2_OMP_CRITICAL OPDI_END_CRITICAL
178#define END_SU2_OMP_PARALLEL OPDI_END_PARALLEL
179#define END_SU2_OMP_FOR OPDI_END_FOR
180
181#endif
182
183/* The SU2_OMP_SAFE_GLOBAL_ACCESS constructs are used to safeguard code that should only be executed by the master
184 * thread, with all threads and memory views synchronized both beforehand and afterwards.
185 */
186
187#define BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS \
188 SU2_OMP_BARRIER; \
189 if (omp_in_parallel()) AD::StartNoSharedReading(); \
190 SU2_OMP_MASTER
191
192#define END_SU2_OMP_SAFE_GLOBAL_ACCESS \
193 END_SU2_OMP_MASTER \
194 if (omp_in_parallel()) AD::EndNoSharedReading(); \
195 SU2_OMP_BARRIER;
196
197#define SU2_OMP_SAFE_GLOBAL_ACCESS(...) BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS{__VA_ARGS__} END_SU2_OMP_SAFE_GLOBAL_ACCESS
198
199/*--- Convenience functions (e.g. to compute chunk sizes). ---*/
200
201/*!
202 * \brief Integer division rounding up.
203 */
204inline constexpr size_t roundUpDiv(size_t numerator, size_t denominator) {
205 return (numerator + denominator - 1) / denominator;
206}
207
208/*!
209 * \brief Round up to next multiple.
210 */
211inline constexpr size_t nextMultiple(size_t argument, size_t multiple) {
212 return roundUpDiv(argument, multiple) * multiple;
213}
214
215/*!
216 * \brief Compute a chunk size based on totalWork and number of threads such that
217 * all threads get the same number of chunks (with limited size).
218 * \param[in] totalWork - e.g. total number of loop iterations.
219 * \param[in] numThreads - Number of threads that will share the work.
220 * \param[in] maxChunkSize - Upper bound for chunk size.
221 * \return The chunkSize.
222 */
223inline size_t computeStaticChunkSize(size_t totalWork, size_t numThreads, size_t maxChunkSize) {
224 if (!totalWork) return maxChunkSize;
225 size_t workPerThread = roundUpDiv(totalWork, numThreads);
226 size_t chunksPerThread = roundUpDiv(workPerThread, maxChunkSize);
227 return roundUpDiv(workPerThread, chunksPerThread);
228}
229
230/*!
231 * \brief Copy data from one array-like object to another in parallel.
232 * \param[in] size - Number of elements.
233 * \param[in] src - Source array.
234 * \param[in] dst - Destination array.
235 */
236template <class T, class U>
237void parallelCopy(size_t size, const T* src, U* dst) {
238 SU2_OMP_FOR_STAT(2048)
239 for (size_t i = 0; i < size; ++i) dst[i] = src[i];
241}
242
243/*!
244 * \brief Set the entries of an array-like object to a constant value in parallel.
245 * \param[in] size - Number of elements.
246 * \param[in] val - Value to set.
247 * \param[in] dst - Destination array.
248 */
249template <class T, class U>
250void parallelSet(size_t size, T val, U* dst) {
251 SU2_OMP_FOR_STAT(2048)
252 for (size_t i = 0; i < size; ++i) dst[i] = val;
254}
255
256/*!
257 * \brief Atomically update a (shared) lhs value with a (local) rhs value.
258 * \note For types without atomic support (non-arithmetic) this is done via critical.
259 * \param[in] rhs - Local variable being added to the shared one.
260 * \param[in,out] lhs - Shared variable being updated.
261 */
262template <class T, su2enable_if<!std::is_arithmetic<T>::value> = 0>
263inline void atomicAdd(T rhs, T& lhs) {
265 lhs += rhs;
267}
268template <class T, su2enable_if<std::is_arithmetic<T>::value> = 0>
269inline void atomicAdd(T rhs, T& lhs) {
271 lhs += rhs;
272}
273
274/*--- GCC supported atomic compare (for min/max) before it was fully 5.1 compliant. ---*/
275#define ATOMIC_COMPARE_SINCE 202011
276#ifdef __GNUC__
277#if __GNUC__ > 11
278#undef ATOMIC_COMPARE_SINCE
279#define ATOMIC_COMPARE_SINCE 201511
280#endif
281#endif
282
283/*--- By default the min/max fallback to critical is required for all types. ---*/
284#define ATOMIC_COMPARE_FALLBACK
285
286/*--- Atomic max, shared = max(shared, local). ---*/
287/*--- nvcc's host pass drops the clause from "#pragma omp atomic compare", which the host
288 * compiler then rejects. The .cu sources do not use these functions, so they simply get
289 * the critical section fallback below. ---*/
290#if defined(_OPENMP) && !defined(__CUDACC__)
291#if _OPENMP >= ATOMIC_COMPARE_SINCE
292/*--- Atomic min/max are supported for arithmetic types. ---*/
293template <class T, su2enable_if<std::is_arithmetic<T>::value> = 0>
294inline void atomicMax(const T& local, T& shared) {
295#pragma omp atomic compare
296 shared = shared < local ? local : shared;
297}
298
299/*--- Redefine the fallback for non arithmetic types. ---*/
300#undef ATOMIC_COMPARE_FALLBACK
301#define ATOMIC_COMPARE_FALLBACK , su2enable_if<!std::is_arithmetic<T>::value> = 0
302#endif
303#endif
304template <class T ATOMIC_COMPARE_FALLBACK>
305inline void atomicMax(const T& local, T& shared) {
307 shared = std::max(local, shared);
309}
310
311/*--- Atomic min, shared = min(shared, local). ---*/
312#if defined(_OPENMP) && !defined(__CUDACC__)
313#if _OPENMP >= ATOMIC_COMPARE_SINCE
314template <class T, su2enable_if<std::is_arithmetic<T>::value> = 0>
315inline void atomicMin(const T& local, T& shared) {
316#pragma omp atomic compare
317 shared = shared > local ? local : shared;
318}
319#endif
320#endif
321template <class T ATOMIC_COMPARE_FALLBACK>
322inline void atomicMin(const T& local, T& shared) {
324 shared = std::min(local, shared);
326}
327#undef ATOMIC_COMPARE_FALLBACK
double passivedouble
Definition code_config.hpp:129
constexpr int omp_get_num_threads()
Number of threads in current team.
Definition omp_structure.hpp:75
#define SU2_OMP_ATOMIC
Definition omp_structure.hpp:136
#define END_SU2_OMP_FOR
Definition omp_structure.hpp:158
constexpr size_t roundUpDiv(size_t numerator, size_t denominator)
Integer division rounding up.
Definition omp_structure.hpp:204
void omp_set_num_threads(int)
Set the maximum number of threads.
Definition omp_structure.hpp:80
constexpr int omp_get_max_threads()
Maximum number of threads available.
Definition omp_structure.hpp:70
void omp_init_lock(omp_lock_t *)
Definition omp_structure.hpp:105
void parallelCopy(size_t size, const T *src, U *dst)
Copy data from one array-like object to another in parallel.
Definition omp_structure.hpp:237
passivedouble omp_get_wtime()
Return the wall time.
Definition omp_structure.hpp:95
void atomicMin(const T &local, T &shared)
Definition omp_structure.hpp:322
size_t computeStaticChunkSize(size_t totalWork, size_t numThreads, size_t maxChunkSize)
Compute a chunk size based on totalWork and number of threads such that all threads get the same numb...
Definition omp_structure.hpp:223
constexpr bool omp_in_parallel()
Returns true if inside a parallel section.
Definition omp_structure.hpp:90
void omp_finalize()
Definition omp_structure.cpp:44
#define SU2_OMP_CRITICAL
Definition omp_structure.hpp:143
void atomicAdd(T rhs, T &lhs)
Atomically update a (shared) lhs value with a (local) rhs value.
Definition omp_structure.hpp:263
void omp_initialize()
Definition omp_structure.cpp:31
void parallelSet(size_t size, T val, U *dst)
Set the entries of an array-like object to a constant value in parallel.
Definition omp_structure.hpp:250
constexpr int omp_get_thread_num()
Index of current thread, akin to MPI rank.
Definition omp_structure.hpp:85
#define END_SU2_OMP_CRITICAL
Definition omp_structure.hpp:156
void omp_unset_lock(omp_lock_t *)
Definition omp_structure.hpp:107
void omp_set_lock(omp_lock_t *)
Definition omp_structure.hpp:106
void atomicMax(const T &local, T &shared)
Definition omp_structure.hpp:305
#define SU2_OMP_FOR_STAT(CHUNK)
Definition omp_structure.hpp:151
constexpr size_t nextMultiple(size_t argument, size_t multiple)
Round up to next multiple.
Definition omp_structure.hpp:211
void omp_destroy_lock(omp_lock_t *)
Definition omp_structure.hpp:108
Definition omp_structure.hpp:101
omp_lock_t & operator[](int)
Definition omp_structure.hpp:103
omp_lock_t l
Definition omp_structure.hpp:102
Dummy lock type and associated functions.
Definition omp_structure.hpp:100