component_sched.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /* StarPU --- Runtime system for heterogeneous multicore architectures.
  2. *
  3. * Copyright (C) 2013 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 <core/jobs.h>
  18. #include <core/workers.h>
  19. #include <starpu_sched_component.h>
  20. #include <starpu_thread_util.h>
  21. #include <float.h>
  22. #include "sched_component.h"
  23. /******************************************************************************
  24. * Generic Scheduling Components' helper functions *
  25. ******************************************************************************/
  26. /* this function find the best implementation or an implementation that need to be calibrated for a worker available
  27. * and set prediction in *length. nan if a implementation need to be calibrated, 0.0 if no perf model are available
  28. * return false if no worker on the component can execute that task
  29. */
  30. int starpu_sched_component_execute_preds(struct starpu_sched_component * component, struct starpu_task * task, double * length)
  31. {
  32. STARPU_ASSERT(component && task);
  33. int can_execute = 0;
  34. starpu_task_bundle_t bundle = task->bundle;
  35. double len = DBL_MAX;
  36. int workerid;
  37. for(workerid = starpu_bitmap_first(component->workers_in_ctx);
  38. workerid != -1;
  39. workerid = starpu_bitmap_next(component->workers_in_ctx, workerid))
  40. {
  41. struct starpu_perfmodel_arch* archtype = starpu_worker_get_perf_archtype(workerid, component->tree->sched_ctx_id);
  42. int nimpl;
  43. for(nimpl = 0; nimpl < STARPU_MAXIMPLEMENTATIONS; nimpl++)
  44. {
  45. if(starpu_worker_can_execute_task(workerid,task,nimpl)
  46. || starpu_combined_worker_can_execute_task(workerid, task, nimpl))
  47. {
  48. double d;
  49. can_execute = 1;
  50. if(bundle)
  51. d = starpu_task_bundle_expected_length(bundle, archtype, nimpl);
  52. else
  53. d = starpu_task_expected_length(task, archtype, nimpl);
  54. if(isnan(d))
  55. {
  56. *length = d;
  57. return can_execute;
  58. }
  59. if(_STARPU_IS_ZERO(d) && !can_execute)
  60. {
  61. can_execute = 1;
  62. continue;
  63. }
  64. if(d < len)
  65. {
  66. len = d;
  67. }
  68. }
  69. }
  70. if(STARPU_SCHED_COMPONENT_IS_HOMOGENEOUS(component))
  71. break;
  72. }
  73. if(len == DBL_MAX) /* we dont have perf model */
  74. len = 0.0;
  75. if(length)
  76. *length = len;
  77. return can_execute;
  78. }
  79. /* very similar function that dont compute prediction */
  80. int starpu_sched_component_can_execute_task(struct starpu_sched_component * component, struct starpu_task * task)
  81. {
  82. STARPU_ASSERT(task);
  83. STARPU_ASSERT(component);
  84. unsigned nimpl;
  85. int worker;
  86. for (nimpl = 0; nimpl < STARPU_MAXIMPLEMENTATIONS; nimpl++)
  87. for(worker = starpu_bitmap_first(component->workers_in_ctx);
  88. -1 != worker;
  89. worker = starpu_bitmap_next(component->workers_in_ctx, worker))
  90. if (starpu_worker_can_execute_task(worker, task, nimpl)
  91. || starpu_combined_worker_can_execute_task(worker, task, nimpl))
  92. return 1;
  93. return 0;
  94. }
  95. /* compute the average of transfer length for tasks on all workers
  96. * maybe this should be optimised if all workers are under the same numa component
  97. */
  98. double starpu_sched_component_transfer_length(struct starpu_sched_component * component, struct starpu_task * task)
  99. {
  100. STARPU_ASSERT(component && task);
  101. int nworkers = starpu_bitmap_cardinal(component->workers_in_ctx);
  102. double sum = 0.0;
  103. int worker;
  104. if(STARPU_SCHED_COMPONENT_IS_SINGLE_MEMORY_NODE(component))
  105. {
  106. unsigned memory_node = starpu_worker_get_memory_node(starpu_bitmap_first(component->workers_in_ctx));
  107. if(task->bundle)
  108. return starpu_task_bundle_expected_data_transfer_time(task->bundle,memory_node);
  109. else
  110. return starpu_task_expected_data_transfer_time(memory_node, task);
  111. }
  112. for(worker = starpu_bitmap_first(component->workers_in_ctx);
  113. worker != -1;
  114. worker = starpu_bitmap_next(component->workers_in_ctx, worker))
  115. {
  116. unsigned memory_node = starpu_worker_get_memory_node(worker);
  117. if(task->bundle)
  118. {
  119. sum += starpu_task_bundle_expected_data_transfer_time(task->bundle,memory_node);
  120. }
  121. else
  122. {
  123. sum += starpu_task_expected_data_transfer_time(memory_node, task);
  124. /* sum += starpu_task_expected_conversion_time(task, starpu_worker_get_perf_archtype(worker, component->tree->sched_ctx_id), impl ?)
  125. * I dont know what to do as we dont know what implementation would be used here...
  126. */
  127. }
  128. }
  129. return sum / nworkers;
  130. }
  131. /* This function can be called by components when they think that a prefetching request can be submitted.
  132. * For example, it is currently used by the MCT component to begin the prefetching on accelerators
  133. * on which it pushed tasks as soon as possible.
  134. */
  135. void starpu_sched_component_prefetch_on_node(struct starpu_sched_component * component, struct starpu_task * task)
  136. {
  137. if (starpu_get_prefetch_flag() && (!task->prefetched)
  138. && (component->properties >= STARPU_SCHED_COMPONENT_SINGLE_MEMORY_NODE))
  139. {
  140. int worker = starpu_bitmap_first(component->workers_in_ctx);
  141. unsigned memory_node = starpu_worker_get_memory_node(worker);
  142. starpu_prefetch_task_input_on_node(task, memory_node);
  143. task->prefetched = 1;
  144. }
  145. }
  146. /* remove all child
  147. * for all child of component, if child->parents[x] == component, set child->parents[x] to null
  148. * call component->deinit_data
  149. */
  150. void starpu_sched_component_destroy(struct starpu_sched_component *component)
  151. {
  152. STARPU_ASSERT(component);
  153. int i,j;
  154. for(i = 0; i < component->nchildren; i++)
  155. {
  156. struct starpu_sched_component * child = component->children[i];
  157. for(j = 0; j < child->nparents; j++)
  158. if(child->parents[j] == component)
  159. child->remove_parent(child,component);
  160. }
  161. while(component->nchildren != 0)
  162. component->remove_child(component, component->children[0]);
  163. for(i = 0; i < component->nparents; i++)
  164. {
  165. struct starpu_sched_component * parent = component->parents[i];
  166. for(j = 0; j < parent->nchildren; j++)
  167. if(parent->children[j] == component)
  168. parent->remove_child(parent,component);
  169. }
  170. while(component->nparents != 0)
  171. component->remove_parent(component, component->parents[0]);
  172. component->deinit_data(component);
  173. free(component->children);
  174. free(component->parents);
  175. starpu_bitmap_destroy(component->workers);
  176. starpu_bitmap_destroy(component->workers_in_ctx);
  177. free(component);
  178. }
  179. void starpu_sched_component_destroy_rec(struct starpu_sched_component * component)
  180. {
  181. if(component == NULL)
  182. return;
  183. int i = 0;
  184. while(i < component->nchildren)
  185. {
  186. if (starpu_sched_component_is_worker(component->children[i]))
  187. i++;
  188. else
  189. starpu_sched_component_destroy_rec(component->children[i]);
  190. }
  191. if (!starpu_sched_component_is_worker(component))
  192. starpu_sched_component_destroy(component);
  193. }
  194. void set_properties(struct starpu_sched_component * component)
  195. {
  196. STARPU_ASSERT(component);
  197. component->properties = 0;
  198. int worker = starpu_bitmap_first(component->workers_in_ctx);
  199. if (worker == -1)
  200. return;
  201. uint32_t first_worker = _starpu_get_worker_struct(worker)->worker_mask;
  202. unsigned first_memory_node = _starpu_get_worker_struct(worker)->memory_node;
  203. int is_homogeneous = 1;
  204. int is_all_same_component = 1;
  205. for(;
  206. worker != -1;
  207. worker = starpu_bitmap_next(component->workers_in_ctx, worker))
  208. {
  209. if(first_worker != _starpu_get_worker_struct(worker)->worker_mask)
  210. is_homogeneous = 0;
  211. if(first_memory_node != _starpu_get_worker_struct(worker)->memory_node)
  212. is_all_same_component = 0;
  213. }
  214. if(is_homogeneous)
  215. component->properties |= STARPU_SCHED_COMPONENT_HOMOGENEOUS;
  216. if(is_all_same_component)
  217. component->properties |= STARPU_SCHED_COMPONENT_SINGLE_MEMORY_NODE;
  218. }
  219. /* recursively set the component->workers member of component's subtree
  220. */
  221. void _starpu_sched_component_update_workers(struct starpu_sched_component * component)
  222. {
  223. STARPU_ASSERT(component);
  224. if(starpu_sched_component_is_worker(component))
  225. return;
  226. starpu_bitmap_unset_all(component->workers);
  227. int i;
  228. for(i = 0; i < component->nchildren; i++)
  229. {
  230. _starpu_sched_component_update_workers(component->children[i]);
  231. starpu_bitmap_or(component->workers, component->children[i]->workers);
  232. component->notify_change_workers(component);
  233. }
  234. }
  235. /* recursively set the component->workers_in_ctx in component's subtree
  236. */
  237. void _starpu_sched_component_update_workers_in_ctx(struct starpu_sched_component * component, unsigned sched_ctx_id)
  238. {
  239. STARPU_ASSERT(component);
  240. if(starpu_sched_component_is_worker(component))
  241. return;
  242. struct starpu_bitmap * workers_in_ctx = _starpu_get_worker_mask(sched_ctx_id);
  243. starpu_bitmap_unset_and(component->workers_in_ctx,component->workers, workers_in_ctx);
  244. int i,j;
  245. for(i = 0; i < component->nchildren; i++)
  246. {
  247. struct starpu_sched_component * child = component->children[i];
  248. _starpu_sched_component_update_workers_in_ctx(child, sched_ctx_id);
  249. for(j = 0; j < STARPU_NMAX_SCHED_CTXS; j++)
  250. if(child->parents[j] == component)
  251. {
  252. starpu_bitmap_or(component->workers_in_ctx, child->workers_in_ctx);
  253. break;
  254. }
  255. }
  256. set_properties(component);
  257. component->notify_change_workers(component);
  258. }
  259. /******************************************************************************
  260. * Scheduling Trees' helper functions *
  261. ******************************************************************************/
  262. struct starpu_bitmap * _starpu_get_worker_mask(unsigned sched_ctx_id)
  263. {
  264. STARPU_ASSERT(sched_ctx_id < STARPU_NMAX_SCHED_CTXS);
  265. struct starpu_sched_tree * t = starpu_sched_ctx_get_policy_data(sched_ctx_id);
  266. STARPU_ASSERT(t);
  267. return t->workers;
  268. }
  269. void starpu_sched_tree_update_workers_in_ctx(struct starpu_sched_tree * t)
  270. {
  271. STARPU_ASSERT(t);
  272. _starpu_sched_component_update_workers_in_ctx(t->root, t->sched_ctx_id);
  273. }
  274. void starpu_sched_tree_update_workers(struct starpu_sched_tree * t)
  275. {
  276. STARPU_ASSERT(t);
  277. _starpu_sched_component_update_workers(t->root);
  278. }
  279. /******************************************************************************
  280. * Scheduling Trees' Functions *
  281. * Most of them are used to define the starpu_sched_policy interface *
  282. ******************************************************************************/
  283. int starpu_sched_tree_push_task(struct starpu_task * task)
  284. {
  285. STARPU_ASSERT(task);
  286. unsigned sched_ctx_id = task->sched_ctx;
  287. struct starpu_sched_tree *tree = starpu_sched_ctx_get_policy_data(sched_ctx_id);
  288. int ret_val = starpu_sched_component_push_task(tree->root,task);
  289. return ret_val;
  290. }
  291. int starpu_sched_component_push_task(struct starpu_sched_component *component, struct starpu_task *task)
  292. {
  293. return component->push_task(component, task);
  294. }
  295. struct starpu_task * starpu_sched_tree_pop_task(unsigned sched_ctx)
  296. {
  297. int workerid = starpu_worker_get_id();
  298. struct starpu_sched_component * component = starpu_sched_component_worker_get(sched_ctx, workerid);
  299. /* _starpu_sched_component_lock_worker(workerid) is called by component->pull_task()
  300. */
  301. struct starpu_task * task = starpu_sched_component_pull_task(component);
  302. return task;
  303. }
  304. struct starpu_task * starpu_sched_component_pull_task(struct starpu_sched_component *component)
  305. {
  306. return component->pull_task(component);
  307. }
  308. void starpu_sched_tree_add_workers(unsigned sched_ctx_id, int *workerids, unsigned nworkers)
  309. {
  310. STARPU_ASSERT(sched_ctx_id < STARPU_NMAX_SCHED_CTXS);
  311. STARPU_ASSERT(workerids);
  312. struct starpu_sched_tree * t = starpu_sched_ctx_get_policy_data(sched_ctx_id);
  313. STARPU_PTHREAD_MUTEX_LOCK(&t->lock);
  314. _starpu_sched_component_lock_all_workers(sched_ctx_id);
  315. unsigned i;
  316. for(i = 0; i < nworkers; i++)
  317. starpu_bitmap_set(t->workers, workerids[i]);
  318. starpu_sched_tree_update_workers_in_ctx(t);
  319. _starpu_sched_component_unlock_all_workers(sched_ctx_id);
  320. STARPU_PTHREAD_MUTEX_UNLOCK(&t->lock);
  321. }
  322. void starpu_sched_tree_remove_workers(unsigned sched_ctx_id, int *workerids, unsigned nworkers)
  323. {
  324. STARPU_ASSERT(sched_ctx_id < STARPU_NMAX_SCHED_CTXS);
  325. STARPU_ASSERT(workerids);
  326. struct starpu_sched_tree * t = starpu_sched_ctx_get_policy_data(sched_ctx_id);
  327. STARPU_PTHREAD_MUTEX_LOCK(&t->lock);
  328. _starpu_sched_component_lock_all_workers(sched_ctx_id);
  329. unsigned i;
  330. for(i = 0; i < nworkers; i++)
  331. starpu_bitmap_unset(t->workers, workerids[i]);
  332. starpu_sched_tree_update_workers_in_ctx(t);
  333. _starpu_sched_component_unlock_all_workers(sched_ctx_id);
  334. STARPU_PTHREAD_MUTEX_UNLOCK(&t->lock);
  335. }
  336. static struct starpu_sched_tree *trees[STARPU_NMAX_SCHED_CTXS];
  337. struct starpu_sched_tree * starpu_sched_tree_create(unsigned sched_ctx_id)
  338. {
  339. STARPU_ASSERT(sched_ctx_id < STARPU_NMAX_SCHED_CTXS);
  340. STARPU_ASSERT(!trees[sched_ctx_id]);
  341. struct starpu_sched_tree * t = malloc(sizeof(*t));
  342. memset(t, 0, sizeof(*t));
  343. t->sched_ctx_id = sched_ctx_id;
  344. t->workers = starpu_bitmap_create();
  345. STARPU_PTHREAD_MUTEX_INIT(&t->lock,NULL);
  346. trees[sched_ctx_id] = t;
  347. return t;
  348. }
  349. void starpu_sched_tree_destroy(struct starpu_sched_tree * tree)
  350. {
  351. STARPU_ASSERT(tree);
  352. STARPU_ASSERT(trees[tree->sched_ctx_id] == tree);
  353. trees[tree->sched_ctx_id] = NULL;
  354. if(tree->root)
  355. starpu_sched_component_destroy_rec(tree->root);
  356. starpu_bitmap_destroy(tree->workers);
  357. STARPU_PTHREAD_MUTEX_DESTROY(&tree->lock);
  358. free(tree);
  359. }
  360. struct starpu_sched_tree * starpu_sched_tree_get(unsigned sched_ctx_id)
  361. {
  362. return trees[sched_ctx_id];
  363. }
  364. /******************************************************************************
  365. * Interface Functions for Generic Scheduling Components *
  366. ******************************************************************************/
  367. static void starpu_sched_component_add_child(struct starpu_sched_component* component, struct starpu_sched_component * child)
  368. {
  369. STARPU_ASSERT(component && child);
  370. STARPU_ASSERT(!starpu_sched_component_is_worker(component));
  371. int i;
  372. for(i = 0; i < component->nchildren; i++)
  373. {
  374. STARPU_ASSERT(component->children[i] != component);
  375. STARPU_ASSERT(component->children[i] != NULL);
  376. }
  377. component->children = realloc(component->children, sizeof(struct starpu_sched_component *) * (component->nchildren + 1));
  378. component->children[component->nchildren] = child;
  379. component->nchildren++;
  380. }
  381. static void starpu_sched_component_remove_child(struct starpu_sched_component * component, struct starpu_sched_component * child)
  382. {
  383. STARPU_ASSERT(component && child);
  384. STARPU_ASSERT(!starpu_sched_component_is_worker(component));
  385. int pos;
  386. for(pos = 0; pos < component->nchildren; pos++)
  387. if(component->children[pos] == child)
  388. break;
  389. STARPU_ASSERT(pos != component->nchildren);
  390. component->children[pos] = component->children[--component->nchildren];
  391. }
  392. static void starpu_sched_component_add_parent(struct starpu_sched_component* component, struct starpu_sched_component * parent)
  393. {
  394. STARPU_ASSERT(component && parent);
  395. int i;
  396. for(i = 0; i < component->nparents; i++)
  397. {
  398. STARPU_ASSERT(component->parents[i] != component);
  399. STARPU_ASSERT(component->parents[i] != NULL);
  400. }
  401. component->parents = realloc(component->parents, sizeof(struct starpu_sched_component *) * (component->nparents + 1));
  402. component->parents[component->nparents] = parent;
  403. component->nparents++;
  404. }
  405. static void starpu_sched_component_remove_parent(struct starpu_sched_component * component, struct starpu_sched_component * parent)
  406. {
  407. STARPU_ASSERT(component && parent);
  408. int pos;
  409. for(pos = 0; pos < component->nparents; pos++)
  410. if(component->parents[pos] == parent)
  411. break;
  412. STARPU_ASSERT(pos != component->nparents);
  413. component->parents[pos] = component->parents[--component->nparents];
  414. }
  415. /* default implementation for component->pull_task()
  416. * just perform a recursive call on parent
  417. */
  418. static struct starpu_task * starpu_sched_component_parents_pull_task(struct starpu_sched_component * component)
  419. {
  420. STARPU_ASSERT(component);
  421. struct starpu_task * task = NULL;
  422. int i;
  423. for(i=0; i < component->nparents; i++)
  424. {
  425. if(component->parents[i] == NULL)
  426. continue;
  427. else
  428. {
  429. task = starpu_sched_component_pull_task(component->parents[i]);
  430. if(task)
  431. break;
  432. }
  433. }
  434. return task;
  435. }
  436. /* The default implementation of the can_push function is a recursive call to its parents.
  437. * A personally-made can_push in a component (like in prio components) is necessary to catch
  438. * this recursive call somewhere, if the user wants to exploit it.
  439. */
  440. static int starpu_sched_component_can_push(struct starpu_sched_component * component)
  441. {
  442. STARPU_ASSERT(component);
  443. int ret = 0;
  444. if(component->nparents > 0)
  445. {
  446. int i;
  447. for(i=0; i < component->nparents; i++)
  448. {
  449. struct starpu_sched_component * parent = component->parents[i];
  450. if(parent != NULL)
  451. ret = parent->can_push(parent);
  452. if(ret)
  453. break;
  454. }
  455. }
  456. return ret;
  457. }
  458. /* A can_pull call will try to wake up one worker associated to the childs of the
  459. * component. It is currenly called by components which holds a queue (like fifo and prio
  460. * components) to signify its childs that a task has been pushed on its local queue.
  461. */
  462. static void starpu_sched_component_can_pull(struct starpu_sched_component * component)
  463. {
  464. STARPU_ASSERT(component);
  465. STARPU_ASSERT(!starpu_sched_component_is_worker(component));
  466. int i;
  467. for(i = 0; i < component->nchildren; i++)
  468. component->children[i]->can_pull(component->children[i]);
  469. }
  470. static double starpu_sched_component_estimated_load(struct starpu_sched_component * component)
  471. {
  472. double sum = 0.0;
  473. int i;
  474. for( i = 0; i < component->nchildren; i++)
  475. {
  476. struct starpu_sched_component * c = component->children[i];
  477. sum += c->estimated_load(c);
  478. }
  479. return sum;
  480. }
  481. static double starpu_sched_component_estimated_end_min(struct starpu_sched_component * component)
  482. {
  483. STARPU_ASSERT(component);
  484. double min = DBL_MAX;
  485. int i;
  486. for(i = 0; i < component->nchildren; i++)
  487. {
  488. double tmp = component->children[i]->estimated_end(component->children[i]);
  489. if(tmp < min)
  490. min = tmp;
  491. }
  492. return min;
  493. }
  494. static void take_component_and_does_nothing(struct starpu_sched_component * component STARPU_ATTRIBUTE_UNUSED)
  495. {
  496. }
  497. struct starpu_sched_component * starpu_sched_component_create(struct starpu_sched_tree *tree)
  498. {
  499. struct starpu_sched_component * component = malloc(sizeof(*component));
  500. memset(component,0,sizeof(*component));
  501. component->tree = tree;
  502. component->workers = starpu_bitmap_create();
  503. component->workers_in_ctx = starpu_bitmap_create();
  504. component->add_child = starpu_sched_component_add_child;
  505. component->remove_child = starpu_sched_component_remove_child;
  506. component->add_parent = starpu_sched_component_add_parent;
  507. component->remove_parent = starpu_sched_component_remove_parent;
  508. component->pull_task = starpu_sched_component_parents_pull_task;
  509. component->can_push = starpu_sched_component_can_push;
  510. component->can_pull = starpu_sched_component_can_pull;
  511. component->estimated_load = starpu_sched_component_estimated_load;
  512. component->estimated_end = starpu_sched_component_estimated_end_min;
  513. component->deinit_data = take_component_and_does_nothing;
  514. component->notify_change_workers = take_component_and_does_nothing;
  515. component->name = "sched";
  516. return component;
  517. }