High-precision calculations for one- and two-valence atomic systems
omp.hpp
Go to the documentation of this file.
1#pragma once
2/*! @file
3 @brief Include instead of `<omp.h>` to allow compilation with or without OpenMP.
4
5 @details
6 If OpenMP is not available, stub macros are provided so `#pragma omp`
7 directives are silently ignored, and the common `omp_*thread` functions return
8 0 or 1.
9*/
10#include <dlfcn.h>
11#include <string>
12
13#if defined(_OPENMP)
14#include <omp.h>
15namespace qip {
16//! True if compiled with OpenMP support, false otherwise.
17constexpr bool use_omp = true;
18} // namespace qip
19#else
20#pragma GCC diagnostic ignored "-Wunknown-pragmas"
21namespace qip {
22//! True if compiled with OpenMP support, false otherwise.
23constexpr bool use_omp = false;
24} // namespace qip
25#define omp_get_thread_num() 0
26#define omp_get_max_threads() 1
27#define omp_get_num_threads() 1
28#endif
29
30namespace qip {
31//! Returns a short string describing the threading status, e.g.
32//! "Using OpenMP with 8 threads." or "Single-threaded."
33inline std::string omp_details() {
34 return use_omp ? "Using OpenMP with " +
35 std::to_string(omp_get_max_threads()) + " threads." :
36 "Single-threaded.";
37}
38
39//==============================================================================
40/*!
41 @brief Scoped guard: forces single-threaded BLAS (OpenBLAS) for the
42 lifetime of the object; restores the previous BLAS thread count on
43 destruction.
44
45 @details
46 BLAS calls made from inside an OpenMP parallel region contend badly with
47 the pthread-build OpenBLAS thread pool (every concurrent call fights over
48 the same pool and its global lock). The outer OpenMP parallelism already
49 saturates the cores there, so BLAS should run single-threaded. Construct
50 one of these in serial code just before entering such a region; the
51 effect is strictly scoped to the object's lifetime.
52
53 OpenBLAS is located at run time (dlsym), so there is no compile-time
54 dependence on it: with any other BLAS library the guard is a no-op
55 (reference BLAS is single-threaded anyway). Also a no-op when OpenMP is
56 not compiled in, or when only one OpenMP thread is available: BLAS
57 threading is then the only parallelism, and is left alone.
58*/
60public:
62 if constexpr (use_omp) {
63 if (omp_get_max_threads() > 1 && oblas_get() && oblas_set()) {
64 m_saved = oblas_get()();
65 if (m_saved > 1) {
66 oblas_set()(1);
67 }
68 }
69 }
70 }
72 if (m_saved > 1) {
73 oblas_set()(m_saved);
74 }
75 }
76 SingleThreadBlas(const SingleThreadBlas &) = delete;
77 SingleThreadBlas &operator=(const SingleThreadBlas &) = delete;
78
79private:
80 int m_saved{0};
81 using fn_set = void (*)(int);
82 using fn_get = int (*)();
83 // Resolved once; nullptr when the linked BLAS is not OpenBLAS
84 static fn_set oblas_set() {
85 static const auto f =
86 reinterpret_cast<fn_set>(dlsym(RTLD_DEFAULT, "openblas_set_num_threads"));
87 return f;
88 }
89 static fn_get oblas_get() {
90 static const auto f =
91 reinterpret_cast<fn_get>(dlsym(RTLD_DEFAULT, "openblas_get_num_threads"));
92 return f;
93 }
94};
95} // namespace qip
Scoped guard: forces single-threaded BLAS (OpenBLAS) for the lifetime of the object; restores the pre...
Definition omp.hpp:59
General-purpose utility library.
Definition Array.hpp:23
constexpr bool use_omp
True if compiled with OpenMP support, false otherwise.
Definition omp.hpp:23
std::string omp_details()
Returns a short string describing the threading status, e.g. "Using OpenMP with 8 threads....
Definition omp.hpp:33