/source/blender/blenlib/intern/task_range.cc

https://github.com/blender/blender · C++ · 168 lines · 114 code · 23 blank · 31 comment · 17 complexity · 35bdb899bb1076bd8b60704610c1bcd5 MD5 · raw file

  1. /*
  2. * This program is free software; you can redistribute it and/or
  3. * modify it under the terms of the GNU General Public License
  4. * as published by the Free Software Foundation; either version 2
  5. * of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program; if not, write to the Free Software Foundation,
  14. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. */
  16. /** \file
  17. * \ingroup bli
  18. *
  19. * Task parallel range functions.
  20. */
  21. #include <stdlib.h>
  22. #include "MEM_guardedalloc.h"
  23. #include "DNA_listBase.h"
  24. #include "BLI_task.h"
  25. #include "BLI_threads.h"
  26. #include "atomic_ops.h"
  27. #ifdef WITH_TBB
  28. /* Quiet top level deprecation message, unrelated to API usage here. */
  29. # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
  30. # include <tbb/tbb.h>
  31. #endif
  32. #ifdef WITH_TBB
  33. /* Functor for running TBB parallel_for and parallel_reduce. */
  34. struct RangeTask {
  35. TaskParallelRangeFunc func;
  36. void *userdata;
  37. const TaskParallelSettings *settings;
  38. void *userdata_chunk;
  39. /* Root constructor. */
  40. RangeTask(TaskParallelRangeFunc func, void *userdata, const TaskParallelSettings *settings)
  41. : func(func), userdata(userdata), settings(settings)
  42. {
  43. init_chunk(settings->userdata_chunk);
  44. }
  45. /* Copy constructor. */
  46. RangeTask(const RangeTask &other)
  47. : func(other.func), userdata(other.userdata), settings(other.settings)
  48. {
  49. init_chunk(settings->userdata_chunk);
  50. }
  51. /* Splitting constructor for parallel reduce. */
  52. RangeTask(RangeTask &other, tbb::split /* unused */)
  53. : func(other.func), userdata(other.userdata), settings(other.settings)
  54. {
  55. init_chunk(settings->userdata_chunk);
  56. }
  57. ~RangeTask()
  58. {
  59. if (settings->func_free != NULL) {
  60. settings->func_free(userdata, userdata_chunk);
  61. }
  62. MEM_SAFE_FREE(userdata_chunk);
  63. }
  64. void init_chunk(void *from_chunk)
  65. {
  66. if (from_chunk) {
  67. userdata_chunk = MEM_mallocN(settings->userdata_chunk_size, "RangeTask");
  68. memcpy(userdata_chunk, from_chunk, settings->userdata_chunk_size);
  69. }
  70. else {
  71. userdata_chunk = NULL;
  72. }
  73. }
  74. void operator()(const tbb::blocked_range<int> &r) const
  75. {
  76. tbb::this_task_arena::isolate([this, r] {
  77. TaskParallelTLS tls;
  78. tls.userdata_chunk = userdata_chunk;
  79. for (int i = r.begin(); i != r.end(); ++i) {
  80. func(userdata, i, &tls);
  81. }
  82. });
  83. }
  84. void join(const RangeTask &other)
  85. {
  86. settings->func_reduce(userdata, userdata_chunk, other.userdata_chunk);
  87. }
  88. };
  89. #endif
  90. void BLI_task_parallel_range(const int start,
  91. const int stop,
  92. void *userdata,
  93. TaskParallelRangeFunc func,
  94. const TaskParallelSettings *settings)
  95. {
  96. #ifdef WITH_TBB
  97. /* Multithreading. */
  98. if (settings->use_threading && BLI_task_scheduler_num_threads() > 1) {
  99. RangeTask task(func, userdata, settings);
  100. const size_t grainsize = MAX2(settings->min_iter_per_thread, 1);
  101. const tbb::blocked_range<int> range(start, stop, grainsize);
  102. if (settings->func_reduce) {
  103. parallel_reduce(range, task);
  104. if (settings->userdata_chunk) {
  105. memcpy(settings->userdata_chunk, task.userdata_chunk, settings->userdata_chunk_size);
  106. }
  107. }
  108. else {
  109. parallel_for(range, task);
  110. }
  111. return;
  112. }
  113. #endif
  114. /* Single threaded. Nothing to reduce as everything is accumulated into the
  115. * main userdata chunk directly. */
  116. TaskParallelTLS tls;
  117. tls.userdata_chunk = settings->userdata_chunk;
  118. for (int i = start; i < stop; i++) {
  119. func(userdata, i, &tls);
  120. }
  121. if (settings->func_free != NULL) {
  122. settings->func_free(userdata, settings->userdata_chunk);
  123. }
  124. }
  125. int BLI_task_parallel_thread_id(const TaskParallelTLS *UNUSED(tls))
  126. {
  127. #ifdef WITH_TBB
  128. /* Get a unique thread ID for texture nodes. In the future we should get rid
  129. * of the thread ID and change texture evaluation to not require per-thread
  130. * storage that can't be efficiently allocated on the stack. */
  131. static tbb::enumerable_thread_specific<int> tbb_thread_id(-1);
  132. static int tbb_thread_id_counter = 0;
  133. int &thread_id = tbb_thread_id.local();
  134. if (thread_id == -1) {
  135. thread_id = atomic_fetch_and_add_int32(&tbb_thread_id_counter, 1);
  136. if (thread_id >= BLENDER_MAX_THREADS) {
  137. BLI_assert(!"Maximum number of threads exceeded for sculpting");
  138. thread_id = thread_id % BLENDER_MAX_THREADS;
  139. }
  140. }
  141. return thread_id;
  142. #else
  143. return 0;
  144. #endif
  145. }