Arcane  v4.1.0.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
TBBThreadImplementation.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* TBBThreadImplementation.cc (C) 2000-2025 */
9/* */
10/* Implémentation des threads utilisant TBB (Intel Threads Building Blocks). */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/IThreadImplementationService.h"
15#include "arcane/utils/IThreadBarrier.h"
16#include "arcane/utils/NotImplementedException.h"
17#include "arcane/utils/IFunctor.h"
18#include "arcane/utils/Mutex.h"
19#include "arcane/utils/PlatformUtils.h"
20#include "arccore/base/internal/DependencyInjection.h"
21
22#include "arcane/parallel/thread/ArcaneThreadMisc.h"
23
24#include <tbb/tbb.h>
25#define ARCANE_TBB_USE_STDTHREAD
26#include <thread>
27#include <mutex>
28#include <new>
29
30/*---------------------------------------------------------------------------*/
31/*---------------------------------------------------------------------------*/
32
33namespace Arcane
34{
35
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39typedef std::thread::id ThreadId;
40typedef std::thread ThreadType;
41// Essaie de convertir un std::thread::id en un 'Int64'.
42// Il n'existe pas de moyen portable de le faire donc on fait quelque
43// chose de pas forcément propre. A terme il serait préférable de supprimer
44// la méthode IThreadImplementation::currentThread().
45inline Int64 arcaneGetThisThreadId()
46{
47 Int64 v = static_cast<Int64>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
48 return v;
49}
50
51/*---------------------------------------------------------------------------*/
52/*---------------------------------------------------------------------------*/
53
55{
56 public:
57 void lock()
58 {
59 m_mutex.lock();
60 }
61 void unlock()
62 {
63 m_mutex.unlock();
64 }
65 private:
66 std::mutex m_mutex;
67};
68
69/*---------------------------------------------------------------------------*/
70/*---------------------------------------------------------------------------*/
71
72class TBBBarrier
73: public IThreadBarrier
74{
75 public:
76
77 TBBBarrier() = default;
78
79 void destroy() override { delete this; }
80
81 void init(Integer nb_thread) override
82 {
83 m_nb_thread = nb_thread;
84 m_nb_thread_finished = 0;
85 m_timestamp = 0;
86 };
87
88 bool wait() override
89 {
90 Int32 ts = m_timestamp;
91 int remaining_thread = m_nb_thread - m_nb_thread_finished.fetch_add(1) - 1;
92 if (remaining_thread > 0) {
93
94 int count = 1;
95 while (m_timestamp==ts){
96 arcaneDoCPUPause(count);
97 if (count<200)
98 count *= 2;
99 else{
100 //count = 0;
101 //__TBB_Yield();
102 //TODO: peut-être rendre la main (__TBB_Yield()) si trop
103 // d'itérations.
104 }
105 }
106
107 return false;
108 }
109 m_nb_thread_finished = 0;
110 ++m_timestamp;
111 return true;
112 }
113 private:
114
115 Int32 m_nb_thread = 0;
116 std::atomic<Int32> m_nb_thread_finished = 0;
117 std::atomic<Int32> m_timestamp = 0;
118};
119
120/*---------------------------------------------------------------------------*/
121/*---------------------------------------------------------------------------*/
122
123extern "C" IThreadBarrier*
124createGlibThreadBarrier();
125
126/*---------------------------------------------------------------------------*/
127/*---------------------------------------------------------------------------*/
131class TBBThreadImplementation
134{
135 ARCCORE_DEFINE_REFERENCE_COUNTED_INCLASS_METHODS();
136
137 void addReference() override { ReferenceCounterImpl::addReference(); }
138 void removeReference() override { ReferenceCounterImpl::removeReference(); }
139
140 public:
141
142 class StartFunc
143 {
144 public:
145
146 explicit StartFunc(IFunctor* f)
147 : m_f(f)
148 {}
149 void operator()() const { m_f->executeFunctor(); }
150 IFunctor* m_f = nullptr;
151 };
152
153 public:
154
155 TBBThreadImplementation()
156 {
157 if (!platform::getEnvironmentVariable("ARCANE_SPINLOCK_BARRIER").null())
158 m_use_tbb_barrier = true;
159 m_std_thread_implementation = Arccore::Concurrency::createStdThreadImplementation();
160 }
161
162 ~TBBThreadImplementation() override
163 {
164 //std::cout << "DESTROYING TBB IMPLEMENTATION\n";
165 GlobalMutex::destroy();
166 if (m_global_mutex_impl)
167 this->destroyMutex(m_global_mutex_impl);
168 }
169
170 public:
171
172 void build()
173 {
174 }
175
176 void initialize() override
177 {
178 m_global_mutex_impl = createMutex();
179 GlobalMutex::init(m_global_mutex_impl);
180 }
181
182 public:
183
184 ThreadImpl* createThread(IFunctor* f) override
185 {
186 return reinterpret_cast<ThreadImpl*>(new ThreadType(StartFunc(f)));
187 }
188 void joinThread(ThreadImpl* t) override
189 {
190 auto* tt = reinterpret_cast<ThreadType*>(t);
191 tt->join();
192 }
193 void destroyThread(ThreadImpl* t) override
194 {
195 auto* tt = reinterpret_cast<ThreadType*>(t);
196 delete tt;
197 }
198
199 void createSpinLock(Int64* spin_lock_addr) override
200 {
201 void* addr = spin_lock_addr;
202 new (addr) tbb::spin_mutex();
203 }
204 void lockSpinLock(Int64* spin_lock_addr,Int64* scoped_spin_lock_addr) override
205 {
206 auto* s = reinterpret_cast<tbb::spin_mutex*>(spin_lock_addr);
207 auto* sl = new (scoped_spin_lock_addr) tbb::spin_mutex::scoped_lock();
208 sl->acquire(*s);
209 }
210 void unlockSpinLock(Int64* spin_lock_addr,Int64* scoped_spin_lock_addr) override
211 {
212 ARCANE_UNUSED(spin_lock_addr);
213 auto* s = reinterpret_cast<tbb::spin_mutex::scoped_lock*>(scoped_spin_lock_addr);
214 s->release();
215 //TODO: detruire le scoped_lock.
216 }
217
218 MutexImpl* createMutex() override
219 {
220 auto* m = new TBBMutexImpl();
221 return reinterpret_cast<MutexImpl*>(m);
222 }
223 void destroyMutex(MutexImpl* mutex) override
224 {
225 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
226 delete tm;
227 }
228 void lockMutex(MutexImpl* mutex) override
229 {
230 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
231 tm->lock();
232 }
233 void unlockMutex(MutexImpl* mutex) override
234 {
235 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
236 tm->unlock();
237 }
238
239 Int64 currentThread() override
240 {
241 Int64 v = arcaneGetThisThreadId();
242 return v;
243 }
244
245 IThreadBarrier* createBarrier() override
246 {
247 // Il faut utiliser les TBB uniquement si demandé, car il utilise
248 // l'attente active ce qui peut vite mettre la machine à genoux
249 // si le nombre de threads total est supérieur au nombre de cœurs
250 // de la machine.
251 if (m_use_tbb_barrier)
252 return new TBBBarrier();
253 return m_std_thread_implementation->createBarrier();
254 }
255
256 private:
257
258 bool m_use_tbb_barrier = false;
259 MutexImpl* m_global_mutex_impl = nullptr;
260 Ref<IThreadImplementation> m_std_thread_implementation;
261};
262
263/*---------------------------------------------------------------------------*/
264/*---------------------------------------------------------------------------*/
265
266class TBBThreadImplementationService
268{
269 public:
270
271 TBBThreadImplementationService() = default;
272
273 public:
274
275 void build() {}
276 public:
277 Ref<IThreadImplementation> createImplementation() override
278 {
280 }
281};
282
283/*---------------------------------------------------------------------------*/
284/*---------------------------------------------------------------------------*/
285
286ARCANE_DI_REGISTER_PROVIDER(TBBThreadImplementationService,
287 DependencyInjection::ProviderProperty("TBBThreadImplementationService"),
288 ARCANE_DI_INTERFACES(IThreadImplementationService),
289 ARCANE_DI_EMPTY_CONSTRUCTOR());
290
291/*---------------------------------------------------------------------------*/
292/*---------------------------------------------------------------------------*/
293
294} // End namespace Arcane
295
296/*---------------------------------------------------------------------------*/
297/*---------------------------------------------------------------------------*/
static void init(MutexImpl *p)
Initialise le mutex global. Interne a Arccore. Doit être alloué par new.
Definition Mutex.cc:60
Interface d'un service de gestion des threads.
Interface d'un service implémentant le support des threads.
Référence à une instance.
Implémentation thread-safe d'un compteur de référence.
void destroy() override
Détruit la barrière.
void init(Integer nb_thread) override
Initialise la barrière pour nb_thread.
bool wait() override
Bloque et attend que tous les threads appellent cette méthode.
Implémentation des threads utilisant TBB (Intel Threads Building Blocks).
ARCCORE_BASE_EXPORT String getEnvironmentVariable(const String &name)
Variable d'environnement du nom name.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
std::int64_t Int64
Type entier signé sur 64 bits.
void arcaneDoCPUPause(Int32 count)
Utilise l'instruction 'pause' du CPU si possible.
Int32 Integer
Type représentant un entier.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Créé une référence sur un pointeur.
std::int32_t Int32
Type entier signé sur 32 bits.