modular_ez.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /* StarPU --- Runtime system for heterogeneous multicore architectures.
  2. *
  3. * Copyright (C) 2013-2020 Université de Bordeaux, CNRS (LaBRI UMR 5800), Inria
  4. * Copyright (C) 2013 Simon Archipoff
  5. *
  6. * StarPU is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation; either version 2.1 of the License, or (at
  9. * your option) any later version.
  10. *
  11. * StarPU is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. *
  15. * See the GNU Lesser General Public License in COPYING.LGPL for more details.
  16. */
  17. #include <starpu_sched_component.h>
  18. #include <starpu_scheduler.h>
  19. #include <limits.h>
  20. #include <core/workers.h>
  21. #include <datawizard/memory_nodes.h>
  22. /* The scheduling strategy may look like this :
  23. *
  24. * |
  25. * fifo_above
  26. * |
  27. * decision_component <--push-- perfmodel_select_component --push--> eager_component
  28. * | | | |
  29. * fifo fifo fifo |
  30. * | | | |
  31. * eager eager eager |
  32. * | | | |
  33. * >--------------------------------------------------------------<
  34. * | |
  35. * best_impl_component best_impl_component
  36. * | |
  37. * worker_component worker_component
  38. */
  39. /* The two thresolds concerns the fifo components below, which contains queues
  40. * who can handle the priority of StarPU tasks. You can tune your
  41. * scheduling by benching those values and choose which one is the
  42. * best for your current application.
  43. * The current value of the ntasks_threshold is the best we found
  44. * so far across several types of applications (cholesky, LU, stencil).
  45. */
  46. #define _STARPU_SCHED_NTASKS_THRESHOLD_HEFT 30
  47. #define _STARPU_SCHED_NTASKS_THRESHOLD_DEFAULT 2
  48. #define _STARPU_SCHED_EXP_LEN_THRESHOLD_DEFAULT 1000000000.0
  49. void starpu_sched_component_initialize_simple_schedulers(unsigned sched_ctx_id, unsigned ndecisions, ...)
  50. {
  51. struct starpu_sched_tree * t;
  52. struct starpu_sched_component *last = NULL; /* Stores the last created component, from top to bottom */
  53. unsigned i, j, n;
  54. struct starpu_sched_component *userchoice_component = NULL;
  55. struct starpu_sched_component *decision_component = NULL;
  56. struct starpu_sched_component *no_perfmodel_component = NULL;
  57. struct starpu_sched_component *calibrator_component = NULL;
  58. unsigned sched;
  59. va_list varg_list;
  60. unsigned decide_flags;
  61. unsigned flags;
  62. /* Start building the tree */
  63. t = starpu_sched_tree_create(sched_ctx_id);
  64. t->root = NULL;
  65. starpu_sched_ctx_set_policy_data(sched_ctx_id, (void*)t);
  66. STARPU_ASSERT(ndecisions >= 1);
  67. if (ndecisions != 1)
  68. {
  69. /* Take choice between schedulers from user */
  70. userchoice_component = starpu_sched_component_userchoice_create(t, NULL);
  71. t->root = userchoice_component;
  72. }
  73. unsigned nbelow;
  74. unsigned nummaxids;
  75. va_start(varg_list, ndecisions);
  76. for (sched = 0; sched < ndecisions; sched++)
  77. {
  78. last = userchoice_component;
  79. starpu_sched_component_create_t create_decision_component = va_arg(varg_list, starpu_sched_component_create_t);
  80. void *data = va_arg(varg_list, void *);
  81. flags = va_arg(varg_list, unsigned);
  82. (void) create_decision_component;
  83. (void) data;
  84. /* Create combined workers if requested */
  85. if (flags & STARPU_SCHED_SIMPLE_COMBINED_WORKERS)
  86. starpu_sched_find_all_worker_combinations();
  87. /* Components parameters */
  88. if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO || flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO)
  89. {
  90. /* The application may use any integer */
  91. if (starpu_sched_ctx_min_priority_is_set(sched_ctx_id) == 0)
  92. starpu_sched_ctx_set_min_priority(sched_ctx_id, INT_MIN);
  93. if (starpu_sched_ctx_max_priority_is_set(sched_ctx_id) == 0)
  94. starpu_sched_ctx_set_max_priority(sched_ctx_id, INT_MAX);
  95. }
  96. /* See what the component will decide */
  97. nummaxids = starpu_worker_get_count() + starpu_combined_worker_get_count();
  98. if (starpu_memory_nodes_get_count() > nummaxids)
  99. nummaxids = starpu_memory_nodes_get_count();
  100. if (STARPU_ANY_WORKER > nummaxids)
  101. nummaxids = STARPU_ANY_WORKER;
  102. if (sched == 0)
  103. decide_flags = flags & STARPU_SCHED_SIMPLE_DECIDE_MASK;
  104. else
  105. STARPU_ASSERT(decide_flags == (flags & STARPU_SCHED_SIMPLE_DECIDE_MASK));
  106. }
  107. va_end(varg_list);
  108. unsigned below_id[nummaxids];
  109. switch (decide_flags)
  110. {
  111. case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
  112. /* Count workers */
  113. nbelow = starpu_worker_get_count() + starpu_combined_worker_get_count();
  114. /* and no need for IDs */
  115. break;
  116. case STARPU_SCHED_SIMPLE_DECIDE_MEMNODES:
  117. {
  118. /* Count memory nodes */
  119. n = starpu_memory_nodes_get_count();
  120. nbelow = 0;
  121. for(i = 0; i < n; i++)
  122. {
  123. for(j = 0; j < starpu_worker_get_count() + starpu_combined_worker_get_count(); j++)
  124. if (starpu_worker_get_memory_node(j) == i)
  125. break;
  126. if (j >= starpu_worker_get_count() + starpu_combined_worker_get_count())
  127. /* Don't create a component string for this memory node with no worker */
  128. continue;
  129. below_id[nbelow] = i;
  130. nbelow++;
  131. }
  132. break;
  133. }
  134. case STARPU_SCHED_SIMPLE_DECIDE_ARCHS:
  135. {
  136. /* Count available architecture types */
  137. enum starpu_worker_archtype type;
  138. nbelow = 0;
  139. for (type = STARPU_CPU_WORKER; type < STARPU_ANY_WORKER; type++)
  140. {
  141. if (starpu_worker_get_count_by_type(type))
  142. {
  143. below_id[nbelow] = type;
  144. nbelow++;
  145. }
  146. }
  147. break;
  148. }
  149. default:
  150. STARPU_ABORT();
  151. }
  152. STARPU_ASSERT(nbelow > 0);
  153. struct starpu_sched_component *last_below[nbelow];
  154. memset(&last_below, 0, sizeof(last_below));
  155. if (ndecisions != 1)
  156. {
  157. /* Will need to stage pulls, create one per choice */
  158. for (i = 0; i < nbelow; i++)
  159. last_below[i] = starpu_sched_component_stage_create(t, NULL);
  160. }
  161. va_start(varg_list, ndecisions);
  162. for (sched = 0; sched < ndecisions; sched++)
  163. {
  164. last = userchoice_component;
  165. starpu_sched_component_create_t create_decision_component = va_arg(varg_list, starpu_sched_component_create_t);
  166. void *data = va_arg(varg_list, void *);
  167. flags = va_arg(varg_list, unsigned);
  168. if (nbelow == 1 && !(flags & STARPU_SCHED_SIMPLE_DECIDE_ALWAYS))
  169. {
  170. /* Oh, no choice, we don't actually need to decide, just
  171. * use an eager scheduler */
  172. decision_component = starpu_sched_component_eager_create(t, NULL);
  173. /* But make sure we have a fifo above it, fifos below it would
  174. * possibly refuse tasks out of available room */
  175. flags |= STARPU_SCHED_SIMPLE_FIFO_ABOVE;
  176. }
  177. else
  178. {
  179. decision_component = create_decision_component(t, data);
  180. }
  181. /* First, a fifo if requested */
  182. if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE)
  183. {
  184. struct starpu_sched_component *fifo_above;
  185. if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO)
  186. {
  187. fifo_above = starpu_sched_component_prio_create(t, NULL);
  188. }
  189. else
  190. {
  191. fifo_above = starpu_sched_component_fifo_create(t, NULL);
  192. }
  193. if (!last)
  194. last = t->root = fifo_above;
  195. else
  196. {
  197. starpu_sched_component_connect(last, fifo_above);
  198. last = fifo_above;
  199. }
  200. }
  201. /* Then, perfmodel calibration if requested, and plug the scheduling decision-making component to it */
  202. if (flags & STARPU_SCHED_SIMPLE_PERFMODEL)
  203. {
  204. no_perfmodel_component = starpu_sched_component_eager_create(t, NULL);
  205. calibrator_component = starpu_sched_component_eager_calibration_create(t, NULL);
  206. struct starpu_sched_component_perfmodel_select_data perfmodel_select_data =
  207. {
  208. .calibrator_component = calibrator_component,
  209. .no_perfmodel_component = no_perfmodel_component,
  210. .perfmodel_component = decision_component,
  211. };
  212. struct starpu_sched_component * perfmodel_select_component = starpu_sched_component_perfmodel_select_create(t, &perfmodel_select_data);
  213. if (!last)
  214. last = t->root = perfmodel_select_component;
  215. else
  216. starpu_sched_component_connect(last, perfmodel_select_component);
  217. starpu_sched_component_connect(perfmodel_select_component, decision_component);
  218. starpu_sched_component_connect(perfmodel_select_component, calibrator_component);
  219. starpu_sched_component_connect(perfmodel_select_component, no_perfmodel_component);
  220. }
  221. else
  222. {
  223. /* No perfmodel calibration */
  224. if (!last)
  225. /* Plug decision_component directly */
  226. last = t->root = decision_component;
  227. else
  228. /* Plug decision_component to fifo */
  229. starpu_sched_component_connect(last, decision_component);
  230. }
  231. /* Silence static analysis warnings */
  232. (void) last;
  233. /* Take default ntasks_threshold */
  234. unsigned ntasks_threshold;
  235. if (starpu_sched_component_is_heft(decision_component) ||
  236. starpu_sched_component_is_mct(decision_component) ||
  237. starpu_sched_component_is_heteroprio(decision_component))
  238. {
  239. /* These need more queueing to allow CPUs to take some share of the work */
  240. ntasks_threshold = _STARPU_SCHED_NTASKS_THRESHOLD_HEFT;
  241. }
  242. else
  243. {
  244. ntasks_threshold = _STARPU_SCHED_NTASKS_THRESHOLD_DEFAULT;
  245. }
  246. /* But let user tune it */
  247. ntasks_threshold = starpu_get_env_number_default("STARPU_NTASKS_THRESHOLD", ntasks_threshold);
  248. double exp_len_threshold = _STARPU_SCHED_EXP_LEN_THRESHOLD_DEFAULT;
  249. exp_len_threshold = starpu_get_env_float_default("STARPU_EXP_LEN_THRESHOLD", exp_len_threshold);
  250. int ready = starpu_get_env_number_default("STARPU_SCHED_READY", flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_READY ? 1 : 0);
  251. int exp = flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_EXP ? 1 : 0;
  252. struct starpu_sched_component_prio_data prio_data =
  253. {
  254. .ntasks_threshold = ntasks_threshold,
  255. .exp_len_threshold = exp_len_threshold,
  256. .ready = ready,
  257. .exp = exp,
  258. };
  259. struct starpu_sched_component_fifo_data fifo_data =
  260. {
  261. .ntasks_threshold = ntasks_threshold,
  262. .exp_len_threshold = exp_len_threshold,
  263. .ready = ready,
  264. .exp = exp,
  265. };
  266. /* Create one fifo+eager component pair per choice, below scheduling decision */
  267. for(i = 0; i < nbelow; i++)
  268. {
  269. last = decision_component;
  270. if (flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW
  271. && !(decide_flags == STARPU_SCHED_SIMPLE_DECIDE_WORKERS
  272. && i >= starpu_worker_get_count()))
  273. {
  274. struct starpu_sched_component *fifo_below;
  275. if (flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO)
  276. {
  277. fifo_below = starpu_sched_component_prio_create(t, &prio_data);
  278. }
  279. else
  280. {
  281. fifo_below = starpu_sched_component_fifo_create(t, &fifo_data);
  282. }
  283. starpu_sched_component_connect(last, fifo_below);
  284. last = fifo_below;
  285. }
  286. switch (decide_flags)
  287. {
  288. case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
  289. /* 1-1 mapping between choice and worker, no need for an eager component */
  290. n = 1;
  291. break;
  292. case STARPU_SCHED_SIMPLE_DECIDE_MEMNODES:
  293. n = 0;
  294. for (j = 0; j < starpu_worker_get_count() + starpu_combined_worker_get_count(); j++)
  295. if (starpu_worker_get_memory_node(j) == below_id[i])
  296. n++;
  297. break;
  298. case STARPU_SCHED_SIMPLE_DECIDE_ARCHS:
  299. n = starpu_worker_get_count_by_type(i);
  300. break;
  301. default:
  302. STARPU_ABORT();
  303. }
  304. STARPU_ASSERT(n >= 1);
  305. if (n > 1)
  306. {
  307. /* Several workers for this choice, need to introduce
  308. * a component to distribute the work */
  309. struct starpu_sched_component *distribute;
  310. if (flags & STARPU_SCHED_SIMPLE_WS_BELOW)
  311. {
  312. distribute = starpu_sched_component_work_stealing_create(t, NULL);
  313. }
  314. else
  315. {
  316. distribute = starpu_sched_component_eager_create(t, NULL);
  317. }
  318. starpu_sched_component_connect(last, distribute);
  319. last = distribute;
  320. }
  321. if (ndecisions != 1)
  322. /* Connect to stage component */
  323. starpu_sched_component_connect(last, last_below[i]);
  324. else
  325. /* Directly let it connected to worker */
  326. last_below[i] = last;
  327. }
  328. }
  329. va_end(varg_list);
  330. /* Finish by creating components per worker */
  331. for(i = 0; i < starpu_worker_get_count() + starpu_combined_worker_get_count(); i++)
  332. {
  333. /* Start from the bottom */
  334. struct starpu_sched_component * worker_component = starpu_sched_component_worker_new(sched_ctx_id, i);
  335. struct starpu_sched_component * worker = worker_component;
  336. unsigned id;
  337. /* Create implementation chooser if requested */
  338. if (flags & STARPU_SCHED_SIMPLE_IMPL)
  339. {
  340. struct starpu_sched_component * impl_component = starpu_sched_component_best_implementation_create(t, NULL);
  341. starpu_sched_component_connect(impl_component, worker_component);
  342. /* Reroute components above through it */
  343. worker = impl_component;
  344. }
  345. switch (decide_flags)
  346. {
  347. case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
  348. id = i;
  349. break;
  350. case STARPU_SCHED_SIMPLE_DECIDE_MEMNODES:
  351. for (id = 0; id < nbelow; id++)
  352. if (below_id[id] == starpu_worker_get_memory_node(i))
  353. break;
  354. break;
  355. case STARPU_SCHED_SIMPLE_DECIDE_ARCHS:
  356. for (id = 0; id < nbelow; id++)
  357. if (below_id[id] == starpu_worker_get_type(i))
  358. break;
  359. break;
  360. default:
  361. STARPU_ABORT();
  362. }
  363. STARPU_ASSERT(id < nbelow);
  364. last = last_below[id];
  365. if (!last)
  366. last = decision_component;
  367. starpu_sched_component_connect(last, worker);
  368. /* Plug perfmodel calibrator if requested */
  369. /* FIXME: this won't work with several scheduling decisions */
  370. if (flags & STARPU_SCHED_SIMPLE_PERFMODEL)
  371. {
  372. starpu_sched_component_connect(no_perfmodel_component, worker);
  373. /* Calibrator needs to choose the implementation */
  374. starpu_sched_component_connect(calibrator_component, worker_component);
  375. }
  376. }
  377. starpu_sched_tree_update_workers(t);
  378. starpu_sched_tree_update_workers_in_ctx(t);
  379. }
  380. void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_create_t create_decision_component, void *data, unsigned flags, unsigned sched_ctx_id)
  381. {
  382. starpu_sched_component_initialize_simple_schedulers(sched_ctx_id, 1, create_decision_component, data, flags);
  383. }