parallel.qbk 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. [/
  2. / Copyright (c) 2014 Vicente J. Botet Escriba
  3. /
  4. / Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. /]
  7. [//////////////////////////////////////////////////////////]
  8. [section:parallel Parallel - Fork-Join -- EXPERIMENTAL]
  9. [section:fork_join Fork-Join]
  10. [warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(]
  11. [note These features are based on the [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4088.pdf [* n4088 - Task Region R3]] C++1y proposal from P. Halpern, A. Robison, A. Laksberg, H. Sutter, et al. The text that follows has been adapted from this paper to show the differences.]
  12. The major difference respect to the standard proposal is that we are able to use a common executor for several task regions.
  13. [note
  14. Up to now, Boost.Thread doesn't implement the parallel algorithms as defined in [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4105.pdf [* n4105 - Information technology – Programming languages, their environments and system software interfaces – Technical Specification for C++ Extensions for Parallelism]].
  15. ]
  16. [////////////////////]
  17. [section Introduction]
  18. This module introduces a C++11/c++14 library function template `task_region` and a library class `task_region_handle`
  19. with member functions `run` and `wait` that together enable developers to write expressive and portable fork-join
  20. parallel code.
  21. The working draft for the Parallelism TS [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4105.pdf [*N4105]] augments the STL algorithms with the inclusion of parallel execution policies. Programmers use these as a basis to write additional high-level algorithms that can be implemented in terms of the provided parallel algorithms. However, the scope of n4105 does not include lower-level mechanisms to express arbitrary fork-join parallelism
  22. The `task_region`, `run` and the `wait` functions provided by this library are based on the `task_group` concept that is a part of the common subset of the PPL and the TBB libraries.
  23. [endsect] [/ Introduction]
  24. [/////////////////////////]
  25. [section:tutorial Tutorial]
  26. Consider an example of a parallel traversal of a tree, where a user-provided function compute is applied to each node of the tree, returning the sum of the results:
  27. template<typename Func>
  28. int traverse(node *n, Func&& compute)
  29. {
  30. int left = 0, right = 0;
  31. task_region([&](task_region_handle& tr) {
  32. if (n->left)
  33. tr.run([&] { left = traverse(n->left, compute); });
  34. if (n->right)
  35. tr.run([&] { right = traverse(n->right, compute); });
  36. });
  37. return compute(n) + left + right;
  38. }
  39. The example above demonstrates the use of two of the functions proposed in this paper, `task_region` and
  40. `task_region_handle::run`.
  41. The `task_region` function delineates a region in a program code potentially containing invocations of tasks
  42. spawned by the `run` member function of the `task_region_handle` class.
  43. The run function spawns a task, a unit of work that is allowed to execute in parallel with respect to the caller.
  44. Any parallel tasks spawned by `run` within the `task_region` are joined back to a single thread of execution at
  45. the end of the `task_region`.
  46. `run` takes a user-provided function object `f` and starts it asynchronously - i.e. it may return before the
  47. execution of `f` completes. The implementation's scheduler may choose to run `f` immediately or delay running
  48. `f` until compute resources become available.
  49. A `task_region_handle` can be constructed only by `task_region` because it has no public constructors.
  50. Thus, `run` can be invoked (directly or indirectly) only from a user-provided function passed to `task_region`:
  51. void g();
  52. void f(task_region_handle& tr)
  53. {
  54. tr.run(g); // OK, invoked from within task_region in h
  55. }
  56. void h()
  57. {
  58. task_region(f);
  59. }
  60. int main()
  61. {
  62. task_region_handle tr; // Error: no public constructor
  63. tr.run(g); // No way to call run outside of a task_region
  64. return 0;
  65. }
  66. [endsect] [/ Tutorial]
  67. [////////////////]
  68. [section:examples Examples]
  69. [section:fib Parallel Fibonacci]
  70. This is surely the worst implementation of the Fibonacci function. Anyway, here it is, as it is simple and shows the fork-join structure clearly. `Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2)`, so the task decomposition is trivial.
  71. int fib_task_region(int n)
  72. {
  73. using boost::experimental::parallel::task_region;
  74. using boost::experimental::parallel::task_region_handle;
  75. if (n == 0) return 0;
  76. if (n == 1) return 1;
  77. int n1;
  78. int n2;
  79. task_region([&](task_region_handle& trh)
  80. {
  81. trh.run([&]
  82. {
  83. n1 = fib_task_region(n - 1);
  84. });
  85. n2 = fib_task_region(n - 2);
  86. });
  87. return n1 + n2;
  88. }
  89. int main()
  90. {
  91. for (int i = 0; i<10; ++i) {
  92. std::cout << fib_task_region(i) << " ";
  93. }
  94. std::cout << std::endl;
  95. }
  96. [endsect] [/ Fib]
  97. [section:fibex Parallel Fibonacci - Specific executor]
  98. The previous example make use of an implementation defined way to spawn the tasks. Often the user wants to master how the task must be spawned. There is an overload of `task_region` that accept an additional `Executor` parameter and a function that takes as parameter a `task_region_handle_gen<Executor>`. `task_region_handle_gen<Executor>` run uses this executor to spawn the tasks.
  99. template <class Ex>
  100. int fib_task_region_gen( Ex& ex, int n)
  101. {
  102. using boost::experimental::parallel::task_region;
  103. using boost::experimental::parallel::task_region_handle_gen;
  104. if (n == 0) return 0;
  105. if (n == 1) return 1;
  106. int n1;
  107. int n2;
  108. task_region(ex, [&](task_region_handle_gen<Ex>& trh) // (2)
  109. {
  110. trh.run([&]
  111. {
  112. n1 = fib_task_region(n - 1);
  113. });
  114. n2 = fib_task_region(n - 2);
  115. });
  116. return n1 + n2;
  117. }
  118. int main()
  119. {
  120. boost::basic_thread_pool tp; // (1)
  121. for (int i = 0; i<10; ++i) {
  122. std::cout << fib_task_region_gen(tp,i) << " ";
  123. }
  124. std::cout << std::endl;
  125. return 0;
  126. }
  127. The specific executor is declared in line (1) and it is used in line (2).
  128. [endsect] [/ Fib ex]
  129. [section:quick_sort Parallel Accumulate]
  130. [endsect] [/ Accumulate]
  131. [section:quick_sort Parallel Quick Sort]
  132. [endsect] [/ QuickSort]
  133. [endsect] [/ Examples]
  134. [////////////////////////]
  135. [section:rationale Design Rationale]
  136. [endsect] [/ Design Rationale]
  137. [endsect] [/ Fork-Join]
  138. [/////////////////////]
  139. [section:ref Reference -- EXPERIMENTAL]
  140. [/////////////////////////]
  141. [section:v1 Parallel V1]
  142. [section:exception_list Header `<experimental/exception_list.hpp>`]
  143. namespace boost
  144. {
  145. namespace experimental
  146. {
  147. namespace parallel
  148. {
  149. inline namespace v1
  150. {
  151. class exception_list;
  152. } // v1
  153. } // parallel
  154. } // experimental
  155. } // boost
  156. [/////////////////////////]
  157. [section:exception_list Class `exception_list`]
  158. namespace boost
  159. {
  160. namespace experimental
  161. {
  162. namespace parallel
  163. {
  164. inline namespace v1
  165. {
  166. class exception_list: public std::exception
  167. {
  168. public:
  169. typedef 'implementation defined' const_iterator;
  170. ~exception_list() noexcept {}
  171. void add(exception_ptr const& e);
  172. size_t size() const noexcept;
  173. const_iterator begin() const noexcept;
  174. const_iterator end() const noexcept;
  175. const char* what() const noexcept;
  176. };
  177. } // v1
  178. } // parallel
  179. } // experimental
  180. } // boost
  181. [endsect] [/ exception_list]
  182. [endsect] [/ exception_list.hpp]
  183. [endsect] [/ Parallel V1]
  184. [////////////////////////////////////////////////////////////////////]
  185. [section:v2 Parallel V2]
  186. [////////////////////////////////////////////////////////////////////]
  187. [section:concepts Concepts]
  188. [////////////////////////////////////////////////////////////////////]
  189. [section:regionCallable Concept `Region_Callable`]
  190. [endsect] [/ Region_Callable]
  191. [////////////////////////////////////////////////////////////////////]
  192. [section:taskCallable Concept `Task_Callable`]
  193. [endsect] [/ Task_Callable]
  194. [////////////////////////////////////////////////////////////////////]
  195. [endsect] [/ Concepts]
  196. [////////////////////////////////////////////////////////////////////]
  197. [section:task_region Header `<experimental/task_region.hpp>`]
  198. namespace boost
  199. {
  200. namespace experimental
  201. {
  202. namespace parallel
  203. {
  204. inline namespace v2
  205. {
  206. class task_canceled_exception;
  207. template <class Executor>
  208. class task_region_handle_gen;
  209. using default_executor = 'implementation defined';
  210. class task_region_handle;
  211. template <typename Executor, typename F>
  212. void task_region_final(Executor& ex, F&& f);
  213. template <typename F>
  214. void task_region_final(F&& f);
  215. template <typename Executor, typename F>
  216. void task_region(Executor& ex, F&& f);
  217. template <typename F>
  218. void task_region(F&& f);
  219. } // v2
  220. } // parallel
  221. } // experimental
  222. } // boost
  223. [////////////////////////////////////////////////////////////////////]
  224. [section:task_canceled_exception Class `task_canceled_exception `]
  225. namespace boost
  226. {
  227. namespace experimental
  228. {
  229. namespace parallel
  230. {
  231. inline namespace v2
  232. {
  233. class task_canceled_exception: public std::exception
  234. {
  235. public:
  236. task_canceled_exception() noexcept;
  237. task_canceled_exception(const task_canceled_exception&) noexcept;
  238. task_canceled_exception& operator=(const task_canceled_exception&) noexcept;
  239. virtual const char* what() const noexcept;
  240. };
  241. } // v2
  242. } // parallel
  243. } // experimental
  244. } // boost
  245. [endsect] [/ task_canceled_exception]
  246. [////////////////////////////////////////////////////////////////////]
  247. [section:task_region_handle_gen Template Class `task_region_handle_gen<>`]
  248. namespace boost
  249. {
  250. namespace experimental
  251. {
  252. namespace parallel
  253. {
  254. inline namespace v2
  255. {
  256. template <class Executor>
  257. class task_region_handle_gen
  258. {
  259. protected:
  260. task_region_handle_gen(Executor& ex);
  261. ~task_region_handle_gen();
  262. public:
  263. task_region_handle_gen(const task_region_handle_gen&) = delete;
  264. task_region_handle_gen& operator=(const task_region_handle_gen&) = delete;
  265. task_region_handle_gen* operator&() const = delete;
  266. template<typename F>
  267. void run(F&& f);
  268. void wait();
  269. };
  270. } // v2
  271. } // parallel
  272. } // experimental
  273. } // boost
  274. [endsect] [/ task_region_handle_gen]
  275. [////////////////////////////////////////////////////////////////////]
  276. [section:default_executor Class `default_executor `]
  277. namespace boost
  278. {
  279. namespace experimental
  280. {
  281. namespace parallel
  282. {
  283. inline namespace v2
  284. {
  285. using default_executor = 'implementation defined';
  286. } // v2
  287. } // parallel
  288. } // experimental
  289. } // boost
  290. [endsect] [/ default_executor]
  291. [////////////////////////////////////////////////////////////////////]
  292. [section:task_region_handle Class `task_region_handle `]
  293. namespace boost
  294. {
  295. namespace experimental
  296. {
  297. namespace parallel
  298. {
  299. inline namespace v2
  300. {
  301. class task_region_handle :
  302. public task_region_handle_gen<default_executor>
  303. {
  304. protected:
  305. task_region_handle();
  306. task_region_handle(const task_region_handle&) = delete;
  307. task_region_handle& operator=(const task_region_handle&) = delete;
  308. task_region_handle* operator&() const = delete;
  309. };
  310. } // v2
  311. } // parallel
  312. } // experimental
  313. } // boost
  314. [endsect] [/ task_region_handle]
  315. [////////////////////////////////////////////////////////////////////]
  316. [section:task_region_final Template Function `task_region_final `]
  317. namespace boost
  318. {
  319. namespace experimental
  320. {
  321. namespace parallel
  322. {
  323. inline namespace v2
  324. {
  325. template <typename Executor, typename F>
  326. void task_region_final(Executor& ex, F&& f);
  327. template <typename F>
  328. void task_region_final(F&& f);
  329. } // v2
  330. } // parallel
  331. } // experimental
  332. } // boost
  333. [endsect] [/ task_region_final]
  334. [////////////////////////////////////////////////////////////////////]
  335. [section:task_region Template Function `task_region `]
  336. namespace boost
  337. {
  338. namespace experimental
  339. {
  340. namespace parallel
  341. {
  342. inline namespace v2
  343. {
  344. template <typename Executor, typename F>
  345. void task_region(Executor& ex, F&& f);
  346. template <typename F>
  347. void task_region(F&& f);
  348. } // v2
  349. } // parallel
  350. } // experimental
  351. } // boost
  352. [endsect] [/ task_region]
  353. [endsect] [/ task_region.hpp]
  354. [endsect] [/ Parallel V2]
  355. [endsect] [/ Reference]
  356. [endsect] [/ Parallel]