You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

task_executor.cpp 7.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /**
  2. * \file dnn/test/common/task_executor.cpp
  3. * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  4. *
  5. * Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
  6. *
  7. * Unless required by applicable law or agreed to in writing,
  8. * software distributed under the License is distributed on an
  9. * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. */
  11. #include "test/common/utils.h"
  12. #if MEGDNN_ENABLE_MULTI_THREADS
  13. #include <atomic>
  14. #include <condition_variable>
  15. #include <mutex>
  16. #include <thread>
  17. #include <vector>
  18. #if defined(WIN32)
  19. #include <windows.h>
  20. #else
  21. #ifdef __APPLE__
  22. #include <mach/mach.h>
  23. #include <mach/mach_host.h>
  24. #else
  25. #include <pthread.h>
  26. #include <sched.h>
  27. #include <stdint.h>
  28. #include <sys/syscall.h>
  29. #include <sys/sysinfo.h>
  30. #include <unistd.h>
  31. #endif
  32. #endif
  33. #endif
  34. using namespace megdnn;
  35. using namespace test;
  36. namespace {
  37. #if MEGDNN_ENABLE_MULTI_THREADS
  38. #define SET_AFFINITY_CHECK(cond) \
  39. do { \
  40. if (cond) { \
  41. megdnn_log_warn("syscall for set affinity error\n"); \
  42. } \
  43. } while (0);
  44. #if defined(WIN32)
  45. DWORD do_set_cpu_affinity(const DWORD& mask) {
  46. auto succ = SetThreadAffinityMask(GetCurrentThread(), mask);
  47. return succ;
  48. }
  49. DWORD set_cpu_affinity(const std::vector<size_t>& cpuset) {
  50. auto nr = get_cpu_count();
  51. DWORD mask = 0;
  52. for (auto i : cpuset) {
  53. megdnn_assert(i < 64 && i < nr);
  54. mask |= 1 << i;
  55. }
  56. return do_set_cpu_affinity(mask);
  57. }
  58. #else // not WIN32
  59. #if defined(__APPLE__)
  60. #pragma message("set_cpu_affinity is not enabled on apple platform")
  61. int do_set_cpu_affinity(const int mask) {
  62. MEGDNN_MARK_USED_VAR(mask);
  63. return -1;
  64. }
  65. int set_cpu_affinity(const std::vector<size_t>& cpuset) {
  66. MEGDNN_MARK_USED_VAR(cpuset);
  67. return -1;
  68. }
  69. #else // not __APPLE__
  70. cpu_set_t do_set_cpu_affinity(const cpu_set_t& mask) {
  71. cpu_set_t prev_mask;
  72. #if defined(ANDROID) || defined(__ANDROID__)
  73. SET_AFFINITY_CHECK(sched_getaffinity(gettid(), sizeof(prev_mask), &prev_mask));
  74. SET_AFFINITY_CHECK(sched_setaffinity(gettid(), sizeof(mask), &mask));
  75. #else
  76. SET_AFFINITY_CHECK(
  77. sched_getaffinity(syscall(__NR_gettid), sizeof(prev_mask), &prev_mask));
  78. SET_AFFINITY_CHECK(sched_setaffinity(syscall(__NR_gettid), sizeof(mask), &mask));
  79. #endif // defined(ANDROID) || defined(__ANDROID__)
  80. return prev_mask;
  81. }
  82. cpu_set_t set_cpu_affinity(const std::vector<size_t>& cpuset) {
  83. cpu_set_t mask;
  84. CPU_ZERO(&mask);
  85. auto nr = get_cpu_count();
  86. for (auto i : cpuset) {
  87. megdnn_assert(i < nr, "invalid CPU ID: nr_cpu=%zu id=%zu", nr, i);
  88. CPU_SET(i, &mask);
  89. }
  90. return do_set_cpu_affinity(mask);
  91. }
  92. #endif // __APPLE__
  93. #endif // WIN32
  94. #endif // MEGDNN_ENABLE_MULTI_THREADS
  95. } // anonymous namespace
  96. CpuDispatchChecker::TaskExecutor::TaskExecutor(TaskExecutorConfig* config) {
  97. if (config != nullptr) {
  98. #if MEGDNN_ENABLE_MULTI_THREADS
  99. m_main_thread_affinity = false;
  100. m_stop = false;
  101. auto worker_threads_main_loop = [this](size_t i) {
  102. if (m_cpu_ids.size() > i)
  103. MEGDNN_MARK_USED_VAR(set_cpu_affinity({m_cpu_ids[i]}));
  104. while (!m_stop) {
  105. int index = -1;
  106. if (m_workers_flag[i]->load(std::memory_order_acquire)) {
  107. while ((index = m_current_task_iter.fetch_sub(
  108. 1, std::memory_order_acq_rel)) &&
  109. index > 0) {
  110. m_task(static_cast<size_t>(m_all_task_iter - index), i);
  111. }
  112. //! Flag worker is finished
  113. m_workers_flag[i]->store(false, std::memory_order_release);
  114. }
  115. std::this_thread::yield();
  116. }
  117. };
  118. m_nr_threads = config->nr_thread;
  119. m_cpu_ids.insert(
  120. m_cpu_ids.end(), config->affinity_core_set.begin(),
  121. config->affinity_core_set.end());
  122. if (m_cpu_ids.empty()) {
  123. megdnn_log_warn("Thread affinity was not set.");
  124. } else {
  125. megdnn_assert(
  126. m_cpu_ids.size() <= get_cpu_count(),
  127. "The input affinity_core_set size exceed the "
  128. "number of CPU cores, got: %zu cpu_count: %zu.",
  129. m_cpu_ids.size(), get_cpu_count());
  130. }
  131. for (size_t i = 0; i < m_nr_threads - 1; i++) {
  132. m_workers_flag.emplace_back(new std::atomic_bool{false});
  133. m_workers.emplace_back(std::bind(worker_threads_main_loop, i));
  134. }
  135. #else
  136. megdnn_throw(
  137. "Try to use multithreading with "
  138. "\'MEGDNN_ENABLE_MULTI_THREADS\' set to 0.");
  139. #endif
  140. } else {
  141. m_nr_threads = 1;
  142. }
  143. }
  144. void CpuDispatchChecker::TaskExecutor::add_task(
  145. const MultiThreadingTask& task, size_t parallelism) {
  146. #if MEGDNN_ENABLE_MULTI_THREADS
  147. if (!m_main_thread_affinity && m_cpu_ids.size() == m_nr_threads) {
  148. m_main_thread_prev_affinity_mask =
  149. set_cpu_affinity({m_cpu_ids[m_nr_threads - 1]});
  150. m_main_thread_affinity = true;
  151. }
  152. #endif
  153. if (m_nr_threads == 1 || parallelism == 1) {
  154. for (size_t i = 0; i < parallelism; i++) {
  155. task(i, 0);
  156. }
  157. } else {
  158. #if MEGDNN_ENABLE_MULTI_THREADS
  159. m_all_task_iter = parallelism;
  160. m_current_task_iter.exchange(parallelism, std::memory_order_acq_rel);
  161. m_task = task;
  162. //! Set flag to start thread working
  163. for (uint32_t i = 0; i < m_nr_threads - 1; i++) {
  164. *m_workers_flag[i] = true;
  165. }
  166. int index = -1;
  167. while ((index = m_current_task_iter.fetch_sub(1, std::memory_order_acq_rel)) &&
  168. index > 0) {
  169. m_task(static_cast<size_t>(m_all_task_iter - index), m_nr_threads - 1);
  170. }
  171. sync();
  172. #else
  173. megdnn_throw(
  174. "Try to use multithreading with "
  175. "\'MEGDNN_ENABLE_MULTI_THREADS\' set to 0.");
  176. #endif
  177. }
  178. }
  179. void CpuDispatchChecker::TaskExecutor::add_task(const Task& task) {
  180. task();
  181. }
  182. void CpuDispatchChecker::TaskExecutor::sync() {
  183. #if MEGDNN_ENABLE_MULTI_THREADS
  184. bool no_finished = false;
  185. do {
  186. no_finished = false;
  187. for (uint32_t i = 0; i < m_nr_threads - 1; ++i) {
  188. if (*m_workers_flag[i]) {
  189. no_finished = true;
  190. break;
  191. }
  192. }
  193. if (no_finished) {
  194. std::this_thread::yield();
  195. }
  196. } while (no_finished);
  197. #endif
  198. }
  199. CpuDispatchChecker::TaskExecutor::~TaskExecutor() {
  200. #if MEGDNN_ENABLE_MULTI_THREADS
  201. m_stop = true;
  202. for (auto& worker : m_workers) {
  203. worker.join();
  204. }
  205. for (auto flag : m_workers_flag) {
  206. delete flag;
  207. }
  208. if (m_main_thread_affinity) {
  209. //! Restore the main thread affinity.
  210. MEGDNN_MARK_USED_VAR(do_set_cpu_affinity(m_main_thread_prev_affinity_mask));
  211. }
  212. #endif
  213. }
  214. // vim: syntax=cpp.doxygen