Arcane  v4.1.7.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-2026 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-2026 */
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// NOTE:
31// Cette implémentation n'est plus l'implémentation par défaut depuis fin 2025.
32// L'implémentation par défaut est maintenant celle utilise la STL.
33// Si tout est OK on pourra supprimer cette implémentation fin 2026 par exemple.
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38namespace Arcane
39{
40
41/*---------------------------------------------------------------------------*/
42/*---------------------------------------------------------------------------*/
43
44typedef std::thread::id ThreadId;
45typedef std::thread ThreadType;
46// Essaie de convertir un std::thread::id en un 'Int64'.
47// Il n'existe pas de moyen portable de le faire donc on fait quelque
48// chose de pas forcément propre. A terme il serait préférable de supprimer
49// la méthode IThreadImplementation::currentThread().
50inline Int64 arcaneGetThisThreadId()
51{
52 Int64 v = static_cast<Int64>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
53 return v;
54}
55
56/*---------------------------------------------------------------------------*/
57/*---------------------------------------------------------------------------*/
58
60{
61 public:
62 void lock()
63 {
64 m_mutex.lock();
65 }
66 void unlock()
67 {
68 m_mutex.unlock();
69 }
70 private:
71 std::mutex m_mutex;
72};
73
74/*---------------------------------------------------------------------------*/
75/*---------------------------------------------------------------------------*/
76
77class TBBBarrier
78: public IThreadBarrier
79{
80 public:
81
82 TBBBarrier() = default;
83
84 void destroy() override { delete this; }
85
86 void init(Integer nb_thread) override
87 {
88 m_nb_thread = nb_thread;
89 m_nb_thread_finished = 0;
90 m_timestamp = 0;
91 };
92
93 void wait() override
94 {
95 Int32 ts = m_timestamp;
96 int remaining_thread = m_nb_thread - m_nb_thread_finished.fetch_add(1) - 1;
97 if (remaining_thread > 0) {
98
99 int count = 1;
100 while (m_timestamp==ts){
101 arcaneDoCPUPause(count);
102 if (count<200)
103 count *= 2;
104 else{
105 //count = 0;
106 //__TBB_Yield();
107 //TODO: peut-être rendre la main (__TBB_Yield()) si trop
108 // d'itérations.
109 }
110 }
111 }
112 m_nb_thread_finished = 0;
113 ++m_timestamp;
114 }
115
116 private:
117
118 Int32 m_nb_thread = 0;
119 std::atomic<Int32> m_nb_thread_finished = 0;
120 std::atomic<Int32> m_timestamp = 0;
121};
122
123/*---------------------------------------------------------------------------*/
124/*---------------------------------------------------------------------------*/
125
126extern "C" IThreadBarrier*
127createGlibThreadBarrier();
128
129/*---------------------------------------------------------------------------*/
130/*---------------------------------------------------------------------------*/
134class TBBThreadImplementation
137{
138 ARCCORE_DEFINE_REFERENCE_COUNTED_INCLASS_METHODS();
139
140 void addReference() override { ReferenceCounterImpl::addReference(); }
141 void removeReference() override { ReferenceCounterImpl::removeReference(); }
142
143 public:
144
145 class StartFunc
146 {
147 public:
148
149 explicit StartFunc(IFunctor* f)
150 : m_f(f)
151 {}
152 void operator()() const { m_f->executeFunctor(); }
153 IFunctor* m_f = nullptr;
154 };
155
156 public:
157
158 TBBThreadImplementation()
159 {
160 if (!platform::getEnvironmentVariable("ARCANE_SPINLOCK_BARRIER").null())
161 m_use_tbb_barrier = true;
162 m_std_thread_implementation = Arccore::Concurrency::createStdThreadImplementation();
163 }
164
165 ~TBBThreadImplementation() override
166 {
167 //std::cout << "DESTROYING TBB IMPLEMENTATION\n";
168 GlobalMutex::destroy();
169 if (m_global_mutex_impl)
170 this->destroyMutex(m_global_mutex_impl);
171 }
172
173 public:
174
175 void build()
176 {
177 }
178
179 void initialize() override
180 {
181 m_global_mutex_impl = createMutex();
182 GlobalMutex::init(m_global_mutex_impl);
183 }
184
185 public:
186
187 ThreadImpl* createThread(IFunctor* f) override
188 {
189 return reinterpret_cast<ThreadImpl*>(new ThreadType(StartFunc(f)));
190 }
191 void joinThread(ThreadImpl* t) override
192 {
193 auto* tt = reinterpret_cast<ThreadType*>(t);
194 tt->join();
195 }
196 void destroyThread(ThreadImpl* t) override
197 {
198 auto* tt = reinterpret_cast<ThreadType*>(t);
199 delete tt;
200 }
201
202 void createSpinLock(Int64* spin_lock_addr) override
203 {
204 void* addr = spin_lock_addr;
205 new (addr) tbb::spin_mutex();
206 }
207 void lockSpinLock(Int64* spin_lock_addr,Int64* scoped_spin_lock_addr) override
208 {
209 auto* s = reinterpret_cast<tbb::spin_mutex*>(spin_lock_addr);
210 auto* sl = new (scoped_spin_lock_addr) tbb::spin_mutex::scoped_lock();
211 sl->acquire(*s);
212 }
213 void unlockSpinLock(Int64* spin_lock_addr,Int64* scoped_spin_lock_addr) override
214 {
215 ARCANE_UNUSED(spin_lock_addr);
216 auto* s = reinterpret_cast<tbb::spin_mutex::scoped_lock*>(scoped_spin_lock_addr);
217 s->release();
218 //TODO: detruire le scoped_lock.
219 }
220
221 MutexImpl* createMutex() override
222 {
223 auto* m = new TBBMutexImpl();
224 return reinterpret_cast<MutexImpl*>(m);
225 }
226 void destroyMutex(MutexImpl* mutex) override
227 {
228 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
229 delete tm;
230 }
231 void lockMutex(MutexImpl* mutex) override
232 {
233 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
234 tm->lock();
235 }
236 void unlockMutex(MutexImpl* mutex) override
237 {
238 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
239 tm->unlock();
240 }
241
242 Int64 currentThread() override
243 {
244 Int64 v = arcaneGetThisThreadId();
245 return v;
246 }
247
248 IThreadBarrier* createBarrier() override
249 {
250 // Il faut utiliser les TBB uniquement si demandé, car il utilise
251 // l'attente active ce qui peut vite mettre la machine à genoux
252 // si le nombre de threads total est supérieur au nombre de cœurs
253 // de la machine.
254 if (m_use_tbb_barrier)
255 return new TBBBarrier();
256 return m_std_thread_implementation->createBarrier();
257 }
258
259 private:
260
261 bool m_use_tbb_barrier = false;
262 MutexImpl* m_global_mutex_impl = nullptr;
263 Ref<IThreadImplementation> m_std_thread_implementation;
264};
265
266/*---------------------------------------------------------------------------*/
267/*---------------------------------------------------------------------------*/
268
269class TBBThreadImplementationService
271{
272 public:
273
274 TBBThreadImplementationService() = default;
275
276 public:
277
278 void build() {}
279 public:
280 Ref<IThreadImplementation> createImplementation() override
281 {
283 }
284};
285
286/*---------------------------------------------------------------------------*/
287/*---------------------------------------------------------------------------*/
288
289ARCANE_DI_REGISTER_PROVIDER(TBBThreadImplementationService,
290 DependencyInjection::ProviderProperty("TBBThreadImplementationService"),
291 ARCANE_DI_INTERFACES(IThreadImplementationService),
292 ARCANE_DI_EMPTY_CONSTRUCTOR());
293
294/*---------------------------------------------------------------------------*/
295/*---------------------------------------------------------------------------*/
296
297} // End namespace Arcane
298
299/*---------------------------------------------------------------------------*/
300/*---------------------------------------------------------------------------*/
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 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 wait() override
Bloque et attend que tous les threads appellent cette méthode.
void init(Integer nb_thread) override
Initialise la barrière pour nb_thread.
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.