gtbbexecutor_internal_tests.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. //
  5. // Copyright (C) 2020 Intel Corporation
  6. // Deliberately include .cpp file instead of header as we use non exported function (execute)
  7. #include <executor/gtbbexecutor.cpp>
  8. #ifdef HAVE_TBB
  9. #include <tbb/tbb.h>
  10. #include <tbb/task.h>
  11. #if TBB_INTERFACE_VERSION < 12000
  12. #include <tbb/task_arena.h>
  13. #include "../test_precomp.hpp"
  14. #include <thread>
  15. namespace {
  16. tbb::task_arena create_task_arena(int max_concurrency = tbb::task_arena::automatic /* set to 1 for single thread */) {
  17. unsigned int reserved_for_master_threads = 1;
  18. if (max_concurrency == 1) {
  19. // Leave no room for TBB worker threads, by reserving all to masters.
  20. // TBB runtime guarantees that no worker threads will join the arena
  21. // if max_concurrency is equal to reserved_for_master_threads
  22. // except 1:1 + use of enqueued tasks for safety guarantee.
  23. // So deliberately make it 2:2 to force TBB not to create extra thread.
  24. //
  25. // N.B. one slot will left empty as only one master thread(one that
  26. // calls root->wait_for_all()) will join the arena.
  27. // FIXME: strictly speaking master can take any free slot, not the first one.
  28. // However at the moment master seems to pick 0 slot all the time.
  29. max_concurrency = 2;
  30. reserved_for_master_threads = 2;
  31. }
  32. return tbb::task_arena{max_concurrency, reserved_for_master_threads};
  33. }
  34. }
  35. namespace opencv_test {
  36. TEST(TBBExecutor, Basic) {
  37. using namespace cv::gimpl::parallel;
  38. bool executed = false;
  39. prio_items_queue_t q;
  40. tile_node n([&]() {
  41. executed = true;
  42. });
  43. q.push(&n);
  44. execute(q);
  45. EXPECT_EQ(true, executed);
  46. }
  47. TEST(TBBExecutor, SerialExecution) {
  48. using namespace cv::gimpl::parallel;
  49. const int n = 10;
  50. prio_items_queue_t q;
  51. std::vector<tile_node> nodes; nodes.reserve(n+1);
  52. std::vector<std::thread::id> thread_id(n);
  53. for (int i=0; i <n; i++) {
  54. nodes.push_back(tile_node([&, i]() {
  55. thread_id[i] = std::this_thread::get_id();
  56. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  57. }));
  58. q.push(&nodes.back());
  59. }
  60. auto serial_arena = create_task_arena(1);
  61. execute(q, serial_arena);
  62. auto print_thread_ids = [&] {
  63. std::stringstream str;
  64. for (auto& i : thread_id) { str << i <<" ";}
  65. return str.str();
  66. };
  67. EXPECT_NE(thread_id[0], std::thread::id{}) << print_thread_ids();
  68. EXPECT_EQ(thread_id.size(), static_cast<size_t>(std::count(thread_id.begin(), thread_id.end(), thread_id[0])))
  69. << print_thread_ids();
  70. }
  71. TEST(TBBExecutor, AsyncBasic) {
  72. using namespace cv::gimpl::parallel;
  73. std::atomic<bool> callback_ready {false};
  74. std::function<void()> callback;
  75. std::atomic<bool> callback_called {false};
  76. std::atomic<bool> master_is_waiting {true};
  77. std::atomic<bool> master_was_blocked_until_callback_called {false};
  78. auto async_thread = std::thread([&] {
  79. bool slept = false;
  80. while (!callback_ready) {
  81. std::this_thread::sleep_for(std::chrono::milliseconds(1));
  82. slept = true;
  83. }
  84. if (!slept) {
  85. std::this_thread::sleep_for(std::chrono::milliseconds(1));
  86. }
  87. callback_called = true;
  88. master_was_blocked_until_callback_called = (master_is_waiting == true);
  89. callback();
  90. });
  91. auto async_task_body = [&](std::function<void()>&& cb, size_t /*total_order_index*/) {
  92. callback = std::move(cb);
  93. callback_ready = true;
  94. };
  95. tile_node n(async, std::move(async_task_body));
  96. prio_items_queue_t q;
  97. q.push(&n);
  98. execute(q);
  99. master_is_waiting = false;
  100. async_thread.join();
  101. EXPECT_EQ(true, callback_called);
  102. EXPECT_EQ(true, master_was_blocked_until_callback_called);
  103. }
  104. TEST(TBBExecutor, Dependencies) {
  105. using namespace cv::gimpl::parallel;
  106. const int n = 10;
  107. bool serial = true;
  108. std::atomic<int> counter {0};
  109. prio_items_queue_t q;
  110. std::vector<tile_node> nodes; nodes.reserve(n+1);
  111. const int invalid_order = -10;
  112. std::vector<int> tiles_exec_order(n, invalid_order);
  113. auto add_dependency_to = [](tile_node& node, tile_node& dependency) {
  114. dependency.dependants.push_back(&node);
  115. node.dependencies++;
  116. node.dependency_count.fetch_add(1);
  117. };
  118. for (int i=0; i <n; i++) {
  119. nodes.push_back(tile_node([&, i]() {
  120. tiles_exec_order[i] = counter++;
  121. if (!serial) {
  122. //sleep gives a better chance for other threads to take part in the execution
  123. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  124. }
  125. }));
  126. if (i >0) {
  127. auto last_node = nodes.end() - 1;
  128. add_dependency_to(*last_node, *(last_node -1));
  129. }
  130. }
  131. q.push(&nodes.front());
  132. auto arena = serial ? create_task_arena(1) : create_task_arena();
  133. execute(q, arena);
  134. auto print_execution_order = [&] {
  135. std::stringstream str;
  136. for (auto& i : tiles_exec_order) { str << i <<" ";}
  137. return str.str();
  138. };
  139. ASSERT_EQ(0, std::count(tiles_exec_order.begin(), tiles_exec_order.end(), invalid_order))
  140. << "Not all " << n << " task executed ?\n"
  141. <<" execution order : " << print_execution_order();
  142. for (size_t i=0; i <nodes.size(); i++) {
  143. auto node_exec_order = tiles_exec_order[i];
  144. for (auto* dependee : nodes[i].dependants) {
  145. auto index = std::distance(&nodes.front(), dependee);
  146. auto dependee_execution_order = tiles_exec_order[index];
  147. ASSERT_LT(node_exec_order, dependee_execution_order) << "node number " << index << " is executed earlier than it's dependency " << i;
  148. }
  149. }
  150. }
  151. } // namespace opencv_test
  152. #endif //TBB_INTERFACE_VERSION
  153. #endif //HAVE_TBB