Arcane  4.2.2.0
Developer documentation
Loading...
Searching...
No Matches
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/* Implementation of tasks using 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// This macro must be defined for the class 'blocked_rangeNd' to be available
40
41#define TBB_PREVIEW_BLOCKED_RANGE_ND 1
42
43// The macro 'ARCCORE_USE_ONETBB' is defined in CMakeLists.txt
44// if compiling with the OneTBB version 2021+
45// (https://github.com/oneapi-src/oneTBB.git)
46// Eventually, this will be the only version supported by Arcane.
47
48// Necessary to access 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: use a specific memory pool to manage the
66// OneTBBTask to optimize the new/delete of instances of this class.
67// Previously, with older versions of TBB, this was managed with
68// the method '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// The class "blocked_rangeNd" was removed in version
76// 2022.0.0 and replaced by "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 // Positive if execution statistics are retrieved
94 bool isStatActive()
95 {
97 }
98
103 class ScopedExecInfo
104 {
105 public:
106
107 explicit ScopedExecInfo(const ForLoopRunInfo& run_info)
108 : m_run_info(run_info)
109 {
110 // If run_info.execInfo() is not null, we use it.
111 // This means that the caller will manage the execution statistics
112 // execution statistics. Otherwise, we use m_stat_info if execution statistics
113 // are requested.
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;
152 bool m_use_own_run_info = true;
153 };
154
155 /*---------------------------------------------------------------------------*/
156 /*---------------------------------------------------------------------------*/
157
158 inline int _currentTaskTreadIndex()
159 {
160 // NOTE: With OneTBB 2021, the value is no longer '0' if this method is called
161 // from a thread outside of a task_arena. With version 2021,
162 // the value is 65535.
163 // NOTE: It seems this is a bug in 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 * Do not use the local observer on the task_arena.
346 * Use the global observer on the scheduler.
347 * For the ID, use tbb::this_task_arena::current_thread_index().
348 */
349class TBBTaskImplementation
350: public ITaskImplementation
351{
352 class Impl;
353 class ParallelForExecute;
354 template <int RankValue>
356
357 public:
358
359 // For performance reasons, aligns to a cache line
360 // and uses 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
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
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;
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 // If we have called the observer for all the allowed thread there
578 // is nothing left to do.
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 // With OneTBB, this method is called every time we enter
585 // our 'task_arena'. Since the notification method should only be called once,
586 // we use a set to keep track of the threads already created.
587 // NOTE: This method cannot be used with the historical TBB version
588 // (2018) because this 'contains' method does not exist
589 {
590 std::scoped_lock sl(m_thread_created_mutex);
591 if (m_constructed_thread_map.contains(my_thread_id))
592 return;
593 m_constructed_thread_map.insert(my_thread_id);
594 }
595
596 Int32 nb_observed = ++m_nb_observed;
597
598 {
599 if (TaskFactory::verboseLevel() >= 1) {
600 std::ostringstream ostr;
601 ostr << "TBB: CREATE THREAD"
602 << " nb_allowed=" << m_nb_allowed_thread
603 << " tbb_default_allowed=" << tbb::info::default_concurrency()
604 << " id=" << my_thread_id
605 << " arena_id=" << _currentTaskTreadIndex()
606 << " is_worker=" << is_worker
607 << " nb_observed=" << nb_observed
608 << "\n";
609 std::cout << ostr.str();
610 }
612 }
613 }
614
615 void notifyThreadDestroyed([[maybe_unused]] bool is_worker)
616 {
617 // With OneTBB, this method is called every time we exit
618 // the main arena. Therefore, it does not truly correspond to a
619 // thread destruction. So we do nothing for this notification.
620 // TODO: Look into how we can be notified of the actual thread destruction.
621 }
622
623 private:
624
625#if TBB_VERSION_MAJOR > 2021 || (TBB_VERSION_MAJOR == 2021 && TBB_VERSION_MINOR > 5)
626 oneapi::tbb::task_scheduler_handle m_task_scheduler_handle = oneapi::tbb::attach();
627#else
628 oneapi::tbb::task_scheduler_handle m_task_scheduler_handle = tbb::task_scheduler_handle::get();
629#endif
630
631 public:
632
633 tbb::task_arena m_main_arena;
635 std::vector<tbb::task_arena*> m_sub_arena_list;
636
637 private:
638
639 TaskObserver m_task_observer;
640 std::mutex m_thread_created_mutex;
641 std::vector<TaskThreadInfo> m_thread_task_infos;
642 std::unordered_set<std::thread::id> m_constructed_thread_map;
643 std::atomic<Int32> m_is_terminate_called = 0;
644
645 private:
646
647 void _init()
648 {
649 ConcurrencyBase::_setMaxAllowedThread(m_nb_allowed_thread);
650
651 if (TaskFactory::verboseLevel() >= 1) {
652 std::cout << "TBB: TBBTaskImplementationInit nb_allowed_thread=" << m_nb_allowed_thread
653 << " id=" << std::this_thread::get_id()
654 << " version=" << TBB_VERSION_MAJOR << "." << TBB_VERSION_MINOR
655 << "\n";
656 }
657 m_thread_task_infos.resize(m_nb_allowed_thread);
658 m_task_observer.observe(true);
659 Integer max_arena_size = m_nb_allowed_thread;
660 // Artificially limit the number of tbb::task_arena
661 // to avoid having too many allocated objects.
662 if (max_arena_size > 512)
663 max_arena_size = 512;
664 if (max_arena_size < 2)
665 max_arena_size = 2;
666 m_sub_arena_list.resize(max_arena_size);
667 m_sub_arena_list[0] = m_sub_arena_list[1] = nullptr;
668 for (Integer i = 2; i < max_arena_size; ++i)
669 m_sub_arena_list[i] = new tbb::task_arena(i);
670 }
671};
672
673/*---------------------------------------------------------------------------*/
674/*---------------------------------------------------------------------------*/
675
679class TBBParallelFor
680{
681 public:
682
683 TBBParallelFor(IRangeFunctor* f, Int32 nb_allowed_thread, ForLoopOneExecStat* stat_info)
684 : m_functor(f)
685 , m_stat_info(stat_info)
686 , m_nb_allowed_thread(nb_allowed_thread)
687 {}
688
689 public:
690
691 void operator()(tbb::blocked_range<Integer>& range) const
692 {
693#ifdef ARCCORE_CHECK
694 if (TaskFactory::verboseLevel() >= 3) {
695 std::ostringstream o;
696 o << "TBB: INDEX=" << TaskFactory::currentTaskThreadIndex()
697 << " id=" << std::this_thread::get_id()
698 << " max_allowed=" << m_nb_allowed_thread
699 << " range_begin=" << range.begin() << " range_size=" << range.size()
700 << "\n";
701 std::cout << o.str();
702 std::cout.flush();
703 }
704
705 int tbb_index = _currentTaskTreadIndex();
706 if (tbb_index < 0 || tbb_index >= m_nb_allowed_thread)
707 ARCCORE_FATAL("Invalid index for thread idx={0} valid_interval=[0..{1}[",
708 tbb_index, m_nb_allowed_thread);
709#endif
710
711 if (m_stat_info)
712 m_stat_info->incrementNbChunk();
713 m_functor->executeFunctor(range.begin(), CheckedConvert::toInteger(range.size()));
714 }
715
716 private:
717
718 IRangeFunctor* m_functor;
719 ForLoopOneExecStat* m_stat_info = nullptr;
720 Int32 m_nb_allowed_thread;
721};
722
723/*---------------------------------------------------------------------------*/
724/*---------------------------------------------------------------------------*/
725
729template <int RankValue>
730class TBBMDParallelFor
731{
732 public:
733
734 TBBMDParallelFor(IMDRangeFunctor<RankValue>* f, Int32 nb_allowed_thread, ForLoopOneExecStat* stat_info)
735 : m_functor(f)
736 , m_stat_info(stat_info)
737 , m_nb_allowed_thread(nb_allowed_thread)
738 {}
739
740 public:
741
742 void operator()(blocked_nd_range<Int32, RankValue>& range) const
743 {
744#ifdef ARCCORE_CHECK
745 if (TaskFactory::verboseLevel() >= 3) {
746 std::ostringstream o;
747 o << "TBB: INDEX=" << TaskFactory::currentTaskThreadIndex()
748 << " id=" << std::this_thread::get_id()
749 << " max_allowed=" << m_nb_allowed_thread
750 << " MDFor ";
751 for (Int32 i = 0; i < RankValue; ++i) {
752 auto r0 = static_cast<Int32>(range.dim(i).begin());
753 auto r1 = static_cast<Int32>(range.dim(i).size());
754 o << " range" << i << " (begin=" << r0 << " size=" << r1 << ")";
755 }
756 o << "\n";
757 std::cout << o.str();
758 std::cout.flush();
759 }
760
761 int tbb_index = _currentTaskTreadIndex();
762 if (tbb_index < 0 || tbb_index >= m_nb_allowed_thread)
763 ARCCORE_FATAL("Invalid index for thread idx={0} valid_interval=[0..{1}[",
764 tbb_index, m_nb_allowed_thread);
765#endif
766
767 if (m_stat_info)
768 m_stat_info->incrementNbChunk();
769 m_functor->executeFunctor(_fromTBBRange(range));
770 }
771
772 private:
773
774 IMDRangeFunctor<RankValue>* m_functor = nullptr;
775 ForLoopOneExecStat* m_stat_info = nullptr;
776 Int32 m_nb_allowed_thread;
777};
778
779/*---------------------------------------------------------------------------*/
780/*---------------------------------------------------------------------------*/
781
800class TBBDeterministicParallelFor
801{
802 public:
803
804 TBBDeterministicParallelFor(TBBTaskImplementation* impl, const TBBParallelFor& tbb_for,
805 Integer begin_index, Integer size, Integer grain_size, Integer nb_thread)
806 : m_impl(impl)
807 , m_tbb_for(tbb_for)
808 , m_nb_thread(nb_thread)
809 , m_begin_index(begin_index)
810 , m_size(size)
811 , m_grain_size(grain_size)
812 , m_nb_block(0)
813 , m_block_size(0)
814 , m_nb_block_per_thread(0)
815 {
816 if (m_nb_thread < 1)
817 m_nb_thread = 1;
818
819 if (m_grain_size > 0) {
820 m_block_size = m_grain_size;
821 if (m_block_size > 0) {
822 m_nb_block = m_size / m_block_size;
823 if ((m_size % m_block_size) != 0)
824 ++m_nb_block;
825 }
826 else
827 m_nb_block = 1;
828 m_nb_block_per_thread = m_nb_block / m_nb_thread;
829 if ((m_nb_block % m_nb_thread) != 0)
830 ++m_nb_block_per_thread;
831 }
832 else {
833 if (m_nb_block < 1)
834 m_nb_block = m_nb_thread;
835 m_block_size = m_size / m_nb_block;
836 m_nb_block_per_thread = 1;
837 }
838 if (TaskFactory::verboseLevel() >= 2) {
839 std::cout << "TBBDeterministicParallelFor: BEGIN=" << m_begin_index << " size=" << m_size
840 << " grain_size=" << m_grain_size
841 << " nb_block=" << m_nb_block << " nb_thread=" << m_nb_thread
842 << " nb_block_per_thread=" << m_nb_block_per_thread
843 << " block_size=" << m_block_size
844 << " block_size*nb_block=" << m_block_size * m_nb_block << '\n';
845 }
846 }
847
848 public:
849
856 void operator()(tbb::blocked_range<Integer>& range) const
857 {
858 auto nb_iter = static_cast<Integer>(range.size());
859 for (Integer i = 0; i < nb_iter; ++i) {
860 Integer task_id = range.begin() + i;
861 for (Integer k = 0, kn = m_nb_block_per_thread; k < kn; ++k) {
862 Integer block_id = task_id + (k * m_nb_thread);
863 if (block_id < m_nb_block)
864 _doBlock(task_id, block_id);
865 }
866 }
867 }
868
869 void _doBlock(Integer task_id, Integer block_id) const
870 {
871 TBBTaskImplementation::TaskInfoLockGuard guard(m_impl->currentTaskThreadInfo(), task_id);
872
873 Integer iter_begin = block_id * m_block_size;
874 Integer iter_size = m_block_size;
875 if ((block_id + 1) == m_nb_block) {
876 // For the last block, the size is the number of remaining elements
877 iter_size = m_size - iter_begin;
878 }
879 iter_begin += m_begin_index;
880#ifdef ARCCORE_CHECK
881 if (TaskFactory::verboseLevel() >= 3) {
882 std::ostringstream o;
883 o << "TBB: DoBlock: BLOCK task_id=" << task_id << " block_id=" << block_id
884 << " iter_begin=" << iter_begin << " iter_size=" << iter_size << '\n';
885 std::cout << o.str();
886 std::cout.flush();
887 }
888#endif
889 if (iter_size > 0) {
890 auto r = tbb::blocked_range<int>(iter_begin, iter_begin + iter_size);
891 m_tbb_for(r);
892 }
893 }
894
895 private:
896
897 TBBTaskImplementation* m_impl;
898 const TBBParallelFor& m_tbb_for;
899 Integer m_nb_thread;
900 Integer m_begin_index;
901 Integer m_size;
902 Integer m_grain_size;
903 Integer m_nb_block;
904 Integer m_block_size;
905 Integer m_nb_block_per_thread;
906};
907
908/*---------------------------------------------------------------------------*/
909/*---------------------------------------------------------------------------*/
910
912{
913 public:
914
915 ParallelForExecute(TBBTaskImplementation* impl, const ParallelLoopOptions& options,
916 Integer begin, Integer size, IRangeFunctor* f, ForLoopOneExecStat* stat_info)
917 : m_impl(impl)
918 , m_begin(begin)
919 , m_size(size)
920 , m_functor(f)
921 , m_options(options)
922 , m_stat_info(stat_info)
923 {}
924
925 public:
926
927 void operator()() const
928 {
929 Integer nb_thread = m_options.maxThread();
930 TBBParallelFor pf(m_functor, nb_thread, m_stat_info);
931 Integer gsize = m_options.grainSize();
932 tbb::blocked_range<Integer> range(m_begin, m_begin + m_size);
933 if (TaskFactory::verboseLevel() >= 1)
934 std::cout << "TBB: TBBTaskImplementationInit ParallelForExecute begin=" << m_begin
935 << " size=" << m_size << " gsize=" << gsize
936 << " partitioner=" << (int)m_options.partitioner()
937 << " nb_thread=" << nb_thread
938 << " has_stat_info=" << (m_stat_info != nullptr)
939 << '\n';
940
941 if (gsize > 0)
942 range = tbb::blocked_range<Integer>(m_begin, m_begin + m_size, gsize);
943
944 if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Static) {
945 tbb::parallel_for(range, pf, tbb::static_partitioner());
946 }
947 else if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Deterministic) {
948 tbb::blocked_range<Integer> range2(0, nb_thread, 1);
949 TBBDeterministicParallelFor dpf(m_impl, pf, m_begin, m_size, gsize, nb_thread);
950 tbb::parallel_for(range2, dpf);
951 }
952 else
953 tbb::parallel_for(range, pf);
954 }
955
956 private:
957
958 TBBTaskImplementation* m_impl = nullptr;
959 Integer m_begin;
960 Integer m_size;
961 IRangeFunctor* m_functor = nullptr;
962 ParallelLoopOptions m_options;
963 ForLoopOneExecStat* m_stat_info = nullptr;
964};
965
966/*---------------------------------------------------------------------------*/
967/*---------------------------------------------------------------------------*/
968
969template <int RankValue>
971{
972 public:
973
974 MDParallelForExecute(TBBTaskImplementation* impl,
975 const ParallelLoopOptions& options,
977 IMDRangeFunctor<RankValue>* f, [[maybe_unused]] ForLoopOneExecStat* stat_info)
978 : m_impl(impl)
979 , m_tbb_range(_toTBBRange(range))
980 , m_functor(f)
981 , m_options(options)
982 , m_stat_info(stat_info)
983 {
984 // We cannot modify the values of a tbb::blocked_rangeNd instance.
985 // We must therefore reconstruct it completely.
986 FixedArray<size_t, RankValue> all_grain_sizes;
987 Int32 gsize = m_options.grainSize();
988 if (gsize > 0) {
989 // If the grain size is not zero, it must be distributed
990 // across all dimensions. We start with the last one.
991 // TODO: check why performance is sometimes
992 // lower than what we get using a static partitioner.
993 constexpr bool is_verbose = false;
994 std::array<Int32, RankValue> range_extents = range.extents().asStdArray();
995 double ratio = static_cast<double>(gsize) / static_cast<double>(range.nbElement());
996 if constexpr (is_verbose) {
997 std::cout << "GSIZE=" << gsize << " rank=" << RankValue << " ratio=" << ratio;
998 for (Int32 i = 0; i < RankValue; ++i)
999 std::cout << " range" << i << "=" << range_extents[i];
1000 std::cout << "\n";
1001 }
1002 Int32 index = RankValue - 1;
1003 Int32 remaining_grain = gsize;
1004 for (; index >= 0; --index) {
1005 Int32 current = range_extents[index];
1006 if constexpr (is_verbose)
1007 std::cout << "Check index=" << index << " remaining=" << remaining_grain << " current=" << current << "\n";
1008 if (remaining_grain > current) {
1009 all_grain_sizes[index] = current;
1010 remaining_grain /= current;
1011 }
1012 else {
1013 all_grain_sizes[index] = remaining_grain;
1014 break;
1015 }
1016 }
1017 for (Int32 i = 0; i < index; ++i)
1018 all_grain_sizes[i] = 1;
1019 if constexpr (is_verbose) {
1020 for (Int32 i = 0; i < RankValue; ++i)
1021 std::cout << " grain" << i << "=" << all_grain_sizes[i];
1022 std::cout << "\n";
1023 }
1024 m_tbb_range = _toTBBRangeWithGrain(m_tbb_range, all_grain_sizes);
1025 }
1026 }
1027
1028 public:
1029
1030 void operator()() const
1031 {
1032 Integer nb_thread = m_options.maxThread();
1033 TBBMDParallelFor<RankValue> pf(m_functor, nb_thread, m_stat_info);
1034
1035 if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Static) {
1036 tbb::parallel_for(m_tbb_range, pf, tbb::static_partitioner());
1037 }
1038 else if (m_options.partitioner() == ParallelLoopOptions::Partitioner::Deterministic) {
1039 // TODO: implement deterministic mode
1040 ARCCORE_THROW(NotImplementedException, "ParallelLoopOptions::Partitioner::Deterministic for multi-dimensionnal loops");
1041 //tbb::blocked_range<Integer> range2(0,nb_thread,1);
1042 //TBBDeterministicParallelFor dpf(m_impl,pf,m_begin,m_size,gsize,nb_thread);
1043 //tbb::parallel_for(range2,dpf);
1044 }
1045 else {
1046 tbb::parallel_for(m_tbb_range, pf);
1047 }
1048 }
1049
1050 private:
1051
1052 TBBTaskImplementation* m_impl = nullptr;
1053 blocked_nd_range<Int32, RankValue> m_tbb_range;
1054 IMDRangeFunctor<RankValue>* m_functor = nullptr;
1055 ParallelLoopOptions m_options;
1056 ForLoopOneExecStat* m_stat_info = nullptr;
1057};
1058
1059/*---------------------------------------------------------------------------*/
1060/*---------------------------------------------------------------------------*/
1061
1062TBBTaskImplementation::
1063~TBBTaskImplementation()
1064{
1065 m_p->terminate();
1066 delete m_p;
1067}
1068
1069/*---------------------------------------------------------------------------*/
1070/*---------------------------------------------------------------------------*/
1071
1073initialize(Int32 nb_thread)
1074{
1075 if (nb_thread < 0)
1076 nb_thread = 0;
1077 m_is_active = (nb_thread != 1);
1078 if (nb_thread != 0)
1079 m_p = new Impl(nb_thread);
1080 else
1081 m_p = new Impl();
1085}
1086
1087/*---------------------------------------------------------------------------*/
1088/*---------------------------------------------------------------------------*/
1089
1091terminate()
1092{
1093 m_p->terminate();
1094}
1095
1096/*---------------------------------------------------------------------------*/
1097/*---------------------------------------------------------------------------*/
1098
1100printInfos(std::ostream& o) const
1101{
1102 o << "OneTBBTaskImplementation"
1103 << " version=" << TBB_VERSION_STRING
1104 << " interface=" << TBB_INTERFACE_VERSION
1105 << " runtime_interface=" << TBB_runtime_interface_version();
1106}
1107
1108/*---------------------------------------------------------------------------*/
1109/*---------------------------------------------------------------------------*/
1110
1111void TBBTaskImplementation::
1112_executeParallelFor(const ParallelFor1DLoopInfo& loop_info)
1113{
1114 ScopedExecInfo sei(loop_info.runInfo());
1115 ForLoopOneExecStat* stat_info = sei.statInfo();
1116 ::Arcane::Impl::ScopedStatLoop scoped_loop(sei.isOwn() ? stat_info : nullptr);
1117
1118 Int32 begin = loop_info.beginIndex();
1119 Int32 size = loop_info.size();
1120 ParallelLoopOptions options = loop_info.runInfo().options().value_or(TaskFactory::defaultParallelLoopOptions());
1121 IRangeFunctor* f = loop_info.functor();
1123
1124 Integer max_thread = options.maxThread();
1125 Integer nb_allowed_thread = m_p->nbAllowedThread();
1126 if (max_thread < 0)
1127 max_thread = nb_allowed_thread;
1128
1129 if (TaskFactory::verboseLevel() >= 1)
1130 std::cout << "TBB: TBBTaskImplementation executeParallelFor begin=" << begin
1131 << " size=" << size << " max_thread=" << max_thread
1132 << " grain_size=" << options.grainSize()
1133 << " nb_allowed=" << nb_allowed_thread << '\n';
1134
1135 // In sequential execution, call the method \a f directly.
1136 if (max_thread == 1 || max_thread == 0) {
1137 f->executeFunctor(begin, size);
1138 return;
1139 }
1140
1141 // Replace the uninitialized values of \a options with those of \a m_default_loop_options
1142 ParallelLoopOptions true_options(options);
1143 true_options.mergeUnsetValues(TaskFactory::defaultParallelLoopOptions());
1144 true_options.setMaxThread(max_thread);
1145
1146 ParallelForExecute pfe(this, true_options, begin, size, f, stat_info);
1147
1148 tbb::task_arena* used_arena = nullptr;
1149 if (max_thread < nb_allowed_thread && max_thread < m_p->m_sub_arena_list.size())
1150 used_arena = m_p->m_sub_arena_list[max_thread];
1151 if (!used_arena)
1152 used_arena = &(m_p->m_main_arena);
1153 used_arena->execute(pfe);
1154}
1155
1156/*---------------------------------------------------------------------------*/
1157/*---------------------------------------------------------------------------*/
1158
1161{
1162 _executeParallelFor(loop_info);
1163}
1164
1165/*---------------------------------------------------------------------------*/
1166/*---------------------------------------------------------------------------*/
1167
1174template <int RankValue> void TBBTaskImplementation::
1177 const ForLoopRunInfo& run_info)
1178{
1179 ParallelLoopOptions options;
1180 if (run_info.options().has_value())
1181 options = run_info.options().value();
1182
1183 ScopedExecInfo sei(run_info);
1184 ForLoopOneExecStat* stat_info = sei.statInfo();
1185 ::Arcane::Impl::ScopedStatLoop scoped_loop(sei.isOwn() ? stat_info : nullptr);
1186
1187 if (TaskFactory::verboseLevel() >= 1) {
1188 std::cout << "TBB: TBBTaskImplementation executeMDParallelFor nb_dim=" << RankValue
1189 << " nb_element=" << loop_ranges.nbElement()
1190 << " grain_size=" << options.grainSize()
1191 << " name=" << run_info.traceInfo().traceInfo()
1192 << " has_stat_info=" << (stat_info != nullptr)
1193 << '\n';
1194 }
1195
1196 Integer max_thread = options.maxThread();
1197 // In sequential execution, call the method \a f directly.
1198 if (max_thread == 1 || max_thread == 0) {
1199 functor->executeFunctor(loop_ranges);
1200 return;
1201 }
1202
1203 // Replace the uninitialized values of \a options with those of \a m_default_loop_options
1204 ParallelLoopOptions true_options(options);
1206
1207 Integer nb_allowed_thread = m_p->nbAllowedThread();
1208 if (max_thread < 0)
1209 max_thread = nb_allowed_thread;
1210 tbb::task_arena* used_arena = nullptr;
1211 if (max_thread < nb_allowed_thread)
1212 used_arena = m_p->m_sub_arena_list[max_thread];
1213 if (!used_arena)
1214 used_arena = &(m_p->m_main_arena);
1215
1216 // For now for dimension 1, use the historical 'ParallelForExecute'
1217 if constexpr (RankValue == 1) {
1218 auto range_1d = _toTBBRange(loop_ranges);
1219 auto x1 = [&](Integer begin, Integer size) {
1220 functor->executeFunctor(makeLoopRanges(ForLoopRange(begin, size)));
1221 //functor->executeFunctor(ComplexForLoopRanges<1>(begin,size));
1222 };
1223 LambdaRangeFunctorT<decltype(x1)> functor_1d(x1);
1224 Integer begin1 = CheckedConvert::toInteger(range_1d.dim(0).begin());
1225 Integer size1 = CheckedConvert::toInteger(range_1d.dim(0).size());
1226 ParallelForExecute pfe(this, true_options, begin1, size1, &functor_1d, stat_info);
1227 used_arena->execute(pfe);
1228 }
1229 else {
1230 MDParallelForExecute<RankValue> pfe(this, true_options, loop_ranges, functor, stat_info);
1231 used_arena->execute(pfe);
1232 }
1233}
1234
1235/*---------------------------------------------------------------------------*/
1236/*---------------------------------------------------------------------------*/
1237
1239executeParallelFor(Integer begin, Integer size, Integer grain_size, IRangeFunctor* f)
1240{
1242 opts.setGrainSize(grain_size);
1243 ForLoopRunInfo run_info(opts);
1244 executeParallelFor(ParallelFor1DLoopInfo(begin, size, f, run_info));
1245}
1246
1247/*---------------------------------------------------------------------------*/
1248/*---------------------------------------------------------------------------*/
1249
1255
1256/*---------------------------------------------------------------------------*/
1257/*---------------------------------------------------------------------------*/
1258
1259TBBTaskImplementation::TaskThreadInfo* TBBTaskImplementation::
1261{
1262 Int32 thread_id = currentTaskThreadIndex();
1263 if (thread_id >= 0)
1264 return m_p->threadTaskInfo(thread_id);
1265 return nullptr;
1266}
1267
1268/*---------------------------------------------------------------------------*/
1269/*---------------------------------------------------------------------------*/
1270
1272currentTaskIndex() const
1273{
1274 Int32 thread_id = currentTaskThreadIndex();
1275 // This test was added to bypass a bug in one of the versions
1276 // of OneTBB. It is probably useless today (2025)
1277 if (thread_id < 0 || thread_id >= m_p->nbAllowedThread())
1278 return 0;
1279 TBBTaskImplementation::TaskThreadInfo* tti = currentTaskThreadInfo();
1280 if (tti) {
1281 Int32 task_index = tti->taskIndex();
1282 if (task_index >= 0)
1283 return task_index;
1284 }
1285 return thread_id;
1286}
1287
1288/*---------------------------------------------------------------------------*/
1289/*---------------------------------------------------------------------------*/
1290
1293{
1294 tbb::task_group task_group;
1295 task_group.run(taskFunctor());
1296 task_group.wait();
1297 delete this;
1298}
1299
1300/*---------------------------------------------------------------------------*/
1301/*---------------------------------------------------------------------------*/
1302
1305{
1306 tbb::task_group task_group;
1307 Integer n = tasks.size();
1308 if (n == 0)
1309 return;
1310
1311 //set_ref_count(n+1);
1312 for (Integer i = 0; i < n; ++i) {
1313 auto* t = static_cast<OneTBBTask*>(tasks[i]);
1314 task_group.run(t->taskFunctor());
1315 }
1316 task_group.wait();
1317 for (Integer i = 0; i < n; ++i) {
1318 auto* t = static_cast<OneTBBTask*>(tasks[i]);
1319 delete t;
1320 }
1321}
1322
1323/*---------------------------------------------------------------------------*/
1324/*---------------------------------------------------------------------------*/
1325
1327launch()
1328{
1329 if (m_task_group.isNull()) {
1330 m_task_group = makeRef(new tbb::task_group());
1331 }
1332 m_task_group->run(taskFunctor());
1333}
1334
1335/*---------------------------------------------------------------------------*/
1336/*---------------------------------------------------------------------------*/
1337
1339wait()
1340{
1341 if (m_task_group.isNull()) {
1342 return;
1343 }
1344 m_task_group->wait();
1345 delete this;
1346}
1347
1348/*---------------------------------------------------------------------------*/
1349/*---------------------------------------------------------------------------*/
1350
1351ITask* OneTBBTask::
1352_createChildTask(ITaskFunctor* functor)
1353{
1354 auto* t = new OneTBBTask(functor);
1355 return t;
1356}
1357
1358/*---------------------------------------------------------------------------*/
1359/*---------------------------------------------------------------------------*/
1360
1361ARCANE_DI_REGISTER_PROVIDER(TBBTaskImplementation,
1362 DependencyInjection::ProviderProperty("TBBTaskImplementation"),
1363 ARCANE_DI_INTERFACES(ITaskImplementation),
1364 ARCANE_DI_EMPTY_CONSTRUCTOR());
1365
1366/*---------------------------------------------------------------------------*/
1367/*---------------------------------------------------------------------------*/
1368
1369} // End namespace Arcane
1370
1371/*---------------------------------------------------------------------------*/
1372/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
#define ARCCORE_THROW(exception_class,...)
Macro to throw an exception with formatting.
#define ARCCORE_CHECK_POINTER(ptr)
Macro that returns the pointer ptr if it is not null or throws an exception if it is null.
Represents the bounds of a multidimensional array.
static void _setMaxAllowedThread(Int32 v)
Sets the maximum number of threads to use.
Constant view of an array of type T.
constexpr Integer size() const noexcept
Number of elements in the array.
Class to manage the profiling of a single loop execution.
Loop execution information.
Interface of a functor on a multi-dimensional iteration interval of dimension RankValue.
Interface of a functor on an iteration interval.
virtual void executeFunctor(Int32 begin, Int32 size)=0
Executes the associated method.
Interface for a task functor.
Definition Task.h:76
virtual void executeFunctor(const TaskContext &tc)=0
Executes the associated method.
Implementation of a task factory.
Int32 nbAllowedThread() const
Maximum number of threads used to manage tasks.
Interface for a concurrent task.
Definition Task.h:194
Class allowing retrieval of the time spent between the constructor call and the destructor call.
Functor over an iteration interval instantiated via a lambda function.
void wait() override
Method allowing to wait the end of the task.
void launchAndWait() override
Launches the task and blocks until it finishes.
void launch() override
Method allowing to launch the task.
Characteristics of a multi-thread 1D loop.
Definition ParallelFor.h:35
Execution options for a parallel loop in multi-threading.
Integer grainSize() const
Size of an iteration interval.
void mergeUnsetValues(const ParallelLoopOptions &po)
Merges the unmodified values of the instance with those of po.
Int32 maxThread() const
Maximum number of allowed threads.
void setGrainSize(Integer v)
Sets the size (approximate) of an iteration interval.
void setMaxThread(Integer v)
Sets the maximum number of allowed threads.
static Arcane::Impl::ForLoopStatInfoList * _threadLocalForLoopInstance()
Definition Profiling.cc:273
static bool hasProfiling()
Indicates if profiling is active.
Reference to an instance.
Deterministic implementation of ParallelFor.
void operator()(tbb::blocked_range< Integer > &range) const
Operator for a given thread.
Executor for a multi-dimensional loop.
Executor for a 1D loop.
std::atomic< Int32 > m_nb_observed
Counter to know how many times the observer has been called.
std::vector< tbb::task_arena * > m_sub_arena_list
Array whose i-th element contains the tbb::task_arena for i thread.
Class for positioning TaskThreadInfo::taskIndex().
Int32 currentTaskThreadIndex() const final
Implementation of TaskFactory::currentTaskThreadIndex().
void initialize(Int32 nb_thread) override
void executeParallelFor(const ComplexForLoopRanges< 1 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 1 > *functor) final
Executes a 1D loop in parallel.
void executeParallelFor(Int32 begin, Int32 size, IRangeFunctor *f) final
Executes the functor f in parallel.
ITask * createRootTask(ITaskFunctor *f) override
Creates a root task. The implementation must copy the value of f, which is either a TaskFunctor or a ...
void printInfos(std::ostream &o) const final
Prints information about the runtime used.
void executeParallelFor(Int32 begin, Int32 size, const ParallelLoopOptions &options, IRangeFunctor *f) final
Executes the functor f in parallel.
void executeParallelFor(const ComplexForLoopRanges< 3 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 3 > *functor) final
Executes a 3D loop in parallel.
void executeParallelFor(const ComplexForLoopRanges< 4 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 4 > *functor) final
Executes a 4D loop in parallel.
TaskThreadInfo * currentTaskThreadInfo() const
Instance of TaskThreadInfo associated with the current thread.
bool isActive() const final
Indicates if the implementation is active.
void _executeMDParallelFor(const ComplexForLoopRanges< RankValue > &loop_ranges, IMDRangeFunctor< RankValue > *functor, const ForLoopRunInfo &run_info)
Execution of an N-dimensional loop.
Int32 currentTaskIndex() const final
Implementation of TaskFactory::currentTaskIndex().
void executeParallelFor(const ComplexForLoopRanges< 2 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 2 > *functor) final
Executes a 2D loop in parallel.
Execution context of a task.
Definition Task.h:50
static void notifyThreadCreated()
Notifies all observers of thread creation.
static const ParallelLoopOptions & defaultParallelLoopOptions()
Default parallel loop execution options.
static Integer verboseLevel()
Verbosity level.
static void setDefaultParallelLoopOptions(const ParallelLoopOptions &v)
Sets the default parallel loop execution options.
static Int32 currentTaskThreadIndex()
Index (between 0 and nbAllowedThread()-1) of the thread executing the current task.
String getStackTrace()
Returns a string containing the call stack.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
SimpleForLoopRanges< 1 > makeLoopRanges(Int32 n1)
Creates an iteration range [0,n1[, [0,n2[.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
std::int32_t Int32
Signed integer type of 32 bits.