test_interfaces.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /* StarPU --- Runtime system for heterogeneous multicore architectures.
  2. *
  3. * Copyright (C) 2011 Institut National de Recherche en Informatique et Automatique
  4. *
  5. * StarPU is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License as published by
  7. * the Free Software Foundation; either version 2.1 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * StarPU is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. *
  14. * See the GNU Lesser General Public License in COPYING.LGPL for more details.
  15. */
  16. #include <starpu.h>
  17. #ifdef STARPU_USE_OPENCL
  18. #include <starpu_opencl.h>
  19. #endif
  20. /* XXX Why cant we dereference a handle without this one ? */
  21. #include <core/sched_policy.h>
  22. #include <assert.h>
  23. #include "test_interfaces.h"
  24. #include "../../common/helper.h"
  25. /*
  26. * This is definitely note thrad-safe.
  27. */
  28. static struct test_config *current_config;
  29. /* TODO :
  30. - OpenCL to OpenCL support
  31. - RAM to RAM ?
  32. */
  33. /*
  34. * Users do not know about this enum. They only know that SUCCESS is 0, and
  35. * FAILURE is 1. Therefore, the values of SUCCESS and FAILURE shall not be
  36. * changed.
  37. */
  38. enum exit_code {
  39. SUCCESS = 0,
  40. FAILURE = 1,
  41. UNTESTED = 2,
  42. TASK_CREATION_FAILURE = 3,
  43. TASK_SUBMISSION_FAILURE = 4
  44. };
  45. static char *
  46. enum_to_string(exit_code)
  47. {
  48. switch (exit_code)
  49. {
  50. case SUCCESS:
  51. return "Success";
  52. case FAILURE:
  53. return "Failure";
  54. case UNTESTED:
  55. return "Untested";
  56. case TASK_CREATION_FAILURE:
  57. return "Task creation failed";
  58. case TASK_SUBMISSION_FAILURE:
  59. return "Task submission failed";
  60. default:
  61. assert(0);
  62. }
  63. }
  64. struct data_interface_test_summary {
  65. int success;
  66. #ifdef STARPU_USE_CUDA
  67. int cpu_to_cuda;
  68. int cuda_to_cuda;
  69. int cuda_to_cpu;
  70. int cpu_to_cuda_async;
  71. int cuda_to_cpu_async;
  72. int cuda_to_cuda_async;
  73. #endif
  74. #ifdef STARPU_USE_OPENCL
  75. int cpu_to_opencl;
  76. int opencl_to_cpu;
  77. int cpu_to_opencl_async;
  78. int opencl_to_cpu_async;
  79. #endif
  80. };
  81. void data_interface_test_summary_print(FILE *f,
  82. struct data_interface_test_summary *s)
  83. {
  84. if (!f)
  85. f = stderr;
  86. FPRINTF(f, "%s : %s\n",
  87. current_config->name, enum_to_string(s->success));
  88. FPRINTF(f, "Asynchronous :\n");
  89. #ifdef STARPU_USE_CUDA
  90. FPRINTF(f, "\tCPU -> CUDA : %s\n",
  91. enum_to_string(s->cpu_to_cuda_async));
  92. FPRINTF(f, "\tCUDA -> CUDA : %s\n",
  93. enum_to_string(s->cuda_to_cuda_async));
  94. FPRINTF(f, "\tCUDA -> CPU : %s\n",
  95. enum_to_string(s->cuda_to_cpu_async));
  96. #endif /* !STARPU_USE_CUDA */
  97. #ifdef STARPU_USE_OPENCL
  98. FPRINTF(f, "\tCPU -> OpenCl : %s\n",
  99. enum_to_string(s->cpu_to_opencl_async));
  100. FPRINTF(f, "\tOpenCl -> CPU : %s\n",
  101. enum_to_string(s->opencl_to_cpu_async));
  102. #endif /* !STARPU_USE_OPENCL */
  103. FPRINTF(f, "Synchronous :\n");
  104. #ifdef STARPU_USE_CUDA
  105. FPRINTF(f, "\tCPU -> CUDA ; %s\n",
  106. enum_to_string(s->cpu_to_cuda));
  107. FPRINTF(f, "\tCUDA -> CUDA : %s\n",
  108. enum_to_string(s->cuda_to_cuda));
  109. FPRINTF(f, "\tCUDA -> CPU : %s\n",
  110. enum_to_string(s->cuda_to_cpu));
  111. #endif /* !STARPU_USE_CUDA */
  112. #ifdef STARPU_USE_OPENCL
  113. FPRINTF(f, "\tCPU -> OpenCl : %s\n",
  114. enum_to_string(s->cpu_to_opencl));
  115. FPRINTF(f, "\tOpenCl -> CPU : %s\n",
  116. enum_to_string(s->opencl_to_cpu));
  117. #endif /* !STARPU_USE_OPENCL */
  118. }
  119. int
  120. data_interface_test_summary_success(data_interface_test_summary *s)
  121. {
  122. return s->success;
  123. }
  124. enum operation {
  125. CPU_TO_CPU,
  126. #ifdef STARPU_USE_CUDA
  127. CPU_TO_CUDA,
  128. CUDA_TO_CUDA,
  129. CUDA_TO_CPU,
  130. #endif /* !STARPU_USE_CUDA */
  131. #ifdef STARPU_USE_OPENCL
  132. CPU_TO_OPENCL,
  133. OPENCL_TO_CPU
  134. #endif /* !STARPU_USE_OPENCL */
  135. };
  136. static int*
  137. get_field(struct data_interface_test_summary *s, int async, enum operation op)
  138. {
  139. switch (op)
  140. {
  141. #ifdef STARPU_USE_CUDA
  142. case CPU_TO_CUDA:
  143. return async?&s->cpu_to_cuda_async:&s->cpu_to_cuda;
  144. case CUDA_TO_CUDA:
  145. return async?&s->cuda_to_cuda_async:&s->cuda_to_cuda;
  146. case CUDA_TO_CPU:
  147. return async?&s->cuda_to_cpu_async:&s->cuda_to_cpu;
  148. #endif /* !STARPU_USE_CUDA */
  149. #ifdef STARPU_USE_OPENCL
  150. case CPU_TO_OPENCL:
  151. return async?&s->cpu_to_opencl_async:&s->cpu_to_opencl;
  152. case OPENCL_TO_CPU:
  153. return async?&s->opencl_to_cpu_async:&s->opencl_to_cpu;
  154. #endif /* !STARPU_USE_OPENCL */
  155. default:
  156. assert(0);
  157. }
  158. }
  159. static void
  160. set_field(struct data_interface_test_summary *s, int async,
  161. enum operation op, int ret)
  162. {
  163. int *field = get_field(s, async, op);
  164. switch (ret)
  165. {
  166. case SUCCESS:
  167. *field = SUCCESS;
  168. break;
  169. case FAILURE:
  170. *field = FAILURE;
  171. s->success = FAILURE;
  172. break;
  173. case UNTESTED:
  174. *field = UNTESTED;
  175. break;
  176. case TASK_CREATION_FAILURE:
  177. *field = TASK_CREATION_FAILURE;
  178. break;
  179. case TASK_SUBMISSION_FAILURE:
  180. *field = TASK_SUBMISSION_FAILURE;
  181. break;
  182. default:
  183. assert(0);
  184. }
  185. }
  186. static struct data_interface_test_summary summary = {
  187. #ifdef STARPU_USE_CUDA
  188. .cpu_to_cuda = UNTESTED,
  189. .cuda_to_cuda = UNTESTED,
  190. .cuda_to_cpu = UNTESTED,
  191. .cpu_to_cuda_async = UNTESTED,
  192. .cuda_to_cpu_async = UNTESTED,
  193. .cuda_to_cuda_async = UNTESTED,
  194. #endif
  195. #ifdef STARPU_USE_OPENCL
  196. .cpu_to_opencl = UNTESTED,
  197. .opencl_to_cpu = UNTESTED,
  198. .cpu_to_opencl_async = UNTESTED,
  199. .opencl_to_cpu_async = UNTESTED,
  200. #endif
  201. .success = SUCCESS
  202. };
  203. /*
  204. * This variable has to be either -1 or 1.
  205. * The kernels should check that the ith value stored in the data interface is
  206. * equal to i, if factor == 1, or -i, if factor == -1.
  207. */
  208. static int factor = -1;
  209. /*
  210. * Creates a complete task, only knowing on what device it should be executed.
  211. * Note that the global variable <current_config> is heavily used here.
  212. * Arguments :
  213. * - taskp : a pointer to a valid task
  214. * - type : STARPU_{CPU,CUDA,OPENCL}_WORKER. Gordon is not supported.
  215. * - id : -1 if you dont care about the device where the task will be
  216. * executed, as long as it has the right type.
  217. * >= 0 if you want to make sure the task will be executed on the
  218. * idth device that has the specified type.
  219. * Return values :
  220. * -ENODEV
  221. * 0 : success.
  222. */
  223. static int
  224. create_task(struct starpu_task **taskp, enum starpu_archtype type, int id)
  225. {
  226. static int cpu_workers[STARPU_MAXCPUS];
  227. static int cuda_workers[STARPU_MAXCUDADEVS];
  228. static int opencl_workers[STARPU_MAXOPENCLDEVS];
  229. static int n_cpus = -1;
  230. static int n_cudas = -1;
  231. static int n_opencls = -1;
  232. if (n_cpus == -1) /* First time here */
  233. {
  234. /* XXX Dont check them all at once. */
  235. /* XXX Error checking */
  236. n_cpus = starpu_worker_get_ids_by_type(STARPU_CPU_WORKER,
  237. cpu_workers,
  238. STARPU_MAXCPUS);
  239. n_cudas = starpu_worker_get_ids_by_type(STARPU_CUDA_WORKER,
  240. cuda_workers,
  241. STARPU_MAXCUDADEVS);
  242. n_opencls = starpu_worker_get_ids_by_type(STARPU_OPENCL_WORKER,
  243. opencl_workers,
  244. STARPU_MAXOPENCLDEVS);
  245. }
  246. int workerid;
  247. static struct starpu_codelet cl;
  248. cl.nbuffers = 1;
  249. switch (type)
  250. {
  251. case STARPU_CPU_WORKER:
  252. if (id != -1)
  253. {
  254. if (id >= n_cpus)
  255. {
  256. FPRINTF(stderr, "Not enough CPU workers\n");
  257. return -ENODEV;
  258. }
  259. workerid = *(cpu_workers + id);
  260. }
  261. cl.where = STARPU_CPU;
  262. cl.cpu_func = current_config->cpu_func;
  263. break;
  264. #ifdef STARPU_USE_CUDA
  265. case STARPU_CUDA_WORKER:
  266. if (id != -1)
  267. {
  268. if (id >= n_cudas)
  269. {
  270. FPRINTF(stderr, "Not enough CUDA workers\n");
  271. return -ENODEV;
  272. }
  273. workerid = cuda_workers[id];
  274. }
  275. cl.where = STARPU_CUDA;
  276. cl.cuda_func = current_config->cuda_func;
  277. break;
  278. #endif /* !STARPU_USE_CUDA */
  279. #ifdef STARPU_USE_OPENCL
  280. case STARPU_OPENCL_WORKER:
  281. if (id != -1)
  282. {
  283. if (id >= n_opencls)
  284. {
  285. FPRINTF(stderr, "Not enough OpenCL workers\n");
  286. return -ENODEV;
  287. }
  288. workerid = *(opencl_workers + id);
  289. }
  290. cl.where = STARPU_OPENCL;
  291. cl.opencl_func = current_config->opencl_func;
  292. break;
  293. #endif /* ! STARPU_USE_OPENCL */
  294. case STARPU_GORDON_WORKER: /* Not supported */
  295. default:
  296. return -ENODEV;
  297. }
  298. struct starpu_task *task = starpu_task_create();
  299. task->synchronous = 1;
  300. task->cl = &cl;
  301. task->buffers[0].handle = *current_config->handle;
  302. task->buffers[0].mode = STARPU_RW;
  303. if (id != -1)
  304. {
  305. task->execute_on_a_specific_worker = 1;
  306. task->workerid = workerid;
  307. }
  308. factor = -factor;
  309. task->cl_arg = &factor;
  310. task->cl_arg_size = sizeof(&factor);
  311. *taskp = task;
  312. return 0;
  313. }
  314. /*
  315. * <device1>_to_<device2> functions.
  316. * They all create and submit a task that has to be executed on <device2>,
  317. * forcing a copy between <device1> and <device2>.
  318. * XXX : could we sometimes use starp_insert_task() ? It seems hars because we
  319. * need to set the execute_on_a_specific_worker field...
  320. */
  321. #ifdef STARPU_USE_CUDA
  322. static enum exit_code
  323. ram_to_cuda(void)
  324. {
  325. int err;
  326. struct starpu_task *task;
  327. err = create_task(&task, STARPU_CUDA_WORKER, 0);
  328. if (err != 0)
  329. return TASK_CREATION_FAILURE;
  330. err = starpu_task_submit(task);
  331. if (err != 0)
  332. return TASK_SUBMISSION_FAILURE;
  333. FPRINTF(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  334. return current_config->copy_failed;
  335. }
  336. #ifdef HAVE_CUDA_MEMCPY_PEER
  337. static enum exit_code
  338. cuda_to_cuda(void)
  339. {
  340. int err;
  341. struct starpu_task *task;
  342. err = create_task(&task, STARPU_CUDA_WORKER, 1);
  343. if (err != 0)
  344. return TASK_CREATION_FAILURE;
  345. err = starpu_task_submit(task);
  346. if (err != 0)
  347. return TASK_SUBMISSION_FAILURE;
  348. FPRINTF(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  349. return current_config->copy_failed;
  350. }
  351. #endif
  352. static enum exit_code
  353. cuda_to_ram(void)
  354. {
  355. int err;
  356. struct starpu_task *task;
  357. err = create_task(&task, STARPU_CPU_WORKER, -1);
  358. if (err != 0)
  359. return TASK_CREATION_FAILURE;
  360. err = starpu_task_submit(task);
  361. if (err != 0)
  362. return TASK_SUBMISSION_FAILURE;
  363. FPRINTF(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  364. return current_config->copy_failed;
  365. }
  366. #endif /* !STARPU_USE_CUDA */
  367. #ifdef STARPU_USE_OPENCL
  368. static enum exit_code
  369. ram_to_opencl()
  370. {
  371. int err;
  372. struct starpu_task *task;
  373. err = create_task(&task, STARPU_OPENCL_WORKER, 0);
  374. if (err != 0)
  375. return TASK_CREATION_FAILURE;
  376. err = starpu_task_submit(task);
  377. if (err != 0)
  378. return TASK_SUBMISSION_FAILURE;
  379. FPRINTF(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  380. return current_config->copy_failed;
  381. }
  382. static enum exit_code
  383. opencl_to_ram()
  384. {
  385. int err;
  386. struct starpu_task *task;
  387. err = create_task(&task, STARPU_CPU_WORKER, -1);
  388. if (err != 0)
  389. return TASK_CREATION_FAILURE;
  390. err = starpu_task_submit(task);
  391. if (err != 0)
  392. return TASK_SUBMISSION_FAILURE;
  393. FPRINTF(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  394. return current_config->copy_failed;
  395. }
  396. #endif /* !STARPU_USE_OPENCL */
  397. /* End of the <device1>_to_<device2> functions. */
  398. #ifdef STARPU_USE_CUDA
  399. static void
  400. run_cuda(int async)
  401. {
  402. /* RAM -> CUDA (-> CUDA) -> RAM */
  403. int err;
  404. err = ram_to_cuda();
  405. set_field(&summary, async, CPU_TO_CUDA, err);
  406. /* If this failed, there is no point in continuing. */
  407. if (err != SUCCESS)
  408. return;
  409. #ifdef HAVE_CUDA_MEMCPY_PEER
  410. err = cuda_to_cuda();
  411. set_field(&summary, async, CUDA_TO_CUDA, err);
  412. /* Even if cuda_to_cuda() failed, a valid copy is left on the first
  413. * cuda device, which means we can safely test cuda_to_ram() */
  414. #else
  415. summary.cuda_to_cuda_async = UNTESTED;
  416. #endif /* !HAVE_CUDA_MEMCPY_PEER */
  417. #ifdef STARPU_USE_CPU
  418. err = cuda_to_ram();
  419. set_field(&summary, async, CUDA_TO_CPU, err);
  420. #endif /* !STARPU_USE_CPU */
  421. }
  422. #endif /* !STARPU_USE_CUDA */
  423. #if STARPU_USE_OPENCL
  424. static void
  425. run_opencl(int async)
  426. {
  427. /* RAM -> OpenCL -> RAM */
  428. int err;
  429. err = ram_to_opencl();
  430. set_field(&summary, async, CPU_TO_OPENCL, err);
  431. if (err != SUCCESS)
  432. return;
  433. #if STARPU_USE_CPU
  434. err = opencl_to_ram();
  435. set_field(&summary, async, OPENCL_TO_CPU, err);
  436. #endif /*!STARPU_USE_CPU */
  437. }
  438. #endif /* !STARPU_USE_OPENCL */
  439. static void
  440. run_async(void)
  441. {
  442. #ifdef STARPU_USE_CUDA
  443. run_cuda(1);
  444. #endif /* !STARPU_USE_CUDA */
  445. #ifdef STARPU_USE_OPENCL
  446. run_opencl(1);
  447. #endif /* !STARPU_USE_OPENCL */
  448. }
  449. static void
  450. run_sync(void)
  451. {
  452. starpu_data_handle_t handle = *current_config->handle;
  453. struct starpu_data_copy_methods new_copy_methods;
  454. struct starpu_data_copy_methods *old_copy_methods;
  455. old_copy_methods = handle->ops->copy_methods;
  456. memcpy(&new_copy_methods,
  457. old_copy_methods,
  458. sizeof(struct starpu_data_copy_methods));
  459. #ifdef STARPU_USE_CUDA
  460. new_copy_methods.ram_to_cuda_async = NULL;
  461. new_copy_methods.cuda_to_cuda_async = NULL;
  462. new_copy_methods.cuda_to_ram_async = NULL;
  463. #endif /* !STARPU_USE_CUDA */
  464. #ifdef STARPU_USE_OPENCL
  465. new_copy_methods.ram_to_opencl_async = NULL;
  466. new_copy_methods.opencl_to_ram_async = NULL;
  467. #endif /* !STARPU_USE_OPENCL */
  468. handle->ops->copy_methods = &new_copy_methods;
  469. #ifdef STARPU_USE_CUDA
  470. run_cuda(0);
  471. #endif /* !STARPU_USE_CUDA */
  472. #ifdef STARPU_USE_OPENCL
  473. run_opencl(0);
  474. #endif /* !STARPU_USE_OPENCL */
  475. handle->ops->copy_methods = old_copy_methods;
  476. }
  477. static int
  478. load_conf(struct test_config *config)
  479. {
  480. if (!config ||
  481. #ifdef STARPU_USE_CPU
  482. !config->cpu_func ||
  483. #endif
  484. #ifdef STARPU_USE_CUDA
  485. !config->cuda_func ||
  486. #endif
  487. #ifdef STARPU_USE_OPENCL
  488. !config->opencl_func ||
  489. #endif
  490. !config->handle)
  491. {
  492. return 1;
  493. }
  494. current_config = config;
  495. return 0;
  496. }
  497. data_interface_test_summary*
  498. run_tests(struct test_config *conf)
  499. {
  500. if (load_conf(conf) == 1)
  501. {
  502. FPRINTF(stderr, "Failed to load conf.\n");
  503. return NULL;
  504. }
  505. run_async();
  506. run_sync();
  507. return &summary;
  508. }