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