Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
openmp.h
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#ifndef DT_SYSTEM_OPENMP_H
19#define DT_SYSTEM_OPENMP_H
20
21/* OpenMP wrappers: the pragma shorthands used across the pixel code, with
22 * single-threaded fallbacks when OpenMP is disabled. Self-contained on purpose:
23 * low-level compute units include this instead of darktable.h. */
24
25#ifdef _OPENMP
26# include <omp.h>
27
28#ifndef dt_omp_nontemporal
29// Clang 10+ supports the nontemporal() OpenMP directive
30// GCC 9 recognizes it as valid, but does not do anything with it
31// GCC 10+ ???
32#if (__clang__+0 >= 10 || __GNUC__ >= 9)
33# define dt_omp_nontemporal(...) nontemporal(__VA_ARGS__)
34#else
35// GCC7/8 only support OpenMP 4.5, which does not have the nontemporal() directive.
36# define dt_omp_nontemporal(var, ...)
37#endif
38#endif /* dt_omp_nontemporal */
39
41#define OMP_PRAGMA(x) _Pragma(#x)
42
43/* Every wrapper below injects TWO clauses the call site does not write, and both change
44 * what the loop means:
45 *
46 * default(firstprivate) -- each thread gets its OWN COPY of every variable the body
47 * mentions and that is not named in an explicit clause, initialised from the value on
48 * entry. Reads see the right value, which is why this is convenient. WRITES DO NOT
49 * ESCAPE: accumulating into an outer variable silently produces nothing, and the loop
50 * still compiles and still gives the correct answer when OpenMP is disabled or the
51 * thread count is 1 -- so it passes casual testing. Name such a variable in an explicit
52 * shared()/reduction() clause. Pointers are copied as POINTERS, so writes THROUGH one
53 * are shared as usual; it is the scalar accumulators that are the trap.
54 *
55 * schedule(static) -- iterations are split into equal contiguous blocks up front, which
56 * is right for uniform per-pixel work and wrong when cost varies by row or when a tile
57 * may exit early. Pass an explicit schedule() for those.
58 *
59 * All of them expand to NOTHING when _OPENMP is undefined, so a body that relies on being
60 * parallel for correctness -- rather than merely for speed -- is silently serialised.
61 */
62
64#define __OMP_PARALLEL__(...) OMP_PRAGMA(omp parallel default(firstprivate) __VA_ARGS__)
65#define __OMP_PARALLEL_FOR__(...) OMP_PRAGMA(omp parallel for default(firstprivate) schedule(static) __VA_ARGS__)
66#define __OMP_PARALLEL_FOR_SIMD__(...) OMP_PRAGMA(omp parallel for simd default(firstprivate) schedule(simd:static) __VA_ARGS__)
67#define __OMP_FOR_SIMD__(...) OMP_PRAGMA(omp for simd schedule(simd:static) __VA_ARGS__)
68#define __OMP_FOR__(...) OMP_PRAGMA(omp for schedule(static) __VA_ARGS__)
69#define __OMP_SIMD__(...) OMP_PRAGMA(omp simd __VA_ARGS__)
70#define __OMP_DECLARE_SIMD__(...) OMP_PRAGMA(omp declare simd __VA_ARGS__)
71
82#define __OMP_PARALLEL_FOR_CPP__(...) OMP_PRAGMA(omp parallel for default(none) schedule(static) __VA_ARGS__)
83
84// TRUE while the caller runs inside a parallel region. Diagnostics belong outside one: a message
85// printed per thread floods the log and interleaves mid-line with the other threads' output.
86#define dt_omp_in_parallel() (omp_in_parallel() != 0)
87
88#else /* _OPENMP */
89
90# define omp_get_max_threads() 1
91# define omp_get_thread_num() 0
92# define dt_omp_in_parallel() 0
93
94#define __OMP_PARALLEL__(...)
95#define __OMP_PARALLEL_FOR__(...)
96#define __OMP_PARALLEL_FOR_SIMD__(...)
97#define __OMP_FOR_SIMD__(...)
98#define __OMP_FOR__(...)
99#define __OMP_SIMD__(...)
100#define __OMP_DECLARE_SIMD__(...)
101
102#define __OMP_PARALLEL_FOR_CPP__(...)
103
104#endif /* _OPENMP */
105
106#if defined(__x86_64__) || defined(__i386__)
107#include <immintrin.h>
108#endif
109
110#ifdef __cplusplus
111extern "C" {
112#endif
113
125
129static inline int dt_get_thread_num()
130{
131#ifdef _OPENMP
132 return omp_get_thread_num();
133#else
134 return 0;
135#endif
136}
137
138// after writing data using copy_pixel_nontemporal, it is necessary to
139// ensure that the writes have completed before attempting reads from
140// a different core. This function produces the required memory
141// fence to ensure proper visibility
142static inline void dt_sfence()
143{
144#if defined(__x86_64__) || defined(__i386__)
145 _mm_sfence();
146#else
147 // the following generates an MFENCE instruction on x86/x64. We
148 // only really need SFENCE, which is less expensive, but none of the
149 // other memory orders generate *any* fence instructions on x64.
151#endif
152}
153
154// if the copy_pixel_nontemporal() writes were inside an OpenMP
155// parallel loop, the OpenMP parallelization will have performed a
156// memory fence before resuming single-threaded operation, so a
157// dt_sfence would be superfluous. But if compiled without OpenMP
158// parallelization, we should play it safe and emit a memory fence.
159// This function should be used right after a parallelized for loop,
160// where it will produce a barrier only if needed.
161#ifdef _OPENMP
162#define dt_omploop_sfence()
163#else
164#define dt_omploop_sfence() dt_sfence()
165#endif
166
167#ifdef __cplusplus
168}
169#endif
170
171#endif // DT_SYSTEM_OPENMP_H
172
173// clang-format off
174// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
175// vim: shiftwidth=2 expandtab tabstop=2 cindent
176// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
177// clang-format on
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:517
static void dt_sfence()
Definition openmp.h:142
static int dt_get_thread_num()
Index of the calling thread within its parallel region, 0 outside one.
Definition openmp.h:129
#define omp_get_thread_num()
Definition openmp.h:91