Arcane  v4.1.2.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// 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 bool 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 return false;
113 }
114 m_nb_thread_finished = 0;
115 ++m_timestamp;
116 return true;
117 }
118 private:
119
120 Int32 m_nb_thread = 0;
121 std::atomic<Int32> m_nb_thread_finished = 0;
122 std::atomic<Int32> m_timestamp = 0;
123};
124
125/*---------------------------------------------------------------------------*/
126/*---------------------------------------------------------------------------*/
127
128extern "C" IThreadBarrier*
129createGlibThreadBarrier();
130
131/*---------------------------------------------------------------------------*/
132/*---------------------------------------------------------------------------*/
136class TBBThreadImplementation
139{
140 ARCCORE_DEFINE_REFERENCE_COUNTED_INCLASS_METHODS();
141
142 void addReference() override { ReferenceCounterImpl::addReference(); }
143 void removeReference() override { ReferenceCounterImpl::removeReference(); }
144
145 public:
146
147 class StartFunc
148 {
149 public:
150
151 explicit StartFunc(IFunctor* f)
152 : m_f(f)
153 {}
154 void operator()() const { m_f->executeFunctor(); }
155 IFunctor* m_f = nullptr;
156 };
157
158 public:
159
160 TBBThreadImplementation()
161 {
162 if (!platform::getEnvironmentVariable("ARCANE_SPINLOCK_BARRIER").null())
163 m_use_tbb_barrier = true;
164 m_std_thread_implementation = Arccore::Concurrency::createStdThreadImplementation();
165 }
166
167 ~TBBThreadImplementation() override
168 {
169 //std::cout << "DESTROYING TBB IMPLEMENTATION\n";
170 GlobalMutex::destroy();
171 if (m_global_mutex_impl)
172 this->destroyMutex(m_global_mutex_impl);
173 }
174
175 public:
176
177 void build()
178 {
179 }
180
181 void initialize() override
182 {
183 m_global_mutex_impl = createMutex();
184 GlobalMutex::init(m_global_mutex_impl);
185 }
186
187 public:
188
189 ThreadImpl* createThread(IFunctor* f) override
190 {
191 return reinterpret_cast<ThreadImpl*>(new ThreadType(StartFunc(f)));
192 }
193 void joinThread(ThreadImpl* t) override
194 {
195 auto* tt = reinterpret_cast<ThreadType*>(t);
196 tt->join();
197 }
198 void destroyThread(ThreadImpl* t) override
199 {
200 auto* tt = reinterpret_cast<ThreadType*>(t);
201 delete tt;
202 }
203
204 void createSpinLock(Int64* spin_lock_addr) override
205 {
206 void* addr = spin_lock_addr;
207 new (addr) tbb::spin_mutex();
208 }
209 void lockSpinLock(Int64* spin_lock_addr,Int64* scoped_spin_lock_addr) override
210 {
211 auto* s = reinterpret_cast<tbb::spin_mutex*>(spin_lock_addr);
212 auto* sl = new (scoped_spin_lock_addr) tbb::spin_mutex::scoped_lock();
213 sl->acquire(*s);
214 }
215 void unlockSpinLock(Int64* spin_lock_addr,Int64* scoped_spin_lock_addr) override
216 {
217 ARCANE_UNUSED(spin_lock_addr);
218 auto* s = reinterpret_cast<tbb::spin_mutex::scoped_lock*>(scoped_spin_lock_addr);
219 s->release();
220 //TODO: detruire le scoped_lock.
221 }
222
223 MutexImpl* createMutex() override
224 {
225 auto* m = new TBBMutexImpl();
226 return reinterpret_cast<MutexImpl*>(m);
227 }
228 void destroyMutex(MutexImpl* mutex) override
229 {
230 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
231 delete tm;
232 }
233 void lockMutex(MutexImpl* mutex) override
234 {
235 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
236 tm->lock();
237 }
238 void unlockMutex(MutexImpl* mutex) override
239 {
240 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
241 tm->unlock();
242 }
243
244 Int64 currentThread() override
245 {
246 Int64 v = arcaneGetThisThreadId();
247 return v;
248 }
249
250 IThreadBarrier* createBarrier() override
251 {
252 // Il faut utiliser les TBB uniquement si demandé, car il utilise
253 // l'attente active ce qui peut vite mettre la machine à genoux
254 // si le nombre de threads total est supérieur au nombre de cœurs
255 // de la machine.
256 if (m_use_tbb_barrier)
257 return new TBBBarrier();
258 return m_std_thread_implementation->createBarrier();
259 }
260
261 private:
262
263 bool m_use_tbb_barrier = false;
264 MutexImpl* m_global_mutex_impl = nullptr;
265 Ref<IThreadImplementation> m_std_thread_implementation;
266};
267
268/*---------------------------------------------------------------------------*/
269/*---------------------------------------------------------------------------*/
270
271class TBBThreadImplementationService
273{
274 public:
275
276 TBBThreadImplementationService() = default;
277
278 public:
279
280 void build() {}
281 public:
282 Ref<IThreadImplementation> createImplementation() override
283 {
285 }
286};
287
288/*---------------------------------------------------------------------------*/
289/*---------------------------------------------------------------------------*/
290
291ARCANE_DI_REGISTER_PROVIDER(TBBThreadImplementationService,
292 DependencyInjection::ProviderProperty("TBBThreadImplementationService"),
293 ARCANE_DI_INTERFACES(IThreadImplementationService),
294 ARCANE_DI_EMPTY_CONSTRUCTOR());
295
296/*---------------------------------------------------------------------------*/
297/*---------------------------------------------------------------------------*/
298
299} // End namespace Arcane
300
301/*---------------------------------------------------------------------------*/
302/*---------------------------------------------------------------------------*/
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.