Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:28:02

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2022 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 // uncomment to remove all use of tbb library.
0012 // #define ACTS_EXAMPLES_NO_TBB
0013 
0014 #ifdef ACTS_EXAMPLES_NO_TBB
0015 #define ACTS_EXAMPLES_WITH_TBB(a)
0016 #include <stdexcept>
0017 #else
0018 #define ACTS_EXAMPLES_WITH_TBB(a) a
0019 #include <optional>
0020 
0021 #include <tbb/parallel_for.h>
0022 #include <tbb/queuing_mutex.h>
0023 #include <tbb/task_arena.h>
0024 #endif
0025 
0026 /// Wrapper for most of the tbb functions that we use in Sequencer.
0027 ///
0028 /// It disables the use of tbb if nthreads=1.
0029 /// Note that only a small subset of tbb functions are implemented, and
0030 /// tbb::blocked_range (which doesn't require any thread setup) is still taken
0031 /// from the tbb library.
0032 ///
0033 /// However, if ACTS_EXAMPLES_NO_TBB is defined, then don't use tbb library at
0034 /// all (requires nthreads=1 or -1). This allows the ACTS Examples to be built
0035 /// without the tbb library (and reduces the dependency on ROOT).
0036 /// In this case, we provide our own minimal implementation of
0037 /// tbb::blocked_range.
0038 ///
0039 /// Based on an idea from
0040 ///   https://stackoverflow.com/questions/59736661/how-to-completely-switch-off-threading-in-tbb-code
0041 
0042 #ifdef ACTS_EXAMPLES_NO_TBB
0043 namespace ActsExamples::tbb {
0044 namespace task_arena {
0045 constexpr int automatic = -1;
0046 }  // namespace task_arena
0047 
0048 template <typename Value>
0049 struct blocked_range {
0050   blocked_range(Value begin_, Value end_) : my_end(end_), my_begin(begin_) {}
0051   Value begin() const { return my_begin; }
0052   Value end() const { return my_end; }
0053 
0054  private:
0055   Value my_end;
0056   Value my_begin;
0057 };
0058 }  // namespace ActsExamples::tbb
0059 #endif
0060 
0061 namespace ActsExamples::tbbWrap {
0062 /// enableTBB keeps a record of whether we are multi-threaded (nthreads!=1) or
0063 /// not. This is set once in task_arena and stored globally.
0064 /// This means that enableTBB(nthreads) itself is not thread-safe. That should
0065 /// be fine because the task_arena is initialised before spawning any threads.
0066 /// If multi-threading is ever enabled, then it is not disabled.
0067 static bool enableTBB(int nthreads = -99) {
0068   static bool setting = false;
0069   if (nthreads != -99) {
0070 #ifdef ACTS_EXAMPLES_NO_TBB
0071     if (nthreads > 1) {
0072       throw std::runtime_error(
0073           "tbb is not available, so can't do multi-threading.");
0074     }
0075 #else
0076     bool newSetting = (nthreads != 1);
0077     if (!setting && newSetting) {
0078       setting = newSetting;
0079     }
0080 #endif
0081   }
0082   return setting;
0083 }
0084 
0085 /// Small wrapper for tbb::task_arena.
0086 /// Note that the tbbWrap::task_arena constructor is not thread-safe.
0087 /// That should be fine because the task_arena is initialised before spawning
0088 /// any threads.
0089 class task_arena {
0090 #ifndef ACTS_EXAMPLES_NO_TBB
0091   std::optional<tbb::task_arena> tbb;
0092 #endif
0093 
0094  public:
0095   task_arena(int nthreads = tbb::task_arena::automatic,
0096              unsigned ACTS_EXAMPLES_WITH_TBB(res) = 1) {
0097     if (enableTBB(nthreads)) {
0098 #ifndef ACTS_EXAMPLES_NO_TBB
0099       tbb.emplace(nthreads, res);
0100 #endif
0101     }
0102   }
0103 
0104   template <typename F>
0105   void execute(const F& f) {
0106 #ifndef ACTS_EXAMPLES_NO_TBB
0107     if (tbb) {
0108       tbb->execute(f);
0109     } else
0110 #endif
0111     {
0112       f();
0113     }
0114   }
0115 };
0116 
0117 /// Small wrapper for tbb::parallel_for.
0118 class parallel_for {
0119  public:
0120   template <typename R, typename F>
0121   parallel_for(const R& r, const F& f) {
0122 #ifndef ACTS_EXAMPLES_NO_TBB
0123     if (enableTBB()) {
0124       tbb::parallel_for(r, f);
0125     } else
0126 #endif
0127     {
0128       for (auto i = r.begin(); i != r.end(); ++i) {  // use default grainsize=1
0129         f(R(i, i + 1));
0130       }
0131     }
0132   }
0133 };
0134 
0135 /// Small wrapper for tbb::queuing_mutex and tbb::queuing_mutex::scoped_lock.
0136 class queuing_mutex {
0137 #ifndef ACTS_EXAMPLES_NO_TBB
0138   std::optional<tbb::queuing_mutex> tbb;
0139 #endif
0140 
0141  public:
0142   queuing_mutex() {
0143 #ifndef ACTS_EXAMPLES_NO_TBB
0144     if (enableTBB()) {
0145       tbb.emplace();
0146     }
0147 #endif
0148   }
0149 
0150   class scoped_lock {
0151 #ifndef ACTS_EXAMPLES_NO_TBB
0152     std::optional<tbb::queuing_mutex::scoped_lock> tbb;
0153 #endif
0154 
0155    public:
0156     scoped_lock() {
0157 #ifndef ACTS_EXAMPLES_NO_TBB
0158       if (enableTBB()) {
0159         tbb.emplace();
0160       }
0161 #endif
0162     }
0163 
0164     scoped_lock(queuing_mutex& ACTS_EXAMPLES_WITH_TBB(m)) {
0165 #ifndef ACTS_EXAMPLES_NO_TBB
0166       if (enableTBB()) {
0167         tbb.emplace(*m.tbb);
0168       }
0169 #endif
0170     }
0171   };
0172 };
0173 
0174 }  // namespace ActsExamples::tbbWrap