parallel_reduce.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Copyright 2009-2021 Intel Corporation
  2. // SPDX-License-Identifier: Apache-2.0
  3. #pragma once
  4. #include "parallel_for.h"
  5. namespace embree
  6. {
  7. template<typename Index, typename Value, typename Func, typename Reduction>
  8. __forceinline Value sequential_reduce( const Index first, const Index last, const Value& identity, const Func& func, const Reduction& reduction )
  9. {
  10. return func(range<Index>(first,last));
  11. }
  12. template<typename Index, typename Value, typename Func, typename Reduction>
  13. __forceinline Value sequential_reduce( const Index first, const Index last, const Index minStepSize, const Value& identity, const Func& func, const Reduction& reduction )
  14. {
  15. return func(range<Index>(first,last));
  16. }
  17. template<typename Index, typename Value, typename Func, typename Reduction>
  18. __noinline Value parallel_reduce_internal( Index taskCount, const Index first, const Index last, const Index minStepSize, const Value& identity, const Func& func, const Reduction& reduction )
  19. {
  20. const Index maxTasks = 512;
  21. const Index threadCount = (Index) TaskScheduler::threadCount();
  22. taskCount = min(taskCount,threadCount,maxTasks);
  23. /* parallel invocation of all tasks */
  24. dynamic_large_stack_array(Value,values,taskCount,8192); // consumes at most 8192 bytes on the stack
  25. parallel_for(taskCount, [&](const Index taskIndex) {
  26. const Index k0 = first+(taskIndex+0)*(last-first)/taskCount;
  27. const Index k1 = first+(taskIndex+1)*(last-first)/taskCount;
  28. values[taskIndex] = func(range<Index>(k0,k1));
  29. });
  30. /* perform reduction over all tasks */
  31. Value v = identity;
  32. for (Index i=0; i<taskCount; i++) v = reduction(v,values[i]);
  33. return v;
  34. }
  35. template<typename Index, typename Value, typename Func, typename Reduction>
  36. __forceinline Value parallel_reduce( const Index first, const Index last, const Index minStepSize, const Value& identity, const Func& func, const Reduction& reduction )
  37. {
  38. #if defined(TASKING_INTERNAL)
  39. /* fast path for small number of iterations */
  40. Index taskCount = (last-first+minStepSize-1)/minStepSize;
  41. if (likely(taskCount == 1)) {
  42. return func(range<Index>(first,last));
  43. }
  44. return parallel_reduce_internal(taskCount,first,last,minStepSize,identity,func,reduction);
  45. #elif defined(TASKING_TBB)
  46. #if TBB_INTERFACE_VERSION >= 12002
  47. tbb::task_group_context context;
  48. const Value v = tbb::parallel_reduce(tbb::blocked_range<Index>(first,last,minStepSize),identity,
  49. [&](const tbb::blocked_range<Index>& r, const Value& start) { return reduction(start,func(range<Index>(r.begin(),r.end()))); },
  50. reduction,context);
  51. // -- GODOT start --
  52. // if (context.is_group_execution_cancelled())
  53. // throw std::runtime_error("task cancelled");
  54. // -- GODOT end --
  55. return v;
  56. #else
  57. const Value v = tbb::parallel_reduce(tbb::blocked_range<Index>(first,last,minStepSize),identity,
  58. [&](const tbb::blocked_range<Index>& r, const Value& start) { return reduction(start,func(range<Index>(r.begin(),r.end()))); },
  59. reduction);
  60. // -- GODOT start --
  61. // if (tbb::task::self().is_cancelled())
  62. // throw std::runtime_error("task cancelled");
  63. // -- GODOT end --
  64. return v;
  65. #endif
  66. #else // TASKING_PPL
  67. struct AlignedValue
  68. {
  69. char storage[__alignof(Value)+sizeof(Value)];
  70. static uintptr_t alignUp(uintptr_t p, size_t a) { return p + (~(p - 1) % a); };
  71. Value* getValuePtr() { return reinterpret_cast<Value*>(alignUp(uintptr_t(storage), __alignof(Value))); }
  72. const Value* getValuePtr() const { return reinterpret_cast<Value*>(alignUp(uintptr_t(storage), __alignof(Value))); }
  73. AlignedValue(const Value& v) { new(getValuePtr()) Value(v); }
  74. AlignedValue(const AlignedValue& v) { new(getValuePtr()) Value(*v.getValuePtr()); }
  75. AlignedValue(const AlignedValue&& v) { new(getValuePtr()) Value(*v.getValuePtr()); };
  76. AlignedValue& operator = (const AlignedValue& v) { *getValuePtr() = *v.getValuePtr(); return *this; };
  77. AlignedValue& operator = (const AlignedValue&& v) { *getValuePtr() = *v.getValuePtr(); return *this; };
  78. operator Value() const { return *getValuePtr(); }
  79. };
  80. struct Iterator_Index
  81. {
  82. Index v;
  83. typedef std::forward_iterator_tag iterator_category;
  84. typedef AlignedValue value_type;
  85. typedef Index difference_type;
  86. typedef Index distance_type;
  87. typedef AlignedValue* pointer;
  88. typedef AlignedValue& reference;
  89. __forceinline Iterator_Index() {}
  90. __forceinline Iterator_Index(Index v) : v(v) {}
  91. __forceinline bool operator== (Iterator_Index other) { return v == other.v; }
  92. __forceinline bool operator!= (Iterator_Index other) { return v != other.v; }
  93. __forceinline Iterator_Index operator++() { return Iterator_Index(++v); }
  94. __forceinline Iterator_Index operator++(int) { return Iterator_Index(v++); }
  95. };
  96. auto range_reduction = [&](Iterator_Index begin, Iterator_Index end, const AlignedValue& start) {
  97. assert(begin.v < end.v);
  98. return reduction(start, func(range<Index>(begin.v, end.v)));
  99. };
  100. const Value v = concurrency::parallel_reduce(Iterator_Index(first), Iterator_Index(last), AlignedValue(identity), range_reduction, reduction);
  101. return v;
  102. #endif
  103. }
  104. template<typename Index, typename Value, typename Func, typename Reduction>
  105. __forceinline Value parallel_reduce( const Index first, const Index last, const Index minStepSize, const Index parallel_threshold, const Value& identity, const Func& func, const Reduction& reduction )
  106. {
  107. if (likely(last-first < parallel_threshold)) {
  108. return func(range<Index>(first,last));
  109. } else {
  110. return parallel_reduce(first,last,minStepSize,identity,func,reduction);
  111. }
  112. }
  113. template<typename Index, typename Value, typename Func, typename Reduction>
  114. __forceinline Value parallel_reduce( const range<Index> range, const Index minStepSize, const Index parallel_threshold, const Value& identity, const Func& func, const Reduction& reduction )
  115. {
  116. return parallel_reduce(range.begin(),range.end(),minStepSize,parallel_threshold,identity,func,reduction);
  117. }
  118. template<typename Index, typename Value, typename Func, typename Reduction>
  119. __forceinline Value parallel_reduce( const Index first, const Index last, const Value& identity, const Func& func, const Reduction& reduction )
  120. {
  121. auto funcr = [&] ( const range<Index> r ) {
  122. Value v = identity;
  123. for (Index i=r.begin(); i<r.end(); i++)
  124. v = reduction(v,func(i));
  125. return v;
  126. };
  127. return parallel_reduce(first,last,Index(1),identity,funcr,reduction);
  128. }
  129. template<typename Index, typename Value, typename Func, typename Reduction>
  130. __forceinline Value parallel_reduce( const range<Index> range, const Value& identity, const Func& func, const Reduction& reduction )
  131. {
  132. return parallel_reduce(range.begin(),range.end(),Index(1),identity,func,reduction);
  133. }
  134. }