simgrid.c 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  1. /* StarPU --- Runtime system for heterogeneous multicore architectures.
  2. *
  3. * Copyright (C) 2012-2017 Université de Bordeaux
  4. * Copyright (C) 2016 Inria
  5. * Copyright (C) 2016, 2017 CNRS
  6. *
  7. * StarPU is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as published by
  9. * the Free Software Foundation; either version 2.1 of the License, or (at
  10. * your option) any later version.
  11. *
  12. * StarPU is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. *
  16. * See the GNU Lesser General Public License in COPYING.LGPL for more details.
  17. */
  18. #include <starpu.h>
  19. #include <datawizard/memory_nodes.h>
  20. #include <common/config.h>
  21. #ifdef HAVE_UNISTD_H
  22. #include <unistd.h>
  23. #endif
  24. #include <core/perfmodel/perfmodel.h>
  25. #include <core/workers.h>
  26. #include <core/simgrid.h>
  27. #if defined(HAVE_SG_LINK_NAME) && (SIMGRID_VERSION_MAJOR >= 4 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR >= 13))
  28. #include <simgrid/simdag.h>
  29. #endif
  30. #ifdef STARPU_SIMGRID
  31. #ifdef HAVE_GETRLIMIT
  32. #include <sys/resource.h>
  33. #endif
  34. #include <simgrid/simix.h>
  35. #pragma weak starpu_main
  36. extern int starpu_main(int argc, char *argv[]);
  37. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 16)
  38. #pragma weak smpi_main
  39. extern int smpi_main(int (*realmain) (int argc, char *argv[]), int argc, char *argv[]);
  40. #endif
  41. #pragma weak _starpu_mpi_simgrid_init
  42. extern int _starpu_mpi_simgrid_init(int argc, char *argv[]);
  43. static int simgrid_started;
  44. static int runners_running;
  45. starpu_pthread_queue_t _starpu_simgrid_transfer_queue[STARPU_MAXNODES];
  46. static struct transfer_runner
  47. {
  48. struct transfer *first_transfer, *last_transfer;
  49. msg_sem_t sem;
  50. msg_process_t runner;
  51. } transfer_runner[STARPU_MAXNODES][STARPU_MAXNODES];
  52. static int transfer_execute(int argc STARPU_ATTRIBUTE_UNUSED, char *argv[] STARPU_ATTRIBUTE_UNUSED);
  53. starpu_pthread_queue_t _starpu_simgrid_task_queue[STARPU_NMAXWORKERS];
  54. static struct worker_runner
  55. {
  56. struct task *first_task, *last_task;
  57. msg_sem_t sem;
  58. msg_process_t runner;
  59. } worker_runner[STARPU_NMAXWORKERS];
  60. static int task_execute(int argc STARPU_ATTRIBUTE_UNUSED, char *argv[] STARPU_ATTRIBUTE_UNUSED);
  61. #ifdef HAVE_MSG_ENVIRONMENT_GET_ROUTING_ROOT
  62. #ifdef HAVE_MSG_GET_AS_BY_NAME
  63. msg_as_t _starpu_simgrid_get_as_by_name(const char *name)
  64. {
  65. return MSG_get_as_by_name(name);
  66. }
  67. #else /* HAVE_MSG_GET_AS_BY_NAME */
  68. static msg_as_t __starpu_simgrid_get_as_by_name(msg_as_t root, const char *name)
  69. {
  70. xbt_dict_t dict;
  71. xbt_dict_cursor_t cursor;
  72. const char *key;
  73. msg_as_t as, ret;
  74. dict = MSG_environment_as_get_routing_sons(root);
  75. xbt_dict_foreach(dict, cursor, key, as)
  76. {
  77. if (!strcmp(MSG_environment_as_get_name(as), name))
  78. return as;
  79. ret = __starpu_simgrid_get_as_by_name(as, name);
  80. if (ret)
  81. return ret;
  82. }
  83. return NULL;
  84. }
  85. msg_as_t _starpu_simgrid_get_as_by_name(const char *name)
  86. {
  87. return __starpu_simgrid_get_as_by_name(MSG_environment_get_routing_root(), name);
  88. }
  89. #endif /* HAVE_MSG_GET_AS_BY_NAME */
  90. #endif /* HAVE_MSG_ENVIRONMENT_GET_ROUTING_ROOT */
  91. int _starpu_simgrid_get_nbhosts(const char *prefix)
  92. {
  93. int ret;
  94. xbt_dynar_t hosts;
  95. unsigned i, nb;
  96. unsigned len = strlen(prefix);
  97. #ifdef HAVE_MSG_ENVIRONMENT_GET_ROUTING_ROOT
  98. char new_prefix[32];
  99. if (_starpu_simgrid_running_smpi())
  100. {
  101. char name[32];
  102. STARPU_ASSERT(starpu_mpi_world_rank);
  103. snprintf(name, sizeof(name), STARPU_MPI_AS_PREFIX"%u", starpu_mpi_world_rank());
  104. hosts = MSG_environment_as_get_hosts(_starpu_simgrid_get_as_by_name(name));
  105. snprintf(new_prefix, sizeof(new_prefix), "%s-%s", name, prefix);
  106. prefix = new_prefix;
  107. len = strlen(prefix);
  108. }
  109. else
  110. #endif /* HAVE_MSG_ENVIRONMENT_GET_ROUTING_ROOT */
  111. hosts = MSG_hosts_as_dynar();
  112. nb = xbt_dynar_length(hosts);
  113. ret = 0;
  114. for (i = 0; i < nb; i++)
  115. {
  116. const char *name;
  117. name = MSG_host_get_name(xbt_dynar_get_as(hosts, i, msg_host_t));
  118. if (!strncmp(name, prefix, len))
  119. ret++;
  120. }
  121. xbt_dynar_free(&hosts);
  122. return ret;
  123. }
  124. unsigned long long _starpu_simgrid_get_memsize(const char *prefix, unsigned devid)
  125. {
  126. char name[32];
  127. msg_host_t host;
  128. const char *memsize;
  129. snprintf(name, sizeof(name), "%s%u", prefix, devid);
  130. host = _starpu_simgrid_get_host_by_name(name);
  131. if (!host)
  132. return 0;
  133. if (!MSG_host_get_properties(host))
  134. return 0;
  135. memsize = MSG_host_get_property_value(host, "memsize");
  136. if (!memsize)
  137. return 0;
  138. return atoll(memsize);
  139. }
  140. msg_host_t _starpu_simgrid_get_host_by_name(const char *name)
  141. {
  142. if (_starpu_simgrid_running_smpi())
  143. {
  144. char mpiname[32];
  145. STARPU_ASSERT(starpu_mpi_world_rank);
  146. snprintf(mpiname, sizeof(mpiname), STARPU_MPI_AS_PREFIX"%d-%s", starpu_mpi_world_rank(), name);
  147. return MSG_get_host_by_name(mpiname);
  148. }
  149. else
  150. return MSG_get_host_by_name(name);
  151. }
  152. msg_host_t _starpu_simgrid_get_host_by_worker(struct _starpu_worker *worker)
  153. {
  154. char *prefix;
  155. char name[16];
  156. msg_host_t host;
  157. switch (worker->arch)
  158. {
  159. case STARPU_CPU_WORKER:
  160. prefix = "CPU";
  161. break;
  162. case STARPU_CUDA_WORKER:
  163. prefix = "CUDA";
  164. break;
  165. case STARPU_OPENCL_WORKER:
  166. prefix = "OpenCL";
  167. break;
  168. default:
  169. STARPU_ASSERT(0);
  170. }
  171. snprintf(name, sizeof(name), "%s%d", prefix, worker->devid);
  172. host = _starpu_simgrid_get_host_by_name(name);
  173. STARPU_ASSERT_MSG(host, "Could not find host %s!", name);
  174. return host;
  175. }
  176. /* Simgrid up to 3.15 would rename main into smpi_simulated_main_, and call that
  177. * from SMPI initialization
  178. * In case the MPI application didn't use smpicc to build the file containing
  179. * main(), but included our #define main starpu_main, try to cope by calling
  180. * starpu_main */
  181. int _starpu_smpi_simulated_main_(int argc, char *argv[])
  182. {
  183. if (!starpu_main)
  184. {
  185. _STARPU_ERROR("In simgrid mode, the file containing the main() function of this application needs to be compiled with starpu.h or starpu_simgrid_wrap.h included, to properly rename it into starpu_main\n");
  186. }
  187. return starpu_main(argc, argv);
  188. }
  189. int smpi_simulated_main_(int argc, char *argv[]) __attribute__((weak, alias("_starpu_smpi_simulated_main_")));
  190. /* This is used to start a non-MPI simgrid environment */
  191. static void start_simgrid(int *argc, char **argv)
  192. {
  193. char path[256];
  194. simgrid_started = 1;
  195. MSG_init(argc, argv);
  196. /* Simgrid uses tiny stacks by default. This comes unexpected to our users. */
  197. unsigned stack_size = 8192;
  198. #ifdef HAVE_GETRLIMIT
  199. struct rlimit rlim;
  200. if (getrlimit(RLIMIT_STACK, &rlim) == 0 && rlim.rlim_cur != 0 && rlim.rlim_cur != RLIM_INFINITY)
  201. stack_size = rlim.rlim_cur / 1024;
  202. #endif
  203. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 13)
  204. extern xbt_cfg_t _sg_cfg_set;
  205. xbt_cfg_set_int(_sg_cfg_set, "contexts/stack_size", stack_size);
  206. #else
  207. xbt_cfg_set_int("contexts/stack-size", stack_size);
  208. #endif
  209. /* Load XML platform */
  210. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 13)
  211. _starpu_simgrid_get_platform_path(3, path, sizeof(path));
  212. #else
  213. _starpu_simgrid_get_platform_path(4, path, sizeof(path));
  214. #endif
  215. MSG_create_environment(path);
  216. }
  217. struct main_args
  218. {
  219. int argc;
  220. char **argv;
  221. };
  222. static int main_ret;
  223. int do_starpu_main(int argc, char *argv[])
  224. {
  225. /* FIXME: Ugly work-around for bug in simgrid: the MPI context is not properly set at MSG process startup */
  226. MSG_process_sleep(0.000001);
  227. main_ret = starpu_main(argc, argv);
  228. return main_ret;
  229. }
  230. /* We need it only when using smpi */
  231. #pragma weak smpi_process_get_user_data
  232. extern void *smpi_process_get_user_data();
  233. /* This is hopefully called before the application and simgrid */
  234. #undef main
  235. #pragma weak main
  236. int main(int argc, char **argv)
  237. {
  238. if (_starpu_simgrid_running_smpi())
  239. {
  240. if (!smpi_process_get_user_data)
  241. {
  242. _STARPU_ERROR("Your version of simgrid does not provide smpi_process_get_user_data, we can not continue without it\n");
  243. }
  244. #if SIMGRID_VERSION_MAJOR > 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR >= 16)
  245. /* Recent versions of simgrid dlopen() us, so we don't need to
  246. * do circumvolutions, just init MPI early and run the application's main */
  247. return _starpu_mpi_simgrid_init(argc, argv);
  248. #else
  249. /* Oops, we are running old SMPI, let it start Simgrid, and we'll
  250. * take back hand in _starpu_simgrid_init from starpu_init() */
  251. return smpi_main(_starpu_mpi_simgrid_init, argc, argv);
  252. #endif
  253. }
  254. /* Managed to catch application's main, initialize simgrid first */
  255. start_simgrid(&argc, argv);
  256. /* Create a simgrid process for main */
  257. char **argv_cpy;
  258. _STARPU_MALLOC(argv_cpy, argc * sizeof(char*));
  259. int i;
  260. for (i = 0; i < argc; i++)
  261. argv_cpy[i] = strdup(argv[i]);
  262. void **tsd;
  263. _STARPU_CALLOC(tsd, MAX_TSD+1, sizeof(void*));
  264. /* Run the application in a separate thread */
  265. MSG_process_create_with_arguments("main", &do_starpu_main, tsd, MSG_get_host_by_name("MAIN"), argc, argv_cpy);
  266. /* And run maestro in the main thread */
  267. MSG_main();
  268. return main_ret;
  269. }
  270. #ifdef HAVE_MSG_PROCESS_ATTACH
  271. static void maestro(void *data STARPU_ATTRIBUTE_UNUSED)
  272. {
  273. MSG_main();
  274. }
  275. #endif
  276. /* This is called early from starpu_init, so thread functions etc. can work */
  277. void _starpu_simgrid_init_early(int *argc STARPU_ATTRIBUTE_UNUSED, char ***argv STARPU_ATTRIBUTE_UNUSED)
  278. {
  279. #ifdef HAVE_MSG_PROCESS_ATTACH
  280. if (!simgrid_started && !_starpu_simgrid_running_smpi())
  281. {
  282. /* "Cannot create_maestro with this ContextFactory.
  283. * Try using --cfg=contexts/factory:thread instead."
  284. * See https://github.com/simgrid/simgrid/issues/141 */
  285. _STARPU_DISP("Warning: In simgrid mode, the file containing the main() function of this application should to be compiled with starpu.h or starpu_simgrid_wrap.h included, to properly rename it into starpu_main to avoid having to use --cfg=contexts/factory:thread which reduces performance\n");
  286. #if SIMGRID_VERSION_MAJOR > 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR >= 14) /* Only recent versions of simgrid support setting xbt_cfg_set_string before starting simgrid */
  287. xbt_cfg_set_string("contexts/factory", "thread");
  288. #endif
  289. /* We didn't catch application's main. */
  290. /* Start maestro as a separate thread */
  291. SIMIX_set_maestro(maestro, NULL);
  292. /* Initialize simgrid */
  293. start_simgrid(argc, *argv);
  294. /* And attach the main thread to the main simgrid process */
  295. void **tsd;
  296. _STARPU_CALLOC(tsd, MAX_TSD+1, sizeof(void*));
  297. MSG_process_attach("main", tsd, MSG_get_host_by_name("MAIN"), NULL);
  298. simgrid_started = 2;
  299. }
  300. #endif
  301. if (!simgrid_started && !starpu_main && !_starpu_simgrid_running_smpi())
  302. {
  303. /* Oops, we don't have MSG_process_attach and didn't catch the
  304. * 'main' symbol, there is no way for us */
  305. _STARPU_ERROR("In simgrid mode, the file containing the main() function of this application needs to be compiled with starpu.h or starpu_simgrid_wrap.h included, to properly rename it into starpu_main\n");
  306. }
  307. if (_starpu_simgrid_running_smpi())
  308. {
  309. #ifndef STARPU_STATIC_ONLY
  310. _STARPU_ERROR("Simgrid currently does not support privatization for dynamically-linked libraries in SMPI. Please reconfigure and build StarPU with --disable-shared");
  311. #endif
  312. void **tsd;
  313. _STARPU_CALLOC(tsd, MAX_TSD+1, sizeof(void*));
  314. MSG_process_set_data(MSG_process_self(), tsd);
  315. }
  316. unsigned i;
  317. for (i = 0; i < STARPU_MAXNODES; i++)
  318. starpu_pthread_queue_init(&_starpu_simgrid_transfer_queue[i]);
  319. for (i = 0; i < STARPU_NMAXWORKERS; i++)
  320. starpu_pthread_queue_init(&_starpu_simgrid_task_queue[i]);
  321. }
  322. /* This is called late from starpu_init, to start task executors */
  323. void _starpu_simgrid_init(void)
  324. {
  325. unsigned i;
  326. runners_running = 1;
  327. for (i = 0; i < starpu_worker_get_count(); i++)
  328. {
  329. char s[32];
  330. snprintf(s, sizeof(s), "worker %u runner", i);
  331. void **tsd;
  332. _STARPU_CALLOC(tsd, MAX_TSD+1, sizeof(void*));
  333. worker_runner[i].sem = MSG_sem_init(0);
  334. tsd[0] = (void*)(uintptr_t) i;
  335. worker_runner[i].runner = MSG_process_create_with_arguments(s, task_execute, tsd, _starpu_simgrid_get_host_by_worker(_starpu_get_worker_struct(i)), 0, NULL);
  336. }
  337. }
  338. void _starpu_simgrid_deinit(void)
  339. {
  340. unsigned i, j;
  341. runners_running = 0;
  342. for (i = 0; i < STARPU_MAXNODES; i++)
  343. {
  344. for (j = 0; j < STARPU_MAXNODES; j++)
  345. {
  346. struct transfer_runner *t = &transfer_runner[i][j];
  347. if (t->runner)
  348. {
  349. MSG_sem_release(t->sem);
  350. #if SIMGRID_VERSION_MAJOR > 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR >= 14)
  351. MSG_process_join(t->runner, 1000000);
  352. #else
  353. MSG_process_sleep(1);
  354. #endif
  355. STARPU_ASSERT(t->first_transfer == NULL);
  356. STARPU_ASSERT(t->last_transfer == NULL);
  357. MSG_sem_destroy(t->sem);
  358. }
  359. }
  360. /* FIXME: queue not empty at this point, needs proper unregistration */
  361. /* starpu_pthread_queue_destroy(&_starpu_simgrid_transfer_queue[i]); */
  362. }
  363. for (i = 0; i < starpu_worker_get_count(); i++)
  364. {
  365. struct worker_runner *w = &worker_runner[i];
  366. MSG_sem_release(w->sem);
  367. #if SIMGRID_VERSION_MAJOR > 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR >= 14)
  368. MSG_process_join(w->runner, 1000000);
  369. #else
  370. MSG_process_sleep(1);
  371. #endif
  372. STARPU_ASSERT(w->first_task == NULL);
  373. STARPU_ASSERT(w->last_task == NULL);
  374. MSG_sem_destroy(w->sem);
  375. starpu_pthread_queue_destroy(&_starpu_simgrid_task_queue[i]);
  376. }
  377. #ifdef HAVE_MSG_PROCESS_ATTACH
  378. if (simgrid_started == 2)
  379. {
  380. /* Started with MSG_process_attach, now detach */
  381. MSG_process_detach();
  382. simgrid_started = 0;
  383. }
  384. #endif
  385. }
  386. /*
  387. * Tasks
  388. */
  389. struct task
  390. {
  391. msg_task_t task;
  392. /* communication termination signalization */
  393. unsigned *finished;
  394. /* Next task on this worker */
  395. struct task *next;
  396. };
  397. /* Actually execute the task. */
  398. static int task_execute(int argc STARPU_ATTRIBUTE_UNUSED, char *argv[] STARPU_ATTRIBUTE_UNUSED)
  399. {
  400. /* FIXME: Ugly work-around for bug in simgrid: the MPI context is not properly set at MSG process startup */
  401. MSG_process_sleep(0.000001);
  402. unsigned workerid = (uintptr_t) STARPU_PTHREAD_GETSPECIFIC(0);
  403. struct worker_runner *w = &worker_runner[workerid];
  404. _STARPU_DEBUG("worker runner %u started\n", workerid);
  405. while (1)
  406. {
  407. struct task *task;
  408. MSG_sem_acquire(w->sem);
  409. if (!runners_running)
  410. break;
  411. task = w->first_task;
  412. w->first_task = task->next;
  413. if (w->last_task == task)
  414. w->last_task = NULL;
  415. _STARPU_DEBUG("task %p started\n", task);
  416. MSG_task_execute(task->task);
  417. MSG_task_destroy(task->task);
  418. _STARPU_DEBUG("task %p finished\n", task);
  419. *task->finished = 1;
  420. /* The worker which started this task may be sleeping out of tasks, wake it */
  421. _starpu_wake_worker_relax(workerid);
  422. free(task);
  423. }
  424. _STARPU_DEBUG("worker %u stopped\n", workerid);
  425. return 0;
  426. }
  427. /* Wait for completion of all asynchronous tasks for this worker */
  428. void _starpu_simgrid_wait_tasks(int workerid)
  429. {
  430. struct task *task = worker_runner[workerid].last_task;
  431. if (!task)
  432. return;
  433. unsigned *finished = task->finished;
  434. starpu_pthread_wait_t wait;
  435. starpu_pthread_wait_init(&wait);
  436. starpu_pthread_queue_register(&wait, &_starpu_simgrid_task_queue[workerid]);
  437. while(1)
  438. {
  439. starpu_pthread_wait_reset(&wait);
  440. if (*finished)
  441. break;
  442. starpu_pthread_wait_wait(&wait);
  443. }
  444. starpu_pthread_queue_unregister(&wait, &_starpu_simgrid_task_queue[workerid]);
  445. starpu_pthread_wait_destroy(&wait);
  446. }
  447. /* Task execution submitted by StarPU */
  448. void _starpu_simgrid_submit_job(int workerid, struct _starpu_job *j, struct starpu_perfmodel_arch* perf_arch, double length, unsigned *finished)
  449. {
  450. struct starpu_task *starpu_task = j->task;
  451. msg_task_t simgrid_task;
  452. if (j->internal)
  453. /* This is not useful to include in simulation (and probably
  454. * doesn't have a perfmodel anyway) */
  455. return;
  456. if (isnan(length))
  457. {
  458. length = starpu_task_expected_length(starpu_task, perf_arch, j->nimpl);
  459. STARPU_ASSERT_MSG(!_STARPU_IS_ZERO(length) && !isnan(length),
  460. "Codelet %s does not have a perfmodel, or is not calibrated enough, please re-run in non-simgrid mode until it is calibrated",
  461. _starpu_job_get_model_name(j));
  462. }
  463. simgrid_task = MSG_task_create(_starpu_job_get_task_name(j),
  464. #ifdef HAVE_MSG_HOST_GET_SPEED
  465. length/1000000.0*MSG_host_get_speed(MSG_host_self()),
  466. #else
  467. length/1000000.0*MSG_get_host_speed(MSG_host_self()),
  468. #endif
  469. 0, NULL);
  470. if (finished == NULL)
  471. {
  472. /* Synchronous execution */
  473. /* First wait for previous tasks */
  474. _starpu_simgrid_wait_tasks(workerid);
  475. MSG_task_execute(simgrid_task);
  476. MSG_task_destroy(simgrid_task);
  477. }
  478. else
  479. {
  480. /* Asynchronous execution */
  481. struct task *task;
  482. struct worker_runner *w = &worker_runner[workerid];
  483. _STARPU_MALLOC(task, sizeof(*task));
  484. task->task = simgrid_task;
  485. task->finished = finished;
  486. *finished = 0;
  487. task->next = NULL;
  488. /* Sleep 10µs for the GPU task queueing */
  489. if (_starpu_simgrid_queue_malloc_cost())
  490. MSG_process_sleep(0.000010);
  491. if (w->last_task)
  492. {
  493. /* Already running a task, queue */
  494. w->last_task->next = task;
  495. w->last_task = task;
  496. }
  497. else
  498. {
  499. STARPU_ASSERT(!w->first_task);
  500. w->first_task = task;
  501. w->last_task = task;
  502. }
  503. MSG_sem_release(w->sem);
  504. }
  505. }
  506. /*
  507. * Transfers
  508. */
  509. /* Note: simgrid is not parallel, so there is no need to hold locks for management of transfers. */
  510. LIST_TYPE(transfer,
  511. msg_task_t task;
  512. int src_node;
  513. int dst_node;
  514. int run_node;
  515. /* communication termination signalization */
  516. unsigned *finished;
  517. /* transfers which wait for this transfer */
  518. struct transfer **wake;
  519. unsigned nwake;
  520. /* Number of transfers that this transfer waits for */
  521. unsigned nwait;
  522. /* Next transfer on this stream */
  523. struct transfer *next;
  524. )
  525. struct transfer_list pending;
  526. /* Tell for two transfers whether they should be handled in sequence */
  527. static int transfers_are_sequential(struct transfer *new_transfer, struct transfer *old_transfer)
  528. {
  529. int new_is_cuda STARPU_ATTRIBUTE_UNUSED, old_is_cuda STARPU_ATTRIBUTE_UNUSED;
  530. int new_is_opencl STARPU_ATTRIBUTE_UNUSED, old_is_opencl STARPU_ATTRIBUTE_UNUSED;
  531. int new_is_gpu_gpu, old_is_gpu_gpu;
  532. new_is_cuda = starpu_node_get_kind(new_transfer->src_node) == STARPU_CUDA_RAM;
  533. new_is_cuda |= starpu_node_get_kind(new_transfer->dst_node) == STARPU_CUDA_RAM;
  534. old_is_cuda = starpu_node_get_kind(old_transfer->src_node) == STARPU_CUDA_RAM;
  535. old_is_cuda |= starpu_node_get_kind(old_transfer->dst_node) == STARPU_CUDA_RAM;
  536. new_is_opencl = starpu_node_get_kind(new_transfer->src_node) == STARPU_OPENCL_RAM;
  537. new_is_opencl |= starpu_node_get_kind(new_transfer->dst_node) == STARPU_OPENCL_RAM;
  538. old_is_opencl = starpu_node_get_kind(old_transfer->src_node) == STARPU_OPENCL_RAM;
  539. old_is_opencl |= starpu_node_get_kind(old_transfer->dst_node) == STARPU_OPENCL_RAM;
  540. new_is_gpu_gpu = new_transfer->src_node && new_transfer->dst_node;
  541. old_is_gpu_gpu = old_transfer->src_node && old_transfer->dst_node;
  542. /* We ignore cuda-opencl transfers, they can not happen */
  543. STARPU_ASSERT(!((new_is_cuda && old_is_opencl) || (old_is_cuda && new_is_opencl)));
  544. /* The following constraints have been observed with CUDA alone */
  545. /* Same source/destination, sequential */
  546. if (new_transfer->src_node == old_transfer->src_node && new_transfer->dst_node == old_transfer->dst_node)
  547. return 1;
  548. /* Crossed GPU-GPU, sequential */
  549. if (new_is_gpu_gpu
  550. && new_transfer->src_node == old_transfer->dst_node
  551. && old_transfer->src_node == new_transfer->dst_node)
  552. return 1;
  553. /* GPU-GPU transfers are sequential with any RAM->GPU transfer */
  554. if (new_is_gpu_gpu
  555. && old_transfer->dst_node == new_transfer->src_node
  556. && old_transfer->dst_node == new_transfer->dst_node)
  557. return 1;
  558. if (old_is_gpu_gpu
  559. && new_transfer->dst_node == old_transfer->src_node
  560. && new_transfer->dst_node == old_transfer->dst_node)
  561. return 1;
  562. /* StarPU's constraint on CUDA transfers is using one stream per
  563. * source/destination pair, which is already handled above */
  564. return 0;
  565. }
  566. static void transfer_queue(struct transfer *transfer)
  567. {
  568. unsigned src = transfer->src_node;
  569. unsigned dst = transfer->dst_node;
  570. struct transfer_runner *t = &transfer_runner[src][dst];
  571. if (!t->runner)
  572. {
  573. /* No runner yet, start it */
  574. static starpu_pthread_mutex_t mutex; /* process_create may yield */
  575. STARPU_PTHREAD_MUTEX_LOCK(&mutex);
  576. if (!t->runner)
  577. {
  578. char s[64];
  579. snprintf(s, sizeof(s), "transfer %u-%u runner", src, dst);
  580. void **tsd;
  581. _STARPU_CALLOC(tsd, MAX_TSD+1, sizeof(void*));
  582. tsd[0] = (void*)(uintptr_t)((src<<16) + dst);
  583. t->runner = MSG_process_create_with_arguments(s, transfer_execute, tsd, _starpu_simgrid_get_memnode_host(src), 0, NULL);
  584. t->sem = MSG_sem_init(0);
  585. }
  586. STARPU_PTHREAD_MUTEX_UNLOCK(&mutex);
  587. }
  588. if (t->last_transfer)
  589. {
  590. /* Already running a transfer, queue */
  591. t->last_transfer->next = transfer;
  592. t->last_transfer = transfer;
  593. }
  594. else
  595. {
  596. STARPU_ASSERT(!t->first_transfer);
  597. t->first_transfer = transfer;
  598. t->last_transfer = transfer;
  599. }
  600. MSG_sem_release(t->sem);
  601. }
  602. /* Actually execute the transfer, and then start transfers waiting for this one. */
  603. static int transfer_execute(int argc STARPU_ATTRIBUTE_UNUSED, char *argv[] STARPU_ATTRIBUTE_UNUSED)
  604. {
  605. /* FIXME: Ugly work-around for bug in simgrid: the MPI context is not properly set at MSG process startup */
  606. MSG_process_sleep(0.000001);
  607. unsigned src_dst = (uintptr_t) STARPU_PTHREAD_GETSPECIFIC(0);
  608. unsigned src = src_dst >> 16;
  609. unsigned dst = src_dst & 0xffff;
  610. struct transfer_runner *t = &transfer_runner[src][dst];
  611. _STARPU_DEBUG("transfer runner %u-%u started\n", src, dst);
  612. while (1)
  613. {
  614. struct transfer *transfer;
  615. MSG_sem_acquire(t->sem);
  616. if (!runners_running)
  617. break;
  618. transfer = t->first_transfer;
  619. t->first_transfer = transfer->next;
  620. if (t->last_transfer == transfer)
  621. t->last_transfer = NULL;
  622. if (transfer->task)
  623. {
  624. _STARPU_DEBUG("transfer %p started\n", transfer);
  625. MSG_task_execute(transfer->task);
  626. MSG_task_destroy(transfer->task);
  627. _STARPU_DEBUG("transfer %p finished\n", transfer);
  628. }
  629. *transfer->finished = 1;
  630. transfer_list_erase(&pending, transfer);
  631. /* The workers which started this request may be sleeping out of tasks, wake it */
  632. _starpu_wake_all_blocked_workers_on_node(transfer->run_node);
  633. unsigned i;
  634. /* Wake transfers waiting for my termination */
  635. /* Note: due to possible preemption inside process_create, the array
  636. * may grow while doing this */
  637. for (i = 0; i < transfer->nwake; i++)
  638. {
  639. struct transfer *wake = transfer->wake[i];
  640. STARPU_ASSERT(wake->nwait > 0);
  641. wake->nwait--;
  642. if (!wake->nwait)
  643. {
  644. _STARPU_DEBUG("triggering transfer %p\n", wake);
  645. transfer_queue(wake);
  646. }
  647. }
  648. free(transfer->wake);
  649. free(transfer);
  650. }
  651. return 0;
  652. }
  653. /* Look for sequentialization between this transfer and pending transfers, and submit this one */
  654. static void transfer_submit(struct transfer *transfer)
  655. {
  656. struct transfer *old;
  657. for (old = transfer_list_begin(&pending);
  658. old != transfer_list_end(&pending);
  659. old = transfer_list_next(old))
  660. {
  661. if (transfers_are_sequential(transfer, old))
  662. {
  663. _STARPU_DEBUG("transfer %p(%d->%d) waits for %p(%d->%d)\n",
  664. transfer, transfer->src_node, transfer->dst_node,
  665. old, old->src_node, old->dst_node);
  666. /* Make new wait for the old */
  667. transfer->nwait++;
  668. /* Make old wake the new */
  669. _STARPU_REALLOC(old->wake, (old->nwake + 1) * sizeof(old->wake));
  670. old->wake[old->nwake] = transfer;
  671. old->nwake++;
  672. }
  673. }
  674. transfer_list_push_front(&pending, transfer);
  675. if (!transfer->nwait)
  676. {
  677. _STARPU_DEBUG("transfer %p waits for nobody, starting\n", transfer);
  678. transfer_queue(transfer);
  679. }
  680. }
  681. int _starpu_simgrid_wait_transfer_event(union _starpu_async_channel_event *event)
  682. {
  683. /* this is not associated to a request so it's synchronous */
  684. starpu_pthread_wait_t wait;
  685. starpu_pthread_wait_init(&wait);
  686. starpu_pthread_queue_register(&wait, event->queue);
  687. while(1)
  688. {
  689. starpu_pthread_wait_reset(&wait);
  690. if (event->finished)
  691. break;
  692. starpu_pthread_wait_wait(&wait);
  693. }
  694. starpu_pthread_queue_unregister(&wait, event->queue);
  695. starpu_pthread_wait_destroy(&wait);
  696. return 0;
  697. }
  698. int _starpu_simgrid_test_transfer_event(union _starpu_async_channel_event *event)
  699. {
  700. return event->finished;
  701. }
  702. /* Wait for completion of all transfers */
  703. static void _starpu_simgrid_wait_transfers(void)
  704. {
  705. unsigned finished = 0;
  706. struct transfer *sync = transfer_new();
  707. struct transfer *cur;
  708. sync->task = NULL;
  709. sync->finished = &finished;
  710. sync->src_node = STARPU_MAIN_RAM;
  711. sync->dst_node = STARPU_MAIN_RAM;
  712. sync->run_node = STARPU_MAIN_RAM;
  713. sync->wake = NULL;
  714. sync->nwake = 0;
  715. sync->nwait = 0;
  716. sync->next = NULL;
  717. for (cur = transfer_list_begin(&pending);
  718. cur != transfer_list_end(&pending);
  719. cur = transfer_list_next(cur))
  720. {
  721. sync->nwait++;
  722. _STARPU_REALLOC(cur->wake, (cur->nwake + 1) * sizeof(cur->wake));
  723. cur->wake[cur->nwake] = sync;
  724. cur->nwake++;
  725. }
  726. if (sync->nwait == 0)
  727. {
  728. /* No transfer to wait for */
  729. free(sync);
  730. return;
  731. }
  732. /* Push synchronization pseudo-transfer */
  733. transfer_list_push_front(&pending, sync);
  734. /* And wait for it */
  735. starpu_pthread_wait_t wait;
  736. starpu_pthread_wait_init(&wait);
  737. starpu_pthread_queue_register(&wait, &_starpu_simgrid_transfer_queue[STARPU_MAIN_RAM]);
  738. while(1)
  739. {
  740. starpu_pthread_wait_reset(&wait);
  741. if (finished)
  742. break;
  743. starpu_pthread_wait_wait(&wait);
  744. }
  745. starpu_pthread_queue_unregister(&wait, &_starpu_simgrid_transfer_queue[STARPU_MAIN_RAM]);
  746. starpu_pthread_wait_destroy(&wait);
  747. }
  748. /* Data transfer issued by StarPU */
  749. int _starpu_simgrid_transfer(size_t size, unsigned src_node, unsigned dst_node, struct _starpu_data_request *req)
  750. {
  751. /* Simgrid does not like 0-bytes transfers */
  752. if (!size)
  753. return 0;
  754. msg_task_t task;
  755. msg_host_t *hosts;
  756. double *computation;
  757. double *communication;
  758. union _starpu_async_channel_event *event, myevent;
  759. _STARPU_CALLOC(hosts, 2, sizeof(*hosts));
  760. _STARPU_CALLOC(computation, 2, sizeof(*computation));
  761. _STARPU_CALLOC(communication, 4, sizeof(*communication));
  762. hosts[0] = _starpu_simgrid_memory_node_get_host(src_node);
  763. hosts[1] = _starpu_simgrid_memory_node_get_host(dst_node);
  764. STARPU_ASSERT(hosts[0] != hosts[1]);
  765. communication[1] = size;
  766. task = MSG_parallel_task_create("copy", 2, hosts, computation, communication, NULL);
  767. struct transfer *transfer = transfer_new();
  768. _STARPU_DEBUG("creating transfer %p for %lu bytes\n", transfer, (unsigned long) size);
  769. transfer->task = task;
  770. transfer->src_node = src_node;
  771. transfer->dst_node = dst_node;
  772. transfer->run_node = _starpu_memory_node_get_local_key();
  773. if (req)
  774. event = &req->async_channel.event;
  775. else
  776. event = &myevent;
  777. event->finished = 0;
  778. transfer->finished = &event->finished;
  779. event->queue = &_starpu_simgrid_transfer_queue[transfer->run_node];
  780. transfer->wake = NULL;
  781. transfer->nwake = 0;
  782. transfer->nwait = 0;
  783. transfer->next = NULL;
  784. if (req)
  785. _STARPU_TRACE_START_DRIVER_COPY_ASYNC(src_node, dst_node);
  786. /* Sleep 10µs for the GPU transfer queueing */
  787. if (_starpu_simgrid_queue_malloc_cost())
  788. MSG_process_sleep(0.000010);
  789. transfer_submit(transfer);
  790. /* Note: from here, transfer might be already freed */
  791. if (req)
  792. {
  793. _STARPU_TRACE_END_DRIVER_COPY_ASYNC(src_node, dst_node);
  794. _STARPU_TRACE_DATA_COPY(src_node, dst_node, size);
  795. return -EAGAIN;
  796. }
  797. else
  798. {
  799. /* this is not associated to a request so it's synchronous */
  800. _starpu_simgrid_wait_transfer_event(event);
  801. return 0;
  802. }
  803. }
  804. /* Sync all GPUs (used on CUDA Free, typically) */
  805. void _starpu_simgrid_sync_gpus(void)
  806. {
  807. _starpu_simgrid_wait_transfers();
  808. }
  809. int
  810. _starpu_simgrid_thread_start(int argc STARPU_ATTRIBUTE_UNUSED, char *argv[])
  811. {
  812. void *(*f)(void*) = (void*) (uintptr_t) strtol(argv[0], NULL, 16);
  813. void *arg = (void*) (uintptr_t) strtol(argv[1], NULL, 16);
  814. /* FIXME: Ugly work-around for bug in simgrid: the MPI context is not properly set at MSG process startup */
  815. MSG_process_sleep(0.000001);
  816. /* _args is freed with process context */
  817. f(arg);
  818. return 0;
  819. }
  820. msg_host_t
  821. _starpu_simgrid_get_memnode_host(unsigned node)
  822. {
  823. const char *fmt;
  824. char name[16];
  825. switch (starpu_node_get_kind(node))
  826. {
  827. case STARPU_CPU_RAM:
  828. fmt = "RAM";
  829. break;
  830. case STARPU_CUDA_RAM:
  831. fmt = "CUDA%u";
  832. break;
  833. case STARPU_OPENCL_RAM:
  834. fmt = "OpenCL%u";
  835. break;
  836. case STARPU_DISK_RAM:
  837. fmt = "DISK%u";
  838. break;
  839. default:
  840. STARPU_ABORT();
  841. break;
  842. }
  843. snprintf(name, sizeof(name), fmt, _starpu_memory_node_get_devid(node));
  844. return _starpu_simgrid_get_host_by_name(name);
  845. }
  846. void _starpu_simgrid_count_ngpus(void)
  847. {
  848. #if defined(HAVE_SG_LINK_NAME) && (SIMGRID_VERSION_MAJOR >= 4 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR >= 13))
  849. unsigned src, dst;
  850. msg_host_t ramhost = _starpu_simgrid_get_host_by_name("RAM");
  851. /* For each pair of memory nodes, get the route */
  852. for (src = 1; src < STARPU_MAXNODES; src++)
  853. for (dst = 1; dst < STARPU_MAXNODES; dst++)
  854. {
  855. int busid;
  856. msg_host_t srchost, dsthost;
  857. #ifdef HAVE_SG_HOST_ROUTE
  858. xbt_dynar_t route_dynar = xbt_dynar_new(sizeof(SD_link_t), NULL);
  859. SD_link_t *route;
  860. #else
  861. const SD_link_t *route;
  862. #endif
  863. int i, routesize;
  864. int through;
  865. unsigned src2;
  866. unsigned ngpus;
  867. const char *name;
  868. if (dst == src)
  869. continue;
  870. busid = starpu_bus_get_id(src, dst);
  871. if (busid == -1)
  872. continue;
  873. srchost = _starpu_simgrid_get_memnode_host(src);
  874. dsthost = _starpu_simgrid_get_memnode_host(dst);
  875. #ifdef HAVE_SG_HOST_ROUTE
  876. sg_host_route(srchost, dsthost, route_dynar);
  877. routesize = xbt_dynar_length(route_dynar);
  878. route = xbt_dynar_to_array(route_dynar);
  879. #else
  880. routesize = SD_route_get_size(srchost, dsthost);
  881. route = SD_route_get_list(srchost, dsthost);
  882. #endif
  883. /* If it goes through "Host", do not care, there is no
  884. * direct transfer support */
  885. for (i = 0; i < routesize; i++)
  886. if (!strcmp(sg_link_name(route[i]), "Host"))
  887. break;
  888. if (i < routesize)
  889. continue;
  890. /* Get the PCI bridge between down and up links */
  891. through = -1;
  892. for (i = 0; i < routesize; i++)
  893. {
  894. name = sg_link_name(route[i]);
  895. size_t len = strlen(name);
  896. if (!strcmp(" through", name+len-8))
  897. through = i;
  898. else if (!strcmp(" up", name+len-3))
  899. break;
  900. }
  901. /* Didn't find it ?! */
  902. if (through == -1)
  903. {
  904. _STARPU_DEBUG("Didn't find through-link for %d->%d\n", src, dst);
  905. continue;
  906. }
  907. name = sg_link_name(route[through]);
  908. /*
  909. * count how many direct routes go through it between
  910. * GPUs and RAM
  911. */
  912. ngpus = 0;
  913. for (src2 = 1; src2 < STARPU_MAXNODES; src2++)
  914. {
  915. if (starpu_bus_get_id(src2, STARPU_MAIN_RAM) == -1)
  916. continue;
  917. msg_host_t srchost2 = _starpu_simgrid_get_memnode_host(src2);
  918. int routesize2;
  919. #ifdef HAVE_SG_HOST_ROUTE
  920. xbt_dynar_t route_dynar2 = xbt_dynar_new(sizeof(SD_link_t), NULL);
  921. SD_link_t *route2;
  922. sg_host_route(srchost2, ramhost, route_dynar2);
  923. routesize2 = xbt_dynar_length(route_dynar2);
  924. route2 = xbt_dynar_to_array(route_dynar2);
  925. #else
  926. const SD_link_t *route2 = SD_route_get_list(srchost2, ramhost);
  927. routesize2 = SD_route_get_size(srchost2, ramhost);
  928. #endif
  929. for (i = 0; i < routesize2; i++)
  930. if (!strcmp(name, sg_link_name(route2[i])))
  931. {
  932. /* This GPU goes through this PCI bridge to access RAM */
  933. ngpus++;
  934. break;
  935. }
  936. #ifdef HAVE_SG_HOST_ROUTE
  937. free(route2);
  938. #endif
  939. }
  940. _STARPU_DEBUG("%d->%d through %s, %u GPUs\n", src, dst, name, ngpus);
  941. starpu_bus_set_ngpus(busid, ngpus);
  942. #ifdef HAVE_SG_HOST_ROUTE
  943. free(route);
  944. #endif
  945. }
  946. #endif
  947. }
  948. typedef struct
  949. {
  950. void_f_pvoid_t code;
  951. void *userparam;
  952. void *father_data;
  953. } thread_data_t;
  954. static int _starpu_simgrid_xbt_thread_create_wrapper(int argc STARPU_ATTRIBUTE_UNUSED, char *argv[] STARPU_ATTRIBUTE_UNUSED)
  955. {
  956. /* FIXME: Ugly work-around for bug in simgrid: the MPI context is not properly set at MSG process startup */
  957. MSG_process_sleep(0.000001);
  958. #ifdef HAVE_SMX_ACTOR_T
  959. smx_actor_t
  960. #else
  961. smx_process_t
  962. #endif
  963. self = SIMIX_process_self();
  964. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 13)
  965. thread_data_t *t = SIMIX_process_self_get_data(self);
  966. #else
  967. thread_data_t *t = SIMIX_process_self_get_data();
  968. #endif
  969. simcall_process_set_data(self, t->father_data);
  970. t->code(t->userparam);
  971. simcall_process_set_data(self, NULL);
  972. free(t);
  973. return 0;
  974. }
  975. void _starpu_simgrid_xbt_thread_create(const char *name, void_f_pvoid_t code, void *param)
  976. {
  977. #ifdef HAVE_SMX_ACTOR_T
  978. smx_actor_t process STARPU_ATTRIBUTE_UNUSED;
  979. #else
  980. smx_process_t process STARPU_ATTRIBUTE_UNUSED;
  981. #endif
  982. thread_data_t *res;
  983. _STARPU_MALLOC(res, sizeof(thread_data_t));
  984. res->userparam = param;
  985. res->code = code;
  986. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 13)
  987. res->father_data = SIMIX_process_self_get_data(SIMIX_process_self());
  988. #else
  989. res->father_data = SIMIX_process_self_get_data();
  990. #endif
  991. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 12)
  992. simcall_process_create(&process,
  993. #else
  994. process = simcall_process_create(
  995. #endif
  996. name,
  997. _starpu_simgrid_xbt_thread_create_wrapper, res,
  998. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 14)
  999. SIMIX_host_self_get_name(),
  1000. #else
  1001. SIMIX_host_self(),
  1002. #endif
  1003. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 15)
  1004. -1.0,
  1005. #endif
  1006. 0, NULL,
  1007. /*props */ NULL
  1008. #if SIMGRID_VERSION_MAJOR < 3 || (SIMGRID_VERSION_MAJOR == 3 && SIMGRID_VERSION_MINOR < 15)
  1009. , 0
  1010. #endif
  1011. );
  1012. }
  1013. #endif