DepSpawn 1.2
 
Loading...
Searching...
No Matches
depspawn.h
1/*
2 DepSpawn: Data Dependent Spawn library
3 Copyright (C) 2012-2023 Carlos H. Gonzalez, Basilio B. Fraguela. Universidade da Coruna
4
5 Distributed under the MIT License. (See accompanying file LICENSE)
6*/
7
14
15#ifndef __DEPSPAWN_H
16#define __DEPSPAWN_H
17
18#include <functional>
19#include <type_traits>
20#include <utility>
21#include <boost/function_types/function_arity.hpp>
22#include <boost/function_types/parameter_types.hpp>
23#include <boost/function.hpp>
24#include <boost/mpl/begin.hpp>
25#include <boost/mpl/next.hpp>
26#include <boost/mpl/deref.hpp>
27#include <atomic>
28#include "depspawn/fixed_defines.h"
29
30#ifdef BZ_ARRAY_H
31#ifndef DEPSPAWN_BLITZ
32#error Only the Blitz++ version released with DepSpawn can be used with DepSpawn
33#endif
34#endif
35
36#ifdef DEPSPAWN_USE_TBB
37#include <tbb/task.h>
38#endif
39
40// Also included when DEPSPAWN_USE_TBB for the sake of get_task_pool() and TaskPool::Task::run
41#include "depspawn/TaskPool.h"
42
43
44#ifndef DEPSPAWN_SCALABLE_POOL
46#define DEPSPAWN_SCALABLE_POOL false
47#endif
48
49#ifdef WIN32
50#include <windows.h>
51#else
52#include <unistd.h>
53#endif
54
55#ifdef DEPSPAWN_DUMMY_POOL
56#include "depspawn/DummyLinkedListPool.h"
57#else
58#include "depspawn/LinkedListPool.h"
59#endif
60
63namespace depspawn {
64
65
66
68 template<typename T>
69 class Ignore {
70
71 T t_;
72
73 public:
74
76 Ignore(T& t) :
77 t_(t)
78 {}
79
81 Ignore<T> operator= (const T& other) {
82 t_ = other;
83 return *this;
84 }
85
87 operator T&() {
88 return t_;
89 }
90
91 /* Formerly used in ref<Ignore<T>&&>::make(Ignore<T>& t)
92 Now replaced by the conversion operator
93 T& operator*() {
94 return t_;
95 }
96
97 const T& operator*() const {
98 return t_;
99 }
100 */
101
103 void *addr() const { return (void *)&t_; }
104 };
105
107 template<typename T>
108 inline Ignore<T> ignore(T&& t) {
109 return Ignore<T>(std::forward<T>(t));
110 }
111
112
115 namespace internal {
116
119#ifdef DEPSPAWN_DUMMY_POOL
120 template<typename T, bool SCALABLE>
121 using Pool_t = DummyLinkedListPool<T, SCALABLE>;
122#else
123 template<typename T, bool SCALABLE>
124 using Pool_t = LinkedListPool<T, true, SCALABLE>;
125#endif
126
132
133 template<typename MembFuncPtr>
134 struct memb_func_traits
135 {};
136
137 template< typename ClassT, typename R, typename... Arg>
138 struct memb_func_traits<R ClassT::* (Arg...)>
139 {
140 typedef ClassT class_type;
141 typedef R signature (Arg...);
142 };
143
144 template< typename ClassT, typename R, typename... Arg>
145 struct memb_func_traits<R (ClassT::*) (Arg...) const>
146 {
147 typedef ClassT class_type;
148 typedef R signature (Arg...);
149 };
150
151 template<typename F>
152 struct function_type {
153 typedef typename memb_func_traits<decltype(&F::operator())>::signature signature;
154 typedef std::function<signature> type;
155 };
156
157 /* For pointer to member function spawn in Apple clang.
158 We choose ClassT& for the std::function 1st arg */
159 template<typename ClassT, typename R, typename... Arg>
160 struct function_type<R (ClassT::*) (Arg...)> {
161 typedef R signature (ClassT&, Arg...);
162 typedef std::function<signature> type;
163 };
164
165 /* For pointer to member function spawn in Apple clang.
166 We choose ClassT& for the std::function 1st arg */
167 template<typename ClassT, typename R, typename... Arg>
168 struct function_type<R (ClassT::*) (Arg...) const> {
169 typedef R signature (const ClassT&, Arg...);
170 typedef std::function<signature> type;
171 };
172
173 template<typename F>
174 typename function_type<F>::type make_function(F && f)
175 {
176 typedef typename function_type<F>::type result_type;
177 return result_type(std::forward<F>(f));
178 }
180
181
184 template<typename T>
185 struct is_function_object :public std::false_type {};
186
187 template<typename T>
188 struct is_function_object<std::function<T>> : public std::true_type {};
189
190 template<typename T>
191 struct is_function_object<boost::function<T>> : public std::true_type {};
193
194
199
201
202 template <typename T>
203 struct ParameterTypes {
204 using intl_type = typename function_type<T>::signature;
205 using type = typename ParameterTypes<intl_type>::type;
206 };
207
208 template <typename T>
209 struct ParameterTypes<T&> {
210 using type = typename ParameterTypes<T>::type;
211 };
212
213 template<typename R, typename... Types>
214 struct ParameterTypes<R(Types...)> {
215 using type = boost::function_types::parameter_types<R(Types...)>;
216 };
217
218 template<typename Function>
219 struct ParameterTypes<std::function<Function>> {
220 using type = boost::function_types::parameter_types<Function>;
221 };
222
223 template<typename Function>
224 struct ParameterTypes<boost::function<Function>> {
225 using type = boost::function_types::parameter_types<Function>;
226 };
227
228 template<typename R, typename... Types>
229 struct ParameterTypes<R (&) (Types...)> {
230 using type = boost::function_types::parameter_types<R(Types...)>;
231 };
232
233 template <typename R, typename C, typename... Types>
234 struct ParameterTypes<R (C::*)(Types...)> {
235 using type = boost::function_types::parameter_types<R(C&, Types...)>;
236 };
237
238 template <typename R, typename C, typename... Types>
239 struct ParameterTypes<R (C::*)(Types...) const> {
240 using type = boost::function_types::parameter_types<R(C&, Types...)>;
241 };
242
244
246 typedef size_t value_t;
247
249 typedef std::pair<int, int> array_range_t;
250
252 template<typename T>
253 struct is_blitz_array : public std::false_type {};
254
256 struct arg_info {
257 value_t addr;
258 size_t size;
259 array_range_t* array_range;
260 arg_info* next;
261 bool wr;
262 char rank;
263
265 void insert_in_arglist(arg_info*& args);
266
268 void solve_overlap(arg_info *other);
269
272 bool overlap_array(const arg_info *other) const;
273
276 bool is_contained_array(const arg_info *other) const;
277
279 bool is_array() const { return rank != 0; }
280
281#ifdef DEPSPAWN_BLITZ
283 template<typename T>
284 inline typename std::enable_if< is_blitz_array<T>::value >::type fill_in_array(const T& a) {
285 addr = (value_t)a.block();
286 const auto& storage = a.storage();
287 rank = storage.originalDims();
288 array_range = new array_range_t[rank];
289 const blitz::TinyVector<int, 2> * const extent = storage.extent();
290 for(int i = 0; i < rank; i++) {
291 array_range[i] = std::make_pair(extent[i][0], extent[i][1]);
292 }
293 }
294#endif
295
297 template<typename T>
298 inline typename std::enable_if< ! is_blitz_array<T>::value >::type fill_in_array(const T& a) {
299 addr = (value_t)&a;
300 rank = 0;
301 array_range = nullptr;
302 }
303
305 static Pool_t<arg_info, DEPSPAWN_SCALABLE_POOL> Pool;
306
307 };
308
312
313 template<typename T>
314 struct ref {
315 static inline auto make(T& t) -> decltype(std::ref(t)) {
316 return std::ref(t);
317 }
318 };
319
320 template<typename T>
321 struct ref<T&&> {
322 static inline T& make(T& t) {
323 return t;
324 }
325 };
326
328 template<typename T>
329 struct ref<Ignore<T>&&> {
330 static inline auto make(Ignore<T>& t) -> decltype(std::ref((T&)t)) {
331 return std::ref((T&)t);
332 }
333 };
334
335#ifdef DEPSPAWN_BLITZ
336 template<typename type, int dim>
337 struct ref<blitz::Array<type, dim>&&> {
338 static inline blitz::Array<type, dim> make(blitz::Array<type, dim>& __t) {
339 return __t;
340 }
341 };
342
343 template<typename type, int dim>
344 struct ref<blitz::Array<type, dim>&> {
345 static inline blitz::Array<type, dim> make(blitz::Array<type, dim>& __t) {
346 return __t;
347 }
348 };
349
350 template<typename type, int dim>
351 struct ref<const blitz::Array<type, dim>&> {
352 static inline const blitz::Array<type, dim> make(const blitz::Array<type, dim>& __t) {
353 return __t;
354 }
355 };
356
357 template<typename type, int dim>
358 struct ref<const blitz::Array<type, dim>> {
359 static inline const blitz::Array<type, dim> make(const blitz::Array<type, dim>& __t) {
360 return __t;
361 }
362 };
363#endif // DEPSPAWN_BLITZ
364
366
368
369 template<typename T>
370 struct is_ignored {
371 static const bool value = false;
372 };
373
374 template<typename T>
375 struct is_ignored<Ignore<T>&&> {
376 static const bool value = true;
377 };
378
380
381#ifdef DEPSPAWN_USE_TBB
383 extern tbb::task* volatile master_task;
384
385 struct AbstractBoxedFunction;
386 struct AbstractRunner;
387#else
388 extern TaskPool * volatile TP;
389 using AbstractRunner = TaskPool::Task;
390#endif
391
393 extern bool EnqueueTasks;
394
395 } //namespace internal
396
397} //namespace depspawn
398
399
400#include "depspawn/workitem.h"
401
402namespace depspawn {
403
404 namespace internal {
405
407 extern thread_local Workitem * enum_thr_spec_father;
408
410 extern void start_master();
411
413 extern void common_wait_for(arg_info *pargs, int nargs);
414
415#ifdef DEPSPAWN_USE_TBB
416
418 struct AbstractBoxedFunction {
419
420 Workitem* ctx_;
421
424 AbstractBoxedFunction(Workitem* ctx)
425 : ctx_(ctx)
426 {}
427
428 void run_in_env(bool from_wait);
429
430 virtual ~AbstractBoxedFunction() {}
431
432 protected:
433
435 virtual void run() = 0;
436 };
437
439 template<typename Function>
440 struct BoxedFunction : public AbstractBoxedFunction {
441
442 Function f_;
443
447 BoxedFunction(Workitem* ctx, Function&& f)
448 : AbstractBoxedFunction(ctx), f_(std::move(f))
449 {}
450
451 virtual ~BoxedFunction() {}
452
453 protected:
454
456 void run() final {
457 f_();
458 }
459
460 };
461
463 struct AbstractRunner : public tbb::task {
464
465 std::atomic<Workitem *> ctx_;
466
469 AbstractRunner(Workitem * ctx)
470 : ctx_(ctx)
471 { }
472
474 virtual AbstractBoxedFunction * steal(Workitem *ctx) = 0;
475
476 virtual ~AbstractRunner() {}
477 };
478
480 template<typename Function>
481 struct runner : public AbstractRunner {
482
483 Function f_;
484
489 template<typename InputFunction, typename... Args>
490 runner(Workitem* ctx, const InputFunction& f, Args&&... args)
491 : AbstractRunner(ctx), f_(std::bind(f, internal::ref<Args&&>::make(args)...))
492 { }
493
495 AbstractBoxedFunction * steal(Workitem *ctx) final {
496 //Danger: ctx_ could have been already nullified by execute()
497 //so the stealer sends it just in case
498 return new BoxedFunction<Function>(ctx, std::move(f_));
499 }
500
502 tbb::task* execute()
503 {
504 Workitem * const ctx_copy = ctx_.exchange(nullptr);
505
506 if (ctx_copy != nullptr) {
507
508 if (!ctx_copy->guard_.fetch_add(1)) {
509
510 ctx_copy->status = Workitem::Status_t::Running;
511
512 Workitem *& ref_father_lcl = enum_thr_spec_father;
513 ref_father_lcl = ctx_copy;
514
515 f_();
516
517 //BBF: in case father thread runs a task.
518 //Should be farther checked
519 ref_father_lcl = nullptr;
520
521 ctx_copy->finish_execution();
522
523 } else {
524 while (ctx_copy->guard_ < 3) { } //Wait for new BoxedFunction to be built
525 ctx_copy->guard_ = 4; // Notify we are leaving
526 }
527
528 }
529
530 //set_ref_count(1);
531
532 return nullptr;
533 }
534
535 virtual ~runner() {}
536
537 };
538#endif
539
541 template<typename T_it>
542 int fill_args(arg_info*&) { return 0; }
543
545 template<typename T_it, typename Head, typename... Tail>
546 int fill_args(arg_info*& args, Head&& h, Tail&&... t) {
547 typedef typename boost::mpl::deref<T_it>::type curr_t; //Formal parameter type
548 constexpr bool is_reference = std::is_reference<curr_t>::value;
549 typedef typename std::remove_reference<curr_t>::type deref_t;
550 constexpr bool is_const = std::is_const<deref_t>::value;
551 constexpr bool is_writable = is_reference && !is_const;
552 constexpr bool is_barray = is_blitz_array<typename std::remove_const<typename std::remove_reference<Head>::type>::type>::value;
553 constexpr int process_argument =
554 !is_ignored<curr_t&&>::value &&
555 !is_ignored<Head&&>::value &&
556 !(std::is_rvalue_reference<Head&&>::value && !is_barray);
557
558 if(process_argument) {
559
560 arg_info* n = arg_info::Pool.malloc();
561 n->size = sizeof(Head);
562 n->wr = is_writable;
563 n->next = nullptr;
564 n->fill_in_array(h);
565
566 // Insert new_arg (n) in order
567 n->insert_in_arglist(args);
568 }
569
570 return process_argument + fill_args<typename boost::mpl::next<T_it>::type>(args, std::forward<Tail>(t)...);
571 }
572
574typedef void spawn_ret_t;
575
576 } //namespace internal
577
583extern void set_task_queue_limit(int limit) noexcept;
584
587extern int get_task_queue_limit() noexcept;
588
598extern void set_threads(int nthreads = -1);
599
601extern int get_num_threads() noexcept;
602
605extern internal::TaskPool& get_task_pool();
606
607#ifdef SEQUENTIAL_DEPSPAWN
608
609#define spawn(f, ...) f(__VA_ARGS__)
610
611#else
612
613 template<typename Function, typename... Args>
614 inline internal::spawn_ret_t spawn(const Function& f, Args&&... args) {
615 using parameter_types = typename internal::ParameterTypes<Function>::type;
616
617 internal::arg_info *pargs = nullptr;
618 const int nargs = internal::fill_args<typename boost::mpl::begin<parameter_types>::type>(pargs, std::forward<Args>(args)...);
619
620#ifdef DEPSPAWN_USE_TBB
621 if(!internal::master_task)
622 internal::start_master();
623
624 internal::Workitem* w = internal::Workitem::Pool.malloc(pargs, nargs);
625 w->insert_in_worklist(new (tbb::task::allocate_additional_child_of(*internal::master_task)) internal::runner<decltype(std::bind(f, internal::ref<Args&&>::make(args)...))>(w, f, std::forward<Args>(args)...));
626#else
627 if(!internal::TP || !internal::TP->is_running())
628 internal::start_master();
629
630 internal::Workitem* w = internal::Workitem::Pool.malloc(pargs, nargs);
631 w->insert_in_worklist(internal::TP->build_task(w, f, internal::ref<Args&&>::make(args)...));
632#endif
633 }
634
636 template<typename T, typename... Args>
637 typename std::enable_if< std::is_reference<T>::value &&
638 ! std::is_member_function_pointer<typename std::remove_reference<T>::type>::value &&
639 ! std::is_function<typename std::remove_reference<T>::type>::value &&
640 ! internal::is_function_object<typename std::remove_reference<T>::type>::value,
641 internal::spawn_ret_t >::type
642 spawn(T&& functor, Args&&... args) {
643 using base_type = typename std::remove_reference<T>::type;
644 return spawn(& base_type::operator(), std::forward<T>(functor), std::forward<Args>(args)...);
645 }
646
647
648#endif // SEQUENTIAL_DEPSPAWN
649
652
657 void wait_for_subtasks(bool priority = true);
658
664 template<typename... Args>
665 void wait_for(const Args&... args) {
666
667#ifdef DEPSPAWN_USE_TBB
668 if(!internal::master_task) //There should be nothing to wait for
669#else
670 if(!internal::TP || !internal::TP->is_running()) //There should be nothing to wait for
671#endif
672 return;
673
674 typedef void Function(const Args&... args);
675 //std::function<Function> f = [](const Args&... args) { std::cout << "EXEC!\n"; };
676 typedef boost::function_types::parameter_types<Function> parameter_types;
677 internal::arg_info *pargs = nullptr;
678 const int nargs = internal::fill_args<typename boost::mpl::begin<parameter_types>::type>(pargs, args...);
679
680 internal::common_wait_for(pargs, nargs);
681 }
682
693 class Observer {
694
696 internal::Workitem * const limit_;
697
699 internal::Workitem * const cur_father_;
700
702 const bool priority_;
703
709 Observer(bool priority, internal::Workitem *w);
710
711 friend void wait_for_subtasks(bool priority);
712
713 public:
714
720 Observer(bool priority = true);
721
722 ~Observer();
723
724 };
725
726/* This helps expand __LINE__ so that each Observer has a different variable name,
727 although this does not happen if we put two depspawn_sync in the same line.
728 But this does not matter because they would be necessaritly nested, so actuallly
729 all the depspawn_sync could have exactly the same name. This looks just nicer. */
730#define depspawnBottomTOKENPASTE(x, y) x ## y
731#define depspawnTOKENPASTE(x, y) depspawnBottomTOKENPASTE(x, y)
732
734#define depspawn_sync(...) { depspawn::Observer depspawnTOKENPASTE(__depspawn_temporary, __LINE__);\
735 {__VA_ARGS__;} }
736
737#ifdef DEPSPAWN_BLITZ
738
743 template<typename F>
744 void for_range(const blitz::Range& r, const F& f, int bs = -1) {
745 const int num_threads = get_num_threads();
746
747 if(bs == -1) bs = (r.length() + num_threads - 1) / (num_threads);
748 int lbs;
749 int i;
750 for(i = r.first(); i < r.last(); i+=bs) {
751 lbs = r.last() -i +1 <= bs ? r.last() -i +1 : bs;
752 f(blitz::Range(i, i+lbs-1));
753 }
754 }
755
762 template<typename F>
763 void for_range(const blitz::Range& r1, const blitz::Range& r2, const F& f, int bs1 = -1, int bs2 = -1) {
764 const int num_threads = get_num_threads();
765
766 if(bs1 == -1) {
767 bs1 = (r1.length() + num_threads - 1) / (num_threads);
768 }
769 if(bs2 == -1) {
770 bs2 = (r2.length() + num_threads - 1) / (num_threads);
771 }
772 int lbs1, lbs2;
773 int i, j;
774 for(i = r1.first(); i < r1.last(); i+=bs1) {
775 lbs1 = r1.last() -i +1 <= bs1 ? r1.last() -i +1 : bs1;
776 for(j = r2.first(); j < r2.last(); j+=bs2) {
777 lbs2 = r2.last() -j +1 <= bs2 ? r2.last() -j +1 : bs2;
778 f(blitz::Range(i, i+lbs1-1), blitz::Range(j, j+lbs2-1));
779 }
780 }
781 }
782
783#endif // DEPSPAWN_BLITZ
784
785} //namespace depspawn
786
787
788#ifdef DEPSPAWN_BLITZ
789
790#define DEFINE_NEW_ARRAY_TYPE(type, dim) \
791namespace depspawn { \
792 namespace internal { \
793 template<> \
794 struct is_blitz_array<blitz::Array<type, dim>> : public std::true_type {}; \
795 }; \
796};
797
798DEFINE_NEW_ARRAY_TYPE(int, 1);
799DEFINE_NEW_ARRAY_TYPE(int, 2);
800DEFINE_NEW_ARRAY_TYPE(int, 3);
801DEFINE_NEW_ARRAY_TYPE(int, 4);
802DEFINE_NEW_ARRAY_TYPE(unsigned int, 1);
803DEFINE_NEW_ARRAY_TYPE(unsigned int, 2);
804DEFINE_NEW_ARRAY_TYPE(unsigned int, 3);
805DEFINE_NEW_ARRAY_TYPE(unsigned int, 4);
806DEFINE_NEW_ARRAY_TYPE(unsigned long long, 1);
807DEFINE_NEW_ARRAY_TYPE(unsigned long long, 2);
808DEFINE_NEW_ARRAY_TYPE(unsigned long long, 3);
809DEFINE_NEW_ARRAY_TYPE(unsigned long long, 4);
810DEFINE_NEW_ARRAY_TYPE(long long, 1);
811DEFINE_NEW_ARRAY_TYPE(long long, 2);
812DEFINE_NEW_ARRAY_TYPE(long long, 3);
813DEFINE_NEW_ARRAY_TYPE(long long, 4);
814DEFINE_NEW_ARRAY_TYPE(float, 1);
815DEFINE_NEW_ARRAY_TYPE(float, 2);
816DEFINE_NEW_ARRAY_TYPE(float, 3);
817DEFINE_NEW_ARRAY_TYPE(float, 4);
818DEFINE_NEW_ARRAY_TYPE(double, 1);
819DEFINE_NEW_ARRAY_TYPE(double, 2);
820DEFINE_NEW_ARRAY_TYPE(double, 3);
821DEFINE_NEW_ARRAY_TYPE(double, 4);
822DEFINE_NEW_ARRAY_TYPE(long double, 1);
823DEFINE_NEW_ARRAY_TYPE(long double, 2);
824DEFINE_NEW_ARRAY_TYPE(long double, 3);
825DEFINE_NEW_ARRAY_TYPE(long double, 4);
826#endif // DEPSPAWN_BLITZ
827
828
829#endif // __DEPSPAWN_H
Class for ignoring arguments when doing dependency analysis.
Definition: depspawn.h:69
Ignore(T &t)
Constructor.
Definition: depspawn.h:76
Ignore< T > operator=(const T &other)
Assignment.
Definition: depspawn.h:81
void * addr() const
For debugging.
Definition: depspawn.h:103
When destroyed, it makes sure that all the tasks spawned by the current thread since its creation hav...
Definition: depspawn.h:693
Observer(bool priority=true)
friend void wait_for_subtasks(bool priority)
Public library API.
Definition: main.dox:1
internal::TaskPool & get_task_pool()
Ignore< T > ignore(T &&t)
Traits for arguments or parameters that should be ignored.
Definition: depspawn.h:108
int get_num_threads() noexcept
Retrieve number of threads currently in use.
void wait_for_all()
Waits for all tasks to finish.
void wait_for(const Args &... args)
Wait for a specific group of variables to be written.
Definition: depspawn.h:665
void set_task_queue_limit(int limit) noexcept
void wait_for_subtasks(bool priority=true)
void set_threads(int nthreads=-1)
int get_task_queue_limit() noexcept