parallel_reduce.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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) && !defined(TASKING_TBB)
  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. //if (context.is_group_execution_cancelled())
  52. // throw std::runtime_error("task cancelled");
  53. return v;
  54. #else
  55. const Value v = tbb::parallel_reduce(tbb::blocked_range<Index>(first,last,minStepSize),identity,
  56. [&](const tbb::blocked_range<Index>& r, const Value& start) { return reduction(start,func(range<Index>(r.begin(),r.end()))); },
  57. reduction);
  58. //if (tbb::task::self().is_cancelled())
  59. // throw std::runtime_error("task cancelled");
  60. return v;
  61. #endif
  62. #else // TASKING_PPL
  63. struct AlignedValue
  64. {
  65. char storage[__alignof(Value)+sizeof(Value)];
  66. static uintptr_t alignUp(uintptr_t p, size_t a) { return p + (~(p - 1) % a); };
  67. Value* getValuePtr() { return reinterpret_cast<Value*>(alignUp(uintptr_t(storage), __alignof(Value))); }
  68. const Value* getValuePtr() const { return reinterpret_cast<Value*>(alignUp(uintptr_t(storage), __alignof(Value))); }
  69. AlignedValue(const Value& v) { new(getValuePtr()) Value(v); }
  70. AlignedValue(const AlignedValue& v) { new(getValuePtr()) Value(*v.getValuePtr()); }
  71. AlignedValue(const AlignedValue&& v) { new(getValuePtr()) Value(*v.getValuePtr()); };
  72. AlignedValue& operator = (const AlignedValue& v) { *getValuePtr() = *v.getValuePtr(); return *this; };
  73. AlignedValue& operator = (const AlignedValue&& v) { *getValuePtr() = *v.getValuePtr(); return *this; };
  74. operator Value() const { return *getValuePtr(); }
  75. };
  76. struct Iterator_Index
  77. {
  78. Index v;
  79. typedef std::forward_iterator_tag iterator_category;
  80. typedef AlignedValue value_type;
  81. typedef Index difference_type;
  82. typedef Index distance_type;
  83. typedef AlignedValue* pointer;
  84. typedef AlignedValue& reference;
  85. __forceinline Iterator_Index() {}
  86. __forceinline Iterator_Index(Index v) : v(v) {}
  87. __forceinline bool operator== (Iterator_Index other) { return v == other.v; }
  88. __forceinline bool operator!= (Iterator_Index other) { return v != other.v; }
  89. __forceinline Iterator_Index operator++() { return Iterator_Index(++v); }
  90. __forceinline Iterator_Index operator++(int) { return Iterator_Index(v++); }
  91. };
  92. auto range_reduction = [&](Iterator_Index begin, Iterator_Index end, const AlignedValue& start) {
  93. assert(begin.v < end.v);
  94. return reduction(start, func(range<Index>(begin.v, end.v)));
  95. };
  96. const Value v = concurrency::parallel_reduce(Iterator_Index(first), Iterator_Index(last), AlignedValue(identity), range_reduction, reduction);
  97. return v;
  98. #endif
  99. }
  100. template<typename Index, typename Value, typename Func, typename Reduction>
  101. __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 )
  102. {
  103. if (likely(last-first < parallel_threshold)) {
  104. return func(range<Index>(first,last));
  105. } else {
  106. return parallel_reduce(first,last,minStepSize,identity,func,reduction);
  107. }
  108. }
  109. template<typename Index, typename Value, typename Func, typename Reduction>
  110. __forceinline Value parallel_reduce( const range<Index> range, const Index minStepSize, const Index parallel_threshold, const Value& identity, const Func& func, const Reduction& reduction )
  111. {
  112. return parallel_reduce(range.begin(),range.end(),minStepSize,parallel_threshold,identity,func,reduction);
  113. }
  114. template<typename Index, typename Value, typename Func, typename Reduction>
  115. __forceinline Value parallel_reduce( const Index first, const Index last, const Value& identity, const Func& func, const Reduction& reduction )
  116. {
  117. auto funcr = [&] ( const range<Index> r ) {
  118. Value v = identity;
  119. for (Index i=r.begin(); i<r.end(); i++)
  120. v = reduction(v,func(i));
  121. return v;
  122. };
  123. return parallel_reduce(first,last,Index(1),identity,funcr,reduction);
  124. }
  125. template<typename Index, typename Value, typename Func, typename Reduction>
  126. __forceinline Value parallel_reduce( const range<Index> range, const Value& identity, const Func& func, const Reduction& reduction )
  127. {
  128. return parallel_reduce(range.begin(),range.end(),Index(1),identity,func,reduction);
  129. }
  130. }