#pragma once #include #include namespace omp_config{ // Configure OpenMP behavior at runtime. inline void omp_configure(int max_active_levels, bool dynamic_threads, const std::vector& threads_per_level = {}, bool bind_close = true) { // 1) Allow nested parallel regions (levels of teams) // Example: outer #pragma omp parallel ... { inner #pragma omp parallel ... } omp_set_max_active_levels(max_active_levels); // 1 = only top-level; 2+ enables nesting // 2) Let the runtime shrink/grow thread counts if it thinks it should // (helps avoid oversubscription when you accidentally ask for too many threads) omp_set_dynamic(dynamic_threads ? 1 : 0); // 3) Thread binding (keep threads near their cores) is controlled via env vars, // so here we just *recommend* a good default (see below). You *can* setenv() // in code, but it’s cleaner to do it outside the program. (void)bind_close; // documented below in env var section // 4) Top-level default thread count (inner levels are usually set per region) if (!threads_per_level.empty()) { omp_set_num_threads(threads_per_level[0]); // e.g. 16 for the outermost team // Inner levels: // - Use num_threads(threads_per_level[L]) on the inner #pragma omp parallel // - or set OMP_NUM_THREADS="outer,inner,inner2" as an environment variable } } // ---------- Helper: may we create another team? ---------- inline bool omp_parallel_allowed() { #ifdef _OPENMP // If we’re not in parallel, we can spawn. if (!omp_in_parallel()) return true; // Already inside parallel: allow only if nesting is enabled and not at limit. int level = omp_get_active_level(); // 0 outside parallel, 1 inside, ... int maxlv = omp_get_max_active_levels(); // user/runtime cap return static_cast(level < maxlv); #else return false; // no OpenMP → no extra teams #endif } } // namespace omp_config