Arcane  4.2.3.0
Documentation utilisateur
Chargement...
Recherche...
Aucune correspondance
TBBTaskImplementation.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/* TBBTaskImplementation.cc (C) 2000-2026 */
9/* */
10/* Implémentation des tâches utilisant TBB (Intel Threads Building Blocks). */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/base/NotImplementedException.h"
15#include "arccore/base/IFunctor.h"
16#include "arccore/base/ForLoopRanges.h"
17#include "arccore/base/IObservable.h"
18#include "arccore/base/PlatformUtils.h"
19#include "arccore/base/FixedArray.h"
20#include "arccore/base/Profiling.h"
21#include "arccore/base/CheckedConvert.h"
22#include "arccore/base/FixedArray.h"
23#include "arccore/base/ForLoopRunInfo.h"
24#include "arccore/base/internal/DependencyInjection.h"
25
26#include "arccore/concurrency/IThreadImplementation.h"
27#include "arccore/concurrency/Task.h"
28#include "arccore/concurrency/ITaskImplementation.h"
29#include "arccore/concurrency/TaskFactory.h"
30#include "arccore/concurrency/ParallelFor.h"
31#include "arccore/concurrency/internal/TaskFactoryInternal.h"
32
33#include <new>
34#include <stack>
35#include <vector>
36#include <unordered_set>
37#include <iostream>
38
39// Il faut définir cette macro pour que la classe 'blocked_rangeNd' soit disponible
40
41#define TBB_PREVIEW_BLOCKED_RANGE_ND 1
42
43// la macro 'ARCCORE_USE_ONETBB' est définie dans le CMakeLists.txt
44// si on compile avec la version OneTBB version 2021+
45// (https://github.com/oneapi-src/oneTBB.git)
46// A terme ce sera la seule version supportée par Arcane.
47
48// Nécessaire pour avoir accès à task_scheduler_handle
49#define TBB_PREVIEW_WAITING_FOR_WORKERS 1
50#include <tbb/tbb.h>
51#include <oneapi/tbb/concurrent_set.h>
52#include <oneapi/tbb/global_control.h>
53
54#include <thread>
55#include <mutex>
56
57/*---------------------------------------------------------------------------*/
58/*---------------------------------------------------------------------------*/
59
60namespace Arcane
61{
62
64
65// TODO: utiliser un pool mémoire spécifique pour gérer les
66// OneTBBTask pour optimiser les new/delete des instances de cette classe.
67// Auparavant avec les anciennes versions de TBB cela était géré avec
68// la méthode 'tbb::task::allocate_child()'.
69
70/*---------------------------------------------------------------------------*/
71/*---------------------------------------------------------------------------*/
72
73#if (TBB_VERSION_MAJOR > 2022) || (TBB_VERSION_MAJOR == 2022 && TBB_VERSION_MINOR > 0) || defined __TBB_blocked_nd_range_H
74
75// La classe "blocked_rangeNd" a été retirée dans la version
76// 2022.0.0 et remplacée par "blocked_nd_range".
77template <typename Value, unsigned int N>
78using blocked_nd_range = tbb::blocked_nd_range<Value, N>;
79
80#else
81
82template <typename Value, unsigned int N>
83using blocked_nd_range = tbb::blocked_rangeNd<Value, N>;
84
85#endif
86
87/*---------------------------------------------------------------------------*/
88/*---------------------------------------------------------------------------*/
89
90namespace
91{
92 constexpr Int32 cache_line_size = 64;
93 // Positif si on récupère les statistiques d'exécution
94 bool isStatActive()
95 {
97 }
98
99 /*!
100 * \brief Classe permettant de garantir qu'on enregistre les statistiques
101 * d'exécution même en cas d'exception.
102 */
103 class ScopedExecInfo
104 {
105 public:
106
107 explicit ScopedExecInfo(const ForLoopRunInfo& run_info)
108 : m_run_info(run_info)
109 {
110 // Si run_info.execInfo() n'est pas nul, on l'utilise.
111 // Cela signifie que c'est l'appelant de qui va gérer les statistiques
112 // d'exécution. Sinon, on utilise \a m_stat_info si les statistiques
113 // d'exécution sont demandées.
114 ForLoopOneExecStat* ptr = run_info.execStat();
115 if (ptr) {
116 m_stat_info_ptr = ptr;
117 m_use_own_run_info = false;
118 }
119 else
120 m_stat_info_ptr = isStatActive() ? &m_stat_info : nullptr;
121 }
122 ~ScopedExecInfo()
123 {
124#ifdef PRINT_STAT_INFO
125 if (m_stat_info_ptr) {
126 bool is_valid = m_run_info.traceInfo().isValid();
127 if (!is_valid)
128 std::cout << "ADD_OWN_RUN_INFO nb_chunk=" << m_stat_info_ptr->nbChunk()
129 << " stack=" << platform::getStackTrace()
130 << "\n";
131 else
132 std::cout << "ADD_OWN_RUN_INFO nb_chunk=" << m_stat_info_ptr->nbChunk()
133 << " trace_name=" << m_run_info.traceInfo().traceInfo().name() << "\n";
134 }
135#endif
136 if (m_stat_info_ptr && m_use_own_run_info) {
137 ProfilingRegistry::_threadLocalForLoopInstance()->merge(*m_stat_info_ptr, m_run_info.traceInfo());
138 }
139 }
140
141 public:
142
143 ForLoopOneExecStat* statInfo() const { return m_stat_info_ptr; }
144 bool isOwn() const { return m_use_own_run_info; }
145
146 private:
147
148 ForLoopOneExecStat m_stat_info;
149 ForLoopOneExecStat* m_stat_info_ptr = nullptr;
150 ForLoopRunInfo m_run_info;
151 //! Indique si on utilise m_stat_info
152 bool m_use_own_run_info = true;
153 };
154
155 /*---------------------------------------------------------------------------*/
156 /*---------------------------------------------------------------------------*/
157
158 inline int _currentTaskTreadIndex()
159 {
160 // NOTE: Avec OneTBB 2021, la valeur n'est plus '0' si on appelle cette méthode
161 // depuis un thread en dehors d'un task_arena. Avec la version 2021,
162 // la valeur est 65535.
163 // NOTE: Il semble que cela soit un bug de la 2021.3.
164 return tbb::this_task_arena::current_thread_index();
165 }
166
167 inline blocked_nd_range<Int32, 1>
168 _toTBBRange(const ComplexForLoopRanges<1>& r)
169 {
170 return { { r.lowerBound<0>(), r.upperBound<0>() } };
171 }
172
173 inline blocked_nd_range<Int32, 2>
174 _toTBBRange(const ComplexForLoopRanges<2>& r)
175 {
176 return { { r.lowerBound<0>(), r.upperBound<0>() },
177 { r.lowerBound<1>(), r.upperBound<1>() } };
178 }
179
180 inline blocked_nd_range<Int32, 3>
181 _toTBBRange(const ComplexForLoopRanges<3>& r)
182 {
183 return { { r.lowerBound<0>(), r.upperBound<0>() },
184 { r.lowerBound<1>(), r.upperBound<1>() },
185 { r.lowerBound<2>(), r.upperBound<2>() } };
186 }
187
188 inline blocked_nd_range<Int32, 4>
189 _toTBBRange(const ComplexForLoopRanges<4>& r)
190 {
191 return { { r.lowerBound<0>(), r.upperBound<0>() },
192 { r.lowerBound<1>(), r.upperBound<1>() },
193 { r.lowerBound<2>(), r.upperBound<2>() },
194 { r.lowerBound<3>(), r.upperBound<3>() } };
195 }
196
197 /*---------------------------------------------------------------------------*/
198 /*---------------------------------------------------------------------------*/
199
200 inline blocked_nd_range<Int32, 2>
201 _toTBBRangeWithGrain(const blocked_nd_range<Int32, 2>& r, FixedArray<size_t, 2> grain_sizes)
202 {
203 return { { r.dim(0).begin(), r.dim(0).end(), grain_sizes[0] },
204 { r.dim(1).begin(), r.dim(1).end(), grain_sizes[1] } };
205 }
206
207 inline blocked_nd_range<Int32, 3>
208 _toTBBRangeWithGrain(const blocked_nd_range<Int32, 3>& r, FixedArray<size_t, 3> grain_sizes)
209 {
210 return { { r.dim(0).begin(), r.dim(0).end(), grain_sizes[0] },
211 { r.dim(1).begin(), r.dim(1).end(), grain_sizes[1] },
212 { r.dim(2).begin(), r.dim(2).end(), grain_sizes[2] } };
213 }
214
215 inline blocked_nd_range<Int32, 4>
216 _toTBBRangeWithGrain(const blocked_nd_range<Int32, 4>& r, FixedArray<size_t, 4> grain_sizes)
217 {
218 return { { r.dim(0).begin(), r.dim(0).end(), grain_sizes[0] },
219 { r.dim(1).begin(), r.dim(1).end(), grain_sizes[1] },
220 { r.dim(2).begin(), r.dim(2).end(), grain_sizes[2] },
221 { r.dim(3).begin(), r.dim(3).end(), grain_sizes[3] } };
222 }
223
224 /*---------------------------------------------------------------------------*/
225 /*---------------------------------------------------------------------------*/
226
228 _fromTBBRange(const blocked_nd_range<Int32, 2>& r)
229 {
230 using BoundsType = ArrayBounds<MDDim2>;
231 using ArrayExtentType = BoundsType::ArrayExtentType;
232
233 BoundsType lower_bounds(ArrayExtentType(r.dim(0).begin(), r.dim(1).begin()));
234 auto s0 = static_cast<Int32>(r.dim(0).size());
235 auto s1 = static_cast<Int32>(r.dim(1).size());
236 BoundsType sizes(ArrayExtentType(s0, s1));
237 return { lower_bounds, sizes };
238 }
239
241 _fromTBBRange(const blocked_nd_range<Int32, 3>& r)
242 {
243 using BoundsType = ArrayBounds<MDDim3>;
244 using ArrayExtentType = BoundsType::ArrayExtentType;
245
246 BoundsType lower_bounds(ArrayExtentType(r.dim(0).begin(), r.dim(1).begin(), r.dim(2).begin()));
247 auto s0 = static_cast<Int32>(r.dim(0).size());
248 auto s1 = static_cast<Int32>(r.dim(1).size());
249 auto s2 = static_cast<Int32>(r.dim(2).size());
250 BoundsType sizes(ArrayExtentType(s0, s1, s2));
251 return { lower_bounds, sizes };
252 }
253
255 _fromTBBRange(const blocked_nd_range<Int32, 4>& r)
256 {
257 using BoundsType = ArrayBounds<MDDim4>;
258 using ArrayExtentType = typename BoundsType::ArrayExtentType;
259
260 BoundsType lower_bounds(ArrayExtentType(r.dim(0).begin(), r.dim(1).begin(), r.dim(2).begin(), r.dim(3).begin()));
261 auto s0 = static_cast<Int32>(r.dim(0).size());
262 auto s1 = static_cast<Int32>(r.dim(1).size());
263 auto s2 = static_cast<Int32>(r.dim(2).size());
264 auto s3 = static_cast<Int32>(r.dim(3).size());
265 BoundsType sizes(ArrayExtentType(s0, s1, s2, s3));
266 return { lower_bounds, sizes };
267 }
268
269} // namespace
270
271/*---------------------------------------------------------------------------*/
272/*---------------------------------------------------------------------------*/
273
274class OneTBBTaskFunctor
275{
276 public:
277
278 OneTBBTaskFunctor(ITaskFunctor* functor, ITask* task)
279 : m_functor(functor)
280 , m_task(task)
281 {}
282
283 public:
284
285 void operator()() const
286 {
287 if (m_functor) {
288 ITaskFunctor* tf = m_functor;
289 m_functor = nullptr;
290 TaskContext task_context(m_task);
291 //cerr << "FUNC=" << typeid(*tf).name();
292 tf->executeFunctor(task_context);
293 }
294 }
295
296 public:
297
298 mutable ITaskFunctor* m_functor;
299 ITask* m_task;
300};
301
302/*---------------------------------------------------------------------------*/
303/*---------------------------------------------------------------------------*/
304
305class OneTBBTask
306: public ITask
307{
308 public:
309
310 static const int FUNCTOR_CLASS_SIZE = 32;
311
312 public:
313
314 explicit OneTBBTask(ITaskFunctor* f)
315 : m_functor(f)
316 {
317 m_functor = f->clone(m_functor_buf.data(), FUNCTOR_CLASS_SIZE);
318 }
319
320 public:
321
322 OneTBBTaskFunctor taskFunctor() { return OneTBBTaskFunctor(m_functor, this); }
323 void launchAndWait() override;
324 void launchAndWait(ConstArrayView<ITask*> tasks) override;
325
326 void launch() override;
327 void wait() override;
328
329 protected:
330
331 ITask* _createChildTask(ITaskFunctor* functor) override;
332
333 public:
334
335 ITaskFunctor* m_functor = nullptr;
337 Ref<tbb::task_group> m_task_group;
338};
339using TBBTask = OneTBBTask;
340
341/*---------------------------------------------------------------------------*/
342/*---------------------------------------------------------------------------*/
343
344/*
345 * Ne pas utiliser l'observer locale au task_arena.
346 * Utiliser l'observer global au scheduler.
347 * Pour l'id, utiliser tbb::this_task_arena::current_thread_index().
348 */
349class TBBTaskImplementation
350: public ITaskImplementation
351{
352 class Impl;
353 class ParallelForExecute;
354 template <int RankValue>
355 class MDParallelForExecute;
356
357 public:
358
359 // Pour des raisons de performance, s'aligne sur une ligne de cache
360 // et utilise un padding.
361 class ARCCORE_ALIGNAS_PACKED(64) TaskThreadInfo
362 {
363 public:
364
365 TaskThreadInfo()
366 : m_task_index(-1)
367 {}
368
369 public:
370
371 void setTaskIndex(Integer v) { m_task_index = v; }
372 Integer taskIndex() const { return m_task_index; }
373
374 private:
375
376 Integer m_task_index;
377 };
378
379 /*!
380 * \brief Classe pour positionner TaskThreadInfo::taskIndex().
381 *
382 * Permet de positionner la valeur de TaskThreadInfo::taskIndex()
383 * lors de la construction et de remettre la valeur d'avant
384 * dans le destructeur.
385 */
386 class TaskInfoLockGuard
387 {
388 public:
389
390 TaskInfoLockGuard(TaskThreadInfo* tti, Integer task_index)
391 : m_tti(tti)
392 , m_old_task_index(-1)
393 {
394 if (tti) {
395 m_old_task_index = tti->taskIndex();
396 tti->setTaskIndex(task_index);
397 }
398 }
399 ~TaskInfoLockGuard()
400 {
401 if (m_tti)
402 m_tti->setTaskIndex(m_old_task_index);
403 }
404
405 private:
406
407 TaskThreadInfo* m_tti;
408 Integer m_old_task_index;
409 };
410
411 public:
412
413 TBBTaskImplementation() = default;
414 ~TBBTaskImplementation() override;
415
416 public:
417
418 void build() {}
419 void initialize(Int32 nb_thread) override;
420 void terminate() override;
421
423 {
424 OneTBBTask* t = new OneTBBTask(f);
425 return t;
426 }
427
428 void executeParallelFor(Int32 begin, Int32 size, const ParallelLoopOptions& options, IRangeFunctor* f) final;
429 void executeParallelFor(Int32 begin, Int32 size, Integer grain_size, IRangeFunctor* f) final;
430 void executeParallelFor(Int32 begin, Int32 size, IRangeFunctor* f) final
431 {
433 }
434 void executeParallelFor(const ParallelFor1DLoopInfo& loop_info) override;
435
437 const ForLoopRunInfo& run_info,
438 IMDRangeFunctor<1>* functor) final
439 {
440 _executeMDParallelFor<1>(loop_ranges, functor, run_info);
441 }
443 const ForLoopRunInfo& run_info,
444 IMDRangeFunctor<2>* functor) final
445 {
446 _executeMDParallelFor<2>(loop_ranges, functor, run_info);
447 }
449 const ForLoopRunInfo& run_info,
450 IMDRangeFunctor<3>* functor) final
451 {
452 _executeMDParallelFor<3>(loop_ranges, functor, run_info);
453 }
455 const ForLoopRunInfo& run_info,
456 IMDRangeFunctor<4>* functor) final
457 {
458 _executeMDParallelFor<4>(loop_ranges, functor, run_info);
459 }
460
461 bool isActive() const final
462 {
463 return m_is_active;
464 }
465
467 {
468 return (nbAllowedThread() <= 1) ? 0 : _currentTaskTreadIndex();
469 }
470
471 Int32 currentTaskIndex() const final;
472
473 void printInfos(std::ostream& o) const final;
474
475 public:
476
477 /*!
478 * \brief Instance de \a TaskThreadInfo associé au thread courant.
479 *
480 * Peut-être nul si le thread courant n'est pas associé à un thread TBB
481 * ou si en dehors d'une exécution d'une tâche ou d'une boucle parallèle.
482 */
483 TaskThreadInfo* currentTaskThreadInfo() const;
484
485 private:
486
487 bool m_is_active = false;
488 Impl* m_p = nullptr;
489
490 private:
491
492 template <int RankValue> void
493 _executeMDParallelFor(const ComplexForLoopRanges<RankValue>& loop_ranges,
494 IMDRangeFunctor<RankValue>* functor,
495 const ForLoopRunInfo& run_info);
496 void _executeParallelFor(const ParallelFor1DLoopInfo& loop_info);
497};
498
499/*---------------------------------------------------------------------------*/
500/*---------------------------------------------------------------------------*/
501
502class TBBTaskImplementation::Impl
503{
504 class TaskObserver
505 : public tbb::task_scheduler_observer
506 {
507 public:
508
509 explicit TaskObserver(TBBTaskImplementation::Impl* p)
510 : tbb::task_scheduler_observer(p->m_main_arena)
511 , m_p(p)
512 {
513 }
514 void on_scheduler_entry(bool is_worker) override
515 {
516 m_p->notifyThreadCreated(is_worker);
517 }
518 void on_scheduler_exit(bool is_worker) override
519 {
520 m_p->notifyThreadDestroyed(is_worker);
521 }
523 };
524
525 public:
526
527 Impl()
528 : m_task_observer(this)
529 , m_thread_task_infos(cache_line_size)
530 {
531 m_nb_allowed_thread = tbb::info::default_concurrency();
532 _init();
533 }
534 Impl(Int32 nb_thread)
535 : m_main_arena(nb_thread)
536 , m_task_observer(this)
537 , m_thread_task_infos(cache_line_size)
538 {
539 m_nb_allowed_thread = nb_thread;
540 _init();
541 }
542
543 public:
544
545 Int32 nbAllowedThread() const { return m_nb_allowed_thread; }
546 TaskThreadInfo* threadTaskInfo(Integer index) { return &m_thread_task_infos[index]; }
547
548 private:
549
550 Int32 m_nb_allowed_thread = 0;
551 //! Compteur pour savoir combien de fois l'observateur a été appelé
552 std::atomic<Int32> m_nb_observed = 0;
553
554 public:
555
556 void terminate()
557 {
558 int v = m_is_terminate_called.fetch_add(1);
559 if (v >= 1)
560 return;
561
562 for (auto x : m_sub_arena_list) {
563 if (x)
564 x->terminate();
565 delete x;
566 }
567 m_sub_arena_list.clear();
568 m_main_arena.terminate();
569 m_task_observer.observe(false);
570 oneapi::tbb::finalize(m_task_scheduler_handle);
571 }
572
573 public:
574
575 void notifyThreadCreated(bool is_worker)
576 {
577 // Si l'observateur a été appelé pour tous les threads autorisés, il
578 // n'y a plus rien à faire.
579 if (m_nb_observed >= m_nb_allowed_thread)
580 return;
581
582 std::thread::id my_thread_id = std::this_thread::get_id();
583
584 // Avec OneTBB, cette méthode est appelée à chaque fois qu'on rentre
585 // dans notre 'task_arena'. Comme il ne faut appeler qu'une seule
586 // fois la méthode de notification on utilise un ensemble pour
587 // conserver la liste des threads déjà créés.
588 // NOTE: On ne peut pas utiliser cette méthode avec la version TBB historique
589 // (2018) car cette méthode 'contains' n'existe pas
590 {
591 std::scoped_lock sl(m_thread_created_mutex);
592 if (m_constructed_thread_map.contains(my_thread_id))
593 return;
594 m_constructed_thread_map.insert(my_thread_id);
595 }
596
597 Int32 nb_observed = ++m_nb_observed;
598
599 {
600 if (TaskFactory::verboseLevel() >= 1) {
601 std::ostringstream ostr;
602 ostr << "TBB: CREATE THREAD"
603 << " nb_allowed=" << m_nb_allowed_thread
604 << " tbb_default_allowed=" << tbb::info::default_concurrency()
605 << " id=" << my_thread_id
606 << " arena_id=" << _currentTaskTreadIndex()
607 << " is_worker=" << is_worker
608 << " nb_observed=" << nb_observed
609 << "\n";
610 std::cout << ostr.str();
611 }
612 TaskFactoryInternal::notifyThreadCreated();
613 }
614 }
615
616 void notifyThreadDestroyed([[maybe_unused]] bool is_worker)
617 {
618 // Avec OneTBB, cette méthode est appelée à chaque fois qu'on sort
619 // de l'arène principale. Du coup elle ne correspond pas vraiment à une
620 // destruction de thread. On ne fait donc rien pour cette notification
621 // TODO: Regarder comment on peut être notifié de la destruction effective
622 // du thread.
623 }
624
625 private:
626
627#if TBB_VERSION_MAJOR > 2021 || (TBB_VERSION_MAJOR == 2021 && TBB_VERSION_MINOR > 5)
628 oneapi::tbb::task_scheduler_handle m_task_scheduler_handle = oneapi::tbb::attach();
629#else
630 oneapi::tbb::task_scheduler_handle m_task_scheduler_handle = tbb::task_scheduler_handle::get();
631#endif
632
633 public:
634
635 tbb::task_arena m_main_arena;
636 //! Tableau dont le i-ème élément contient la tbb::task_arena pour \a i thread.
637 std::vector<tbb::task_arena*> m_sub_arena_list;
638
639 private:
640
641 TaskObserver m_task_observer;
642 std::mutex m_thread_created_mutex;
643 std::vector<TaskThreadInfo> m_thread_task_infos;
644 std::unordered_set<std::thread::id> m_constructed_thread_map;
645 std::atomic<Int32> m_is_terminate_called = 0;
646
647 private:
648
649 void _init()
650 {
651 ConcurrencyBase::_setMaxAllowedThread(m_nb_allowed_thread);
652
653 if (TaskFactory::verboseLevel() >= 1) {
654 std::cout << "TBB: TBBTaskImplementationInit nb_allowed_thread=" << m_nb_allowed_thread
655 << " id=" << std::this_thread::get_id()
656 << " version=" << TBB_VERSION_MAJOR << "." << TBB_VERSION_MINOR
657 << "\n";
658 }
659 m_thread_task_infos.resize(m_nb_allowed_thread);
660 m_task_observer.observe(true);
661 Integer max_arena_size = m_nb_allowed_thread;
662 // Limite artificiellement le nombre de tbb::task_arena
663 // pour éviter d'avoir trop d'objets alloués.
664 if (max_arena_size > 512)
665 max_arena_size = 512;
666 if (max_arena_size < 2)
667 max_arena_size = 2;
668 m_sub_arena_list.resize(max_arena_size);
669 m_sub_arena_list[0] = m_sub_arena_list[1] = nullptr;
670 for (Integer i = 2; i < max_arena_size; ++i)
671 m_sub_arena_list[i] = new tbb::task_arena(i);
672 }
673};
674
675/*---------------------------------------------------------------------------*/
676/*---------------------------------------------------------------------------*/
677
678/*!
679 * \brief Exécuteur pour une boucle 1D.
680 */
681class TBBParallelFor
682{
683 public:
684
685 TBBParallelFor(IRangeFunctor* f, Int32 nb_allowed_thread, ForLoopOneExecStat* stat_info)
686 : m_functor(f)
687 , m_stat_info(stat_info)
688 , m_nb_allowed_thread(nb_allowed_thread)
689 {}
690
691 public:
692
693 void operator()(tbb::blocked_range<Integer>& range) const
694 {
695#ifdef ARCCORE_CHECK
696 if (TaskFactory::verboseLevel() >= 3) {
697 std::ostringstream o;
698 o << "TBB: INDEX=" << TaskFactory::currentTaskThreadIndex()
699 << " id=" << std::this_thread::get_id()
700 << " max_allowed=" << m_nb_allowed_thread
701 << " range_begin=" << range.begin() << " range_size=" << range.size()
702 << "\n";
703 std::cout << o.str();
704 std::cout.flush();
705 }
706
707 int tbb_index = _currentTaskTreadIndex();
708 if (tbb_index < 0 || tbb_index >= m_nb_allowed_thread)
709 ARCCORE_FATAL("Invalid index for thread idx={0} valid_interval=[0..{1}[",
710 tbb_index, m_nb_allowed_thread);
711#endif
712
713 if (m_stat_info)
714 m_stat_info->incrementNbChunk();
715 m_functor->executeFunctor(range.begin(), CheckedConvert::toInteger(range.size()));
716 }
717
718 private:
719
720 IRangeFunctor* m_functor;
721 ForLoopOneExecStat* m_stat_info = nullptr;
722 Int32 m_nb_allowed_thread;
723};
724
725/*---------------------------------------------------------------------------*/
726/*---------------------------------------------------------------------------*/
727
728/*!
729 * \brief Exécuteur pour une boucle multi-dimension.
730 */
731template <int RankValue>
732class TBBMDParallelFor
733{
734 public:
735
736 TBBMDParallelFor(IMDRangeFunctor<RankValue>* f, Int32 nb_allowed_thread, ForLoopOneExecStat* stat_info)
737 : m_functor(f)
738 , m_stat_info(stat_info)
739 , m_nb_allowed_thread(nb_allowed_thread)
740 {}
741
742 public:
743
744 void operator()(blocked_nd_range<Int32, RankValue>& range) const
745 {
746#ifdef ARCCORE_CHECK
747 if (TaskFactory::verboseLevel() >= 3) {
748 std::ostringstream o;
749 o << "TBB: INDEX=" << TaskFactory::currentTaskThreadIndex()
750 << " id=" << std::this_thread::get_id()
751 << " max_allowed=" << m_nb_allowed_thread
752 << " MDFor ";
753 for (Int32 i = 0; i < RankValue; ++i) {
754 auto r0 = static_cast<Int32>(range.dim(i).begin());
755 auto r1 = static_cast<Int32>(range.dim(i).size());
756 o << " range" << i << " (begin=" << r0 << " size=" << r1 << ")";
757 }
758 o << "\n";
759 std::cout << o.str();
760 std::cout.flush();
761 }
762
763 int tbb_index = _currentTaskTreadIndex();
764 if (tbb_index < 0 || tbb_index >= m_nb_allowed_thread)
765 ARCCORE_FATAL("Invalid index for thread idx={0} valid_interval=[0..{1}[",
766 tbb_index, m_nb_allowed_thread);
767#endif
768
769 if (m_stat_info)
770 m_stat_info->incrementNbChunk();
771 m_functor->executeFunctor(_fromTBBRange(range));
772 }
773
774 private:
775
776 IMDRangeFunctor<RankValue>* m_functor = nullptr;
777 ForLoopOneExecStat* m_stat_info = nullptr;
778 Int32 m_nb_allowed_thread;
779};
780
781/*---------------------------------------------------------------------------*/
782/*---------------------------------------------------------------------------*/
783
784/*!
785 * \brief Implémentation déterministe de ParallelFor.
786 *
787 * L'implémentation est déterministe dans le sens où elle ne dépend que
788 * de l'intervalle d'itération (m_begin_index et m_size),
789 * du nombre de threads spécifié (\a m_nb_thread) et de la taille du grain (\a m_grain_size).
790 *
791 * L'algorithme utilisé se rapproche de celui utilisé par OpenMP pour un
792 * parallel for avec l'option statique: on découpe l'intervalle d'itération
793 * en plusieurs blocs et chaque bloc est assigné à une tâche en fonction
794 * d'un algorithme round-robin.
795 * Pour déterminer le nombre de blocs, deux cas sont possibles :
796 * - si \a m_grain_size n'est pas spécifié, on découpe l'intervalle
797 * d'itération en un nombre de blocs équivalent au nombre de threads utilisés.
798 * - si \a m_grain_size est spécifié, le nombre de blocs sera égal
799 * à \a m_size divisé par \a m_grain_size.
800 */
801class TBBDeterministicParallelFor
802{
803 public:
804
805 TBBDeterministicParallelFor(TBBTaskImplementation* impl, const TBBParallelFor& tbb_for,
806 Integer begin_index, Integer size, Integer grain_size, Integer nb_thread)
807 : m_impl(impl)
808 , m_tbb_for(tbb_for)
809 , m_nb_thread(nb_thread)
810 , m_begin_index(begin_index)
811 , m_size(size)
812 , m_grain_size(grain_size)
813 , m_nb_block(0)
814 , m_block_size(0)
815 , m_nb_block_per_thread(0)
816 {
817 if (m_nb_thread < 1)
818 m_nb_thread = 1;
819
820 if (m_grain_size > 0) {
821 m_block_size = m_grain_size;
822 if (m_block_size > 0) {
823 m_nb_block = m_size / m_block_size;
824 if ((m_size % m_block_size) != 0)
825 ++m_nb_block;
826 }
827 else
828 m_nb_block = 1;
829 m_nb_block_per_thread = m_nb_block / m_nb_thread;
830 if ((m_nb_block % m_nb_thread) != 0)
831 ++m_nb_block_per_thread;
832 }
833 else {
834 if (m_nb_block < 1)
835 m_nb_block = m_nb_thread;
836 m_block_size = m_size / m_nb_block;
837 m_nb_block_per_thread = 1;
838 }
839 if (TaskFactory::verboseLevel() >= 2) {
840 std::cout << "TBBDeterministicParallelFor: BEGIN=" << m_begin_index << " size=" << m_size
841 << " grain_size=" << m_grain_size
842 << " nb_block=" << m_nb_block << " nb_thread=" << m_nb_thread
843 << " nb_block_per_thread=" << m_nb_block_per_thread
844 << " block_size=" << m_block_size
845 << " block_size*nb_block=" << m_block_size * m_nb_block << '\n';
846 }
847 }
848
849 public:
850
851 /*!
852 * \brief Opérateur pour un thread donné.
853 *
854 * En règle générale, range.size() vaudra un, car un thread ne traitera qu'une itération,
855 * mais ce n'est a priori pas garanti par les TBB.
856 */
857 void operator()(tbb::blocked_range<Integer>& range) const
858 {
859 auto nb_iter = static_cast<Integer>(range.size());
860 for (Integer i = 0; i < nb_iter; ++i) {
861 Integer task_id = range.begin() + i;
862 for (Integer k = 0, kn = m_nb_block_per_thread; k < kn; ++k) {
863 Integer block_id = task_id + (k * m_nb_thread);
864 if (block_id < m_nb_block)
865 _doBlock(task_id, block_id);
866 }
867 }
868 }
869
870 void _doBlock(Integer task_id, Integer block_id) const
871 {
872 TBBTaskImplementation::TaskInfoLockGuard guard(m_impl->currentTaskThreadInfo(), task_id);
873
874 Integer iter_begin = block_id * m_block_size;
875 Integer iter_size = m_block_size;
876 if ((block_id + 1) == m_nb_block) {
877 // Pour le dernier bloc, la taille est le nombre d'éléments restants
878 iter_size = m_size - iter_begin;
879 }
880 iter_begin += m_begin_index;
881#ifdef ARCCORE_CHECK
882 if (TaskFactory::verboseLevel() >= 3) {
883 std::ostringstream o;
884 o << "TBB: DoBlock: BLOCK task_id=" << task_id << " block_id=" << block_id
885 << " iter_begin=" << iter_begin << " iter_size=" << iter_size << '\n';
886 std::cout << o.str();
887 std::cout.flush();
888 }
889#endif
890 if (iter_size > 0) {
891 auto r = tbb::blocked_range<int>(iter_begin, iter_begin + iter_size);
892 m_tbb_for(r);
893 }
894 }
895
896 private:
897
898 TBBTaskImplementation* m_impl;
899 const TBBParallelFor& m_tbb_for;
900 Integer m_nb_thread;
901 Integer m_begin_index;
902 Integer m_size;
903 Integer m_grain_size;
904 Integer m_nb_block;
905 Integer m_block_size;
906 Integer m_nb_block_per_thread;
907};
908
909/*---------------------------------------------------------------------------*/
910/*---------------------------------------------------------------------------*/
911
913{
914 public:
915
916 ParallelForExecute(TBBTaskImplementation* impl, const ParallelLoopOptions& options,
917 Integer begin, Integer size, IRangeFunctor* f, ForLoopOneExecStat* stat_info)
918 : m_impl(impl)
919 , m_begin(begin)
920 , m_size(size)
921 , m_functor(f)
922 , m_options(options)
923 , m_stat_info(stat_info)
924 {}
925
926 public:
927
928 void operator()() const
929 {
930 Integer nb_thread = m_options.maxThread();
931 TBBParallelFor pf(m_functor, nb_thread, m_stat_info);
932 Integer gsize = m_options.grainSize();
933 tbb::blocked_range<Integer> range(m_begin, m_begin + m_size);
934 if (TaskFactory::verboseLevel() >= 1)
935 std::cout << "TBB: TBBTaskImplementationInit ParallelForExecute begin=" << m_begin
936 << " size=" << m_size << " gsize=" << gsize
937 << " partitioner=" << (int)m_options.partitioner()
938 << " nb_thread=" << nb_thread
939 << " has_stat_info=" << (m_stat_info != nullptr)
940 << '\n';
941
942 if (gsize > 0)
943 range = tbb::blocked_range<Integer>(m_begin, m_begin + m_size, gsize);
944
945 if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Static) {
946 tbb::parallel_for(range, pf, tbb::static_partitioner());
947 }
948 else if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Deterministic) {
949 tbb::blocked_range<Integer> range2(0, nb_thread, 1);
950 TBBDeterministicParallelFor dpf(m_impl, pf, m_begin, m_size, gsize, nb_thread);
951 tbb::parallel_for(range2, dpf);
952 }
953 else
954 tbb::parallel_for(range, pf);
955 }
956
957 private:
958
959 TBBTaskImplementation* m_impl = nullptr;
960 Integer m_begin;
961 Integer m_size;
962 IRangeFunctor* m_functor = nullptr;
963 ParallelLoopOptions m_options;
964 ForLoopOneExecStat* m_stat_info = nullptr;
965};
966
967/*---------------------------------------------------------------------------*/
968/*---------------------------------------------------------------------------*/
969
970template <int RankValue>
971class TBBTaskImplementation::MDParallelForExecute
972{
973 public:
974
975 MDParallelForExecute(TBBTaskImplementation* impl,
976 const ParallelLoopOptions& options,
978 IMDRangeFunctor<RankValue>* f, [[maybe_unused]] ForLoopOneExecStat* stat_info)
979 : m_impl(impl)
980 , m_tbb_range(_toTBBRange(range))
981 , m_functor(f)
982 , m_options(options)
983 , m_stat_info(stat_info)
984 {
985 // On ne peut pas modifier les valeurs d'une instance de tbb::blocked_rangeNd.
986 // Il faut donc en reconstruire une complètement.
987 FixedArray<size_t, RankValue> all_grain_sizes;
988 Int32 gsize = m_options.grainSize();
989 if (gsize > 0) {
990 // Si la taille du grain est différent zéro, il faut la répartir
991 // sur l'ensemble des dimensions. On commence par la dernière.
992 // TODO: regarder pourquoi dans certains cas les performances sont
993 // inférieures à celles qu'on obtient en utilisant un partitionneur
994 // statique.
995 constexpr bool is_verbose = false;
996 std::array<Int32, RankValue> range_extents = range.extents().asStdArray();
997 double ratio = static_cast<double>(gsize) / static_cast<double>(range.nbElement());
998 if constexpr (is_verbose) {
999 std::cout << "GSIZE=" << gsize << " rank=" << RankValue << " ratio=" << ratio;
1000 for (Int32 i = 0; i < RankValue; ++i)
1001 std::cout << " range" << i << "=" << range_extents[i];
1002 std::cout << "\n";
1003 }
1004 Int32 index = RankValue - 1;
1005 Int32 remaining_grain = gsize;
1006 for (; index >= 0; --index) {
1007 Int32 current = range_extents[index];
1008 if constexpr (is_verbose)
1009 std::cout << "Check index=" << index << " remaining=" << remaining_grain << " current=" << current << "\n";
1010 if (remaining_grain > current) {
1011 all_grain_sizes[index] = current;
1012 remaining_grain /= current;
1013 }
1014 else {
1015 all_grain_sizes[index] = remaining_grain;
1016 break;
1017 }
1018 }
1019 for (Int32 i = 0; i < index; ++i)
1020 all_grain_sizes[i] = 1;
1021 if constexpr (is_verbose) {
1022 for (Int32 i = 0; i < RankValue; ++i)
1023 std::cout << " grain" << i << "=" << all_grain_sizes[i];
1024 std::cout << "\n";
1025 }
1026 m_tbb_range = _toTBBRangeWithGrain(m_tbb_range, all_grain_sizes);
1027 }
1028 }
1029
1030 public:
1031
1032 void operator()() const
1033 {
1034 Integer nb_thread = m_options.maxThread();
1035 TBBMDParallelFor<RankValue> pf(m_functor, nb_thread, m_stat_info);
1036
1037 if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Static) {
1038 tbb::parallel_for(m_tbb_range, pf, tbb::static_partitioner());
1039 }
1040 else if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Deterministic) {
1041 // TODO: implémenter le mode déterministe
1042 ARCCORE_THROW(NotImplementedException, "ParallelLoopOptions::Partitioner::Deterministic for multi-dimensionnal loops");
1043 //tbb::blocked_range<Integer> range2(0,nb_thread,1);
1044 //TBBDeterministicParallelFor dpf(m_impl,pf,m_begin,m_size,gsize,nb_thread);
1045 //tbb::parallel_for(range2,dpf);
1046 }
1047 else {
1048 tbb::parallel_for(m_tbb_range, pf);
1049 }
1050 }
1051
1052 private:
1053
1054 TBBTaskImplementation* m_impl = nullptr;
1055 blocked_nd_range<Int32, RankValue> m_tbb_range;
1056 IMDRangeFunctor<RankValue>* m_functor = nullptr;
1057 ParallelLoopOptions m_options;
1058 ForLoopOneExecStat* m_stat_info = nullptr;
1059};
1060
1061/*---------------------------------------------------------------------------*/
1062/*---------------------------------------------------------------------------*/
1063
1064TBBTaskImplementation::
1065~TBBTaskImplementation()
1066{
1067 m_p->terminate();
1068 delete m_p;
1069}
1070
1071/*---------------------------------------------------------------------------*/
1072/*---------------------------------------------------------------------------*/
1073
1074void TBBTaskImplementation::
1075initialize(Int32 nb_thread)
1076{
1077 if (nb_thread < 0)
1078 nb_thread = 0;
1079 m_is_active = (nb_thread != 1);
1080 if (nb_thread != 0)
1081 m_p = new Impl(nb_thread);
1082 else
1083 m_p = new Impl();
1084 ParallelLoopOptions opts = TaskFactory::defaultParallelLoopOptions();
1085 opts.setMaxThread(nbAllowedThread());
1087}
1088
1089/*---------------------------------------------------------------------------*/
1090/*---------------------------------------------------------------------------*/
1091
1092void TBBTaskImplementation::
1093terminate()
1094{
1095 m_p->terminate();
1096}
1097
1098/*---------------------------------------------------------------------------*/
1099/*---------------------------------------------------------------------------*/
1100
1102printInfos(std::ostream& o) const
1103{
1104 o << "OneTBBTaskImplementation"
1105 << " version=" << TBB_VERSION_STRING
1106 << " interface=" << TBB_INTERFACE_VERSION
1107 << " runtime_interface=" << TBB_runtime_interface_version();
1108}
1109
1110/*---------------------------------------------------------------------------*/
1111/*---------------------------------------------------------------------------*/
1112
1113void TBBTaskImplementation::
1114_executeParallelFor(const ParallelFor1DLoopInfo& loop_info)
1115{
1116 ScopedExecInfo sei(loop_info.runInfo());
1117 ForLoopOneExecStat* stat_info = sei.statInfo();
1118 ::Arcane::Impl::ScopedStatLoop scoped_loop(sei.isOwn() ? stat_info : nullptr);
1119
1120 Int32 begin = loop_info.beginIndex();
1121 Int32 size = loop_info.size();
1122 ParallelLoopOptions options = loop_info.runInfo().options().value_or(TaskFactory::defaultParallelLoopOptions());
1123 IRangeFunctor* f = loop_info.functor();
1125
1126 Integer max_thread = options.maxThread();
1127 Integer nb_allowed_thread = m_p->nbAllowedThread();
1128 if (max_thread < 0)
1129 max_thread = nb_allowed_thread;
1130
1131 if (TaskFactory::verboseLevel() >= 1)
1132 std::cout << "TBB: TBBTaskImplementation executeParallelFor begin=" << begin
1133 << " size=" << size << " max_thread=" << max_thread
1134 << " grain_size=" << options.grainSize()
1135 << " nb_allowed=" << nb_allowed_thread << '\n';
1136
1137 // En exécution séquentielle, appelle directement la méthode \a f.
1138 if (max_thread == 1 || max_thread == 0) {
1139 f->executeFunctor(begin, size);
1140 return;
1141 }
1142
1143 // Remplace les valeurs non initialisées de \a options par celles de \a m_default_loop_options
1144 ParallelLoopOptions true_options(options);
1145 true_options.mergeUnsetValues(TaskFactory::defaultParallelLoopOptions());
1146 true_options.setMaxThread(max_thread);
1147
1148 ParallelForExecute pfe(this, true_options, begin, size, f, stat_info);
1149
1150 tbb::task_arena* used_arena = nullptr;
1151 if (max_thread < nb_allowed_thread && max_thread < m_p->m_sub_arena_list.size())
1152 used_arena = m_p->m_sub_arena_list[max_thread];
1153 if (!used_arena)
1154 used_arena = &(m_p->m_main_arena);
1155 used_arena->execute(pfe);
1156}
1157
1158/*---------------------------------------------------------------------------*/
1159/*---------------------------------------------------------------------------*/
1160
1163{
1164 _executeParallelFor(loop_info);
1165}
1166
1167/*---------------------------------------------------------------------------*/
1168/*---------------------------------------------------------------------------*/
1169
1170/*!
1171 * \brief Exécution d'une boucle N-dimensions.
1172 *
1173 * \warning L'implémentation actuelle ne tient pas compte de \a options
1174 * pour les boucles autres que une dimension.
1175 */
1176template <int RankValue> void TBBTaskImplementation::
1177_executeMDParallelFor(const ComplexForLoopRanges<RankValue>& loop_ranges,
1179 const ForLoopRunInfo& run_info)
1180{
1181 ParallelLoopOptions options;
1182 if (run_info.options().has_value())
1183 options = run_info.options().value();
1184
1185 ScopedExecInfo sei(run_info);
1186 ForLoopOneExecStat* stat_info = sei.statInfo();
1187 ::Arcane::Impl::ScopedStatLoop scoped_loop(sei.isOwn() ? stat_info : nullptr);
1188
1189 if (TaskFactory::verboseLevel() >= 1) {
1190 std::cout << "TBB: TBBTaskImplementation executeMDParallelFor nb_dim=" << RankValue
1191 << " nb_element=" << loop_ranges.nbElement()
1192 << " grain_size=" << options.grainSize()
1193 << " name=" << run_info.traceInfo().traceInfo()
1194 << " has_stat_info=" << (stat_info != nullptr)
1195 << '\n';
1196 }
1197
1198 Integer max_thread = options.maxThread();
1199 // En exécution séquentielle, appelle directement la méthode \a f.
1200 if (max_thread == 1 || max_thread == 0) {
1201 functor->executeFunctor(loop_ranges);
1202 return;
1203 }
1204
1205 // Remplace les valeurs non initialisées de \a options par celles de \a m_default_loop_options
1206 ParallelLoopOptions true_options(options);
1207 true_options.mergeUnsetValues(TaskFactory::defaultParallelLoopOptions());
1208
1209 Integer nb_allowed_thread = m_p->nbAllowedThread();
1210 if (max_thread < 0)
1211 max_thread = nb_allowed_thread;
1212 tbb::task_arena* used_arena = nullptr;
1213 if (max_thread < nb_allowed_thread)
1214 used_arena = m_p->m_sub_arena_list[max_thread];
1215 if (!used_arena)
1216 used_arena = &(m_p->m_main_arena);
1217
1218 // Pour l'instant pour la dimension 1, utilise le 'ParallelForExecute' historique
1219 if constexpr (RankValue == 1) {
1220 auto range_1d = _toTBBRange(loop_ranges);
1221 auto x1 = [&](Integer begin, Integer size) {
1222 functor->executeFunctor(makeLoopRanges(ForLoopRange(begin, size)));
1223 //functor->executeFunctor(ComplexForLoopRanges<1>(begin,size));
1224 };
1225 LambdaRangeFunctorT<decltype(x1)> functor_1d(x1);
1226 Integer begin1 = CheckedConvert::toInteger(range_1d.dim(0).begin());
1227 Integer size1 = CheckedConvert::toInteger(range_1d.dim(0).size());
1228 ParallelForExecute pfe(this, true_options, begin1, size1, &functor_1d, stat_info);
1229 used_arena->execute(pfe);
1230 }
1231 else {
1232 MDParallelForExecute<RankValue> pfe(this, true_options, loop_ranges, functor, stat_info);
1233 used_arena->execute(pfe);
1234 }
1235}
1236
1237/*---------------------------------------------------------------------------*/
1238/*---------------------------------------------------------------------------*/
1239
1241executeParallelFor(Integer begin, Integer size, Integer grain_size, IRangeFunctor* f)
1242{
1244 opts.setGrainSize(grain_size);
1245 ForLoopRunInfo run_info(opts);
1246 executeParallelFor(ParallelFor1DLoopInfo(begin, size, f, run_info));
1247}
1248
1249/*---------------------------------------------------------------------------*/
1250/*---------------------------------------------------------------------------*/
1251
1257
1258/*---------------------------------------------------------------------------*/
1259/*---------------------------------------------------------------------------*/
1260
1261TBBTaskImplementation::TaskThreadInfo* TBBTaskImplementation::
1263{
1264 Int32 thread_id = currentTaskThreadIndex();
1265 if (thread_id >= 0)
1266 return m_p->threadTaskInfo(thread_id);
1267 return nullptr;
1268}
1269
1270/*---------------------------------------------------------------------------*/
1271/*---------------------------------------------------------------------------*/
1272
1274currentTaskIndex() const
1275{
1276 Int32 thread_id = currentTaskThreadIndex();
1277 // Ce test avait été ajouté pour coutourner un bug dans une des versions
1278 // de OneTBB. Il est surement inutile aujourd'hui (2025)
1279 if (thread_id < 0 || thread_id >= m_p->nbAllowedThread())
1280 return 0;
1281 TBBTaskImplementation::TaskThreadInfo* tti = currentTaskThreadInfo();
1282 if (tti) {
1283 Int32 task_index = tti->taskIndex();
1284 if (task_index >= 0)
1285 return task_index;
1286 }
1287 return thread_id;
1288}
1289
1290/*---------------------------------------------------------------------------*/
1291/*---------------------------------------------------------------------------*/
1292
1295{
1296 tbb::task_group task_group;
1297 task_group.run(taskFunctor());
1298 task_group.wait();
1299 delete this;
1300}
1301
1302/*---------------------------------------------------------------------------*/
1303/*---------------------------------------------------------------------------*/
1304
1307{
1308 tbb::task_group task_group;
1309 Integer n = tasks.size();
1310 if (n == 0)
1311 return;
1312
1313 //set_ref_count(n+1);
1314 for (Integer i = 0; i < n; ++i) {
1315 auto* t = static_cast<OneTBBTask*>(tasks[i]);
1316 task_group.run(t->taskFunctor());
1317 }
1318 task_group.wait();
1319 for (Integer i = 0; i < n; ++i) {
1320 auto* t = static_cast<OneTBBTask*>(tasks[i]);
1321 delete t;
1322 }
1323}
1324
1325/*---------------------------------------------------------------------------*/
1326/*---------------------------------------------------------------------------*/
1327
1329launch()
1330{
1331 if (m_task_group.isNull()) {
1332 m_task_group = makeRef(new tbb::task_group());
1333 }
1334 m_task_group->run(taskFunctor());
1335}
1336
1337/*---------------------------------------------------------------------------*/
1338/*---------------------------------------------------------------------------*/
1339
1341wait()
1342{
1343 if (m_task_group.isNull()) {
1344 return;
1345 }
1346 m_task_group->wait();
1347 delete this;
1348}
1349
1350/*---------------------------------------------------------------------------*/
1351/*---------------------------------------------------------------------------*/
1352
1353ITask* OneTBBTask::
1354_createChildTask(ITaskFunctor* functor)
1355{
1356 auto* t = new OneTBBTask(functor);
1357 return t;
1358}
1359
1360/*---------------------------------------------------------------------------*/
1361/*---------------------------------------------------------------------------*/
1362
1363ARCANE_DI_REGISTER_PROVIDER(TBBTaskImplementation,
1364 DependencyInjection::ProviderProperty("TBBTaskImplementation"),
1365 ARCANE_DI_INTERFACES(ITaskImplementation),
1366 ARCANE_DI_EMPTY_CONSTRUCTOR());
1367
1368/*---------------------------------------------------------------------------*/
1369/*---------------------------------------------------------------------------*/
1370
1371} // End namespace Arcane
1372
1373/*---------------------------------------------------------------------------*/
1374/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ARCCORE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCCORE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
Représente les limites d'un tableau multidimensionnel.
Vue constante d'un tableau de type T.
constexpr Integer size() const noexcept
Nombre d'éléments du tableau.
Classe pour gérer le profiling d'une seule exécution d'une boucle.
Informations d'exécution d'une boucle.
Interface d'un fonctor sur un interval d'itération multi-dimensionnel de dimension RankValue.
virtual void executeFunctor(const ComplexForLoopRanges< RankValue > &loop_range)=0
Exécute la méthode associée.
Interface d'un fonctor sur un interval d'itération.
virtual void executeFunctor(Int32 begin, Int32 size)=0
Exécute la méthode associée.
virtual void executeFunctor(const TaskContext &tc)=0
Exécute la méthode associé
Int32 nbAllowedThread() const
Nombre de threads utilisés au maximum pour gérer les tâches.
Interface d'une tâche concourante.
Definition Task.h:188
Classe permettant de récupérer le temps passé entre l'appel au constructeur et au destructeur.
void wait() override
Méthode permettant d'attendre la fin de la tâche.
void launchAndWait() override
Lance la tâche et bloque jusqu'à ce qu'elle se termine.
void launch() override
Méthode permettant de lancer la tâche.
Caractéristiques d'un boucle 1D multi-thread.
Definition ParallelFor.h:35
Options d'exécution d'une boucle parallèle en multi-thread.
Integer grainSize() const
Taille d'un intervalle d'itération.
Int32 maxThread() const
Nombre maximal de threads autorisés.
void setGrainSize(Integer v)
Positionne la taille (approximative) d'un intervalle d'itération.
@ Deterministic
Utilise un partitionnement et un ordonnancement statique.
static bool hasProfiling()
Indique si le profilage est actif.
Référence à une instance.
Implémentation déterministe de ParallelFor.
void operator()(tbb::blocked_range< Integer > &range) const
Opérateur pour un thread donné.
Exécuteur pour une boucle 1D.
std::vector< tbb::task_arena * > m_sub_arena_list
Tableau dont le i-ème élément contient la tbb::task_arena pour i thread.
Classe pour positionner TaskThreadInfo::taskIndex().
Int32 currentTaskThreadIndex() const final
Implémentation de TaskFactory::currentTaskThreadIndex().
void executeParallelFor(const ComplexForLoopRanges< 1 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 1 > *functor) final
Exécute une boucle 1D en concurrence.
void executeParallelFor(Int32 begin, Int32 size, IRangeFunctor *f) final
Exécute le fonctor f en concurrence.
ITask * createRootTask(ITaskFunctor *f) override
Créé une tâche racine. L'implémentation doit recopier la valeur de f qui est soit un TaskFunctor,...
void printInfos(std::ostream &o) const final
Affiche les informations sur le runtime utilisé
void executeParallelFor(Int32 begin, Int32 size, const ParallelLoopOptions &options, IRangeFunctor *f) final
Exécute le fonctor f en concurrence.
void executeParallelFor(const ComplexForLoopRanges< 3 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 3 > *functor) final
Exécute une boucle 3D en concurrence.
void executeParallelFor(const ComplexForLoopRanges< 4 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 4 > *functor) final
Exécute une boucle 4D en concurrence.
TaskThreadInfo * currentTaskThreadInfo() const
Instance de TaskThreadInfo associé au thread courant.
bool isActive() const final
Indique si l'implémentation est active.
Int32 currentTaskIndex() const final
Implémentation de TaskFactory::currentTaskIndex().
void executeParallelFor(const ComplexForLoopRanges< 2 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 2 > *functor) final
Exécute une boucle 2D en concurrence.
Contexte d'éxecution d'une tâche.
Definition Task.h:48
static const ParallelLoopOptions & defaultParallelLoopOptions()
Valeurs par défaut d'exécution d'une boucle parallèle.
static Integer verboseLevel()
Niveau de verbosité
static void setDefaultParallelLoopOptions(const ParallelLoopOptions &v)
Positionne les valeurs par défaut d'exécution d'une boucle parallèle.
static Int32 currentTaskThreadIndex()
Indice (entre 0 et nbAllowedThread()-1) du thread exécutant la tâche actuelle.
String getStackTrace()
Retourne une chaîne de caractere contenant la pile d'appel.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type représentant un entier.
SimpleForLoopRanges< 1 > makeLoopRanges(Int32 n1)
Crée une plage d'itération [0,n1[, [0,n2[.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Créé une référence sur un pointeur.
std::int32_t Int32
Type entier signé sur 32 bits.