test_interfaces.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  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. #include <assert.h>
  21. #include "test_interfaces.h"
  22. /*
  23. * This is definitely note thrad-safe.
  24. */
  25. static struct test_config *current_config;
  26. /* TODO :
  27. - OpenCL to OpenCL support
  28. - RAM to RAM ?
  29. - Asynchronous vs synchronous
  30. - Better error messages
  31. */
  32. /*
  33. * Users do not know about this enum. They only know that SUCCESS is 0, and
  34. * FAILURE is 1. Therefore, the values of SUCCESS and FAILURE shall not be
  35. * changed.
  36. */
  37. enum exit_code {
  38. SUCCESS = 0,
  39. FAILURE = 1,
  40. UNTESTED = 2,
  41. TASK_CREATION_FAILURE = 3,
  42. TASK_SUBMISSION_FAILURE = 4
  43. };
  44. static char *
  45. enum_to_string(exit_code)
  46. {
  47. switch (exit_code)
  48. {
  49. case SUCCESS:
  50. return "Success";
  51. case FAILURE:
  52. return "Failure";
  53. case UNTESTED:
  54. return "Untested";
  55. case TASK_CREATION_FAILURE:
  56. return "Task creation failed";
  57. case TASK_SUBMISSION_FAILURE:
  58. return "Task submission failed";
  59. default:
  60. assert(0);
  61. }
  62. }
  63. struct data_interface_test_summary {
  64. int success;
  65. #ifdef STARPU_USE_CUDA
  66. int cpu_to_cuda_async;
  67. int cuda_to_cpu_async;
  68. int cuda_to_cuda_async;
  69. #endif
  70. #ifdef STARPU_USE_OPENCL
  71. int cpu_to_opencl_async;
  72. int opencl_to_cpu_async;
  73. #endif
  74. };
  75. void data_interface_test_summary_print(FILE *f,
  76. struct data_interface_test_summary *s)
  77. {
  78. if (!f)
  79. f = stderr;
  80. (void) fprintf(f, "%s : %s\n",
  81. current_config->name, enum_to_string(s->success));
  82. (void) fprintf(f, "Details :\n");
  83. (void) fprintf(f, "\tCPU -> CUDA : %s\n",
  84. enum_to_string(s->cpu_to_cuda_async));
  85. (void) fprintf(f, "\tCUDA -> CUDA : %s\n",
  86. enum_to_string(s->cuda_to_cuda_async));
  87. (void) fprintf(f, "\tCUDA -> CPU : %s\n",
  88. enum_to_string(s->cuda_to_cpu_async));
  89. (void) fprintf(f, "\tCPU -> OpenCl : %s\n",
  90. enum_to_string(s->cpu_to_opencl_async));
  91. (void) fprintf(f, "\tOpenCl -< CPU : %s\n",
  92. enum_to_string(s->opencl_to_cpu_async));
  93. }
  94. int
  95. data_interface_test_summary_success(data_interface_test_summary *s)
  96. {
  97. return s->success;
  98. }
  99. static void
  100. set_field(struct data_interface_test_summary *s, int *test, int ret)
  101. {
  102. switch (ret)
  103. {
  104. case SUCCESS:
  105. *test = SUCCESS;
  106. break;
  107. case FAILURE:
  108. *test = FAILURE;
  109. s->success = FAILURE;
  110. break;
  111. case UNTESTED:
  112. *test = UNTESTED;
  113. break;
  114. case TASK_CREATION_FAILURE:
  115. *test = TASK_CREATION_FAILURE;
  116. break;
  117. case TASK_SUBMISSION_FAILURE:
  118. *test = TASK_SUBMISSION_FAILURE;
  119. break;
  120. default:
  121. assert(0);
  122. }
  123. }
  124. static struct data_interface_test_summary summary = {
  125. #ifdef STARPU_USE_CUDA
  126. .cpu_to_cuda_async = UNTESTED,
  127. .cuda_to_cpu_async = UNTESTED,
  128. .cuda_to_cuda_async = UNTESTED,
  129. #endif
  130. #ifdef STARPU_USE_OPENCL
  131. .cpu_to_opencl_async = UNTESTED,
  132. .opencl_to_cpu_async = UNTESTED,
  133. #endif
  134. .success = SUCCESS
  135. };
  136. /*
  137. * This variable has to be either -1 or 1.
  138. * The kernels should check that the ith value stored in the data interface is
  139. * equal to i, if factor == 1, or -i, if factor == -1.
  140. */
  141. static int factor = -1;
  142. /*
  143. * Creates a complete task, only knowing on what device it should be executed.
  144. * Note that the global variable <current_config> is heavily used here.
  145. * Arguments :
  146. * - taskp : a pointer to a valid task
  147. * - type : STARPU_{CPU,CUDA,OPENCL}_WORKER. Gordon is not supported.
  148. * - id : -1 if you dont care about the device where the task will be
  149. * executed, as long as it has the right type.
  150. * >= 0 if you want to make sure the task will be executed on the
  151. * idth device that has the specified type.
  152. * Return values :
  153. * -ENODEV
  154. * 0 : success.
  155. */
  156. static int
  157. create_task(struct starpu_task **taskp, enum starpu_archtype type, int id)
  158. {
  159. static int cpu_workers[STARPU_MAXCPUS];
  160. static int cuda_workers[STARPU_MAXCUDADEVS];
  161. static int opencl_workers[STARPU_MAXOPENCLDEVS];
  162. static int n_cpus = -1;
  163. static int n_cudas = -1;
  164. static int n_opencls = -1;
  165. if (n_cpus == -1) /* First time here */
  166. {
  167. /* XXX Dont check them all at once. */
  168. /* XXX Error checking */
  169. n_cpus = starpu_worker_get_ids_by_type(STARPU_CPU_WORKER,
  170. cpu_workers,
  171. STARPU_MAXCPUS);
  172. n_cudas = starpu_worker_get_ids_by_type(STARPU_CUDA_WORKER,
  173. cuda_workers,
  174. STARPU_MAXCUDADEVS);
  175. n_opencls = starpu_worker_get_ids_by_type(STARPU_OPENCL_WORKER,
  176. opencl_workers,
  177. STARPU_MAXOPENCLDEVS);
  178. }
  179. int workerid;
  180. static struct starpu_codelet_t cl;
  181. cl.nbuffers = 1;
  182. switch (type)
  183. {
  184. case STARPU_CPU_WORKER:
  185. if (id != -1)
  186. {
  187. if (id >= n_cpus)
  188. {
  189. fprintf(stderr, "Not enough CPU workers\n");
  190. return -ENODEV;
  191. }
  192. workerid = *(cpu_workers + id);
  193. }
  194. cl.where = STARPU_CPU;
  195. cl.cpu_func = current_config->cpu_func;
  196. break;
  197. #ifdef STARPU_USE_CUDA
  198. case STARPU_CUDA_WORKER:
  199. if (id != -1)
  200. {
  201. if (id >= n_cudas)
  202. {
  203. fprintf(stderr, "Not enough CUDA workers\n");
  204. return -ENODEV;
  205. }
  206. workerid = cuda_workers[id];
  207. }
  208. cl.where = STARPU_CUDA;
  209. cl.cuda_func = current_config->cuda_func;
  210. break;
  211. #endif /* !STARPU_USE_CUDA */
  212. #ifdef STARPU_USE_OPENCL
  213. case STARPU_OPENCL_WORKER:
  214. if (id != -1)
  215. {
  216. if (id >= n_opencls)
  217. {
  218. fprintf(stderr, "Not enough OpenCL workers\n");
  219. return -ENODEV;
  220. }
  221. workerid = *(opencl_workers + id);
  222. }
  223. cl.where = STARPU_OPENCL;
  224. cl.opencl_func = current_config->opencl_func;
  225. break;
  226. #endif /* ! STARPU_USE_OPENCL */
  227. case STARPU_GORDON_WORKER: /* Not supported */
  228. default:
  229. return -ENODEV;
  230. }
  231. struct starpu_task *task = starpu_task_create();
  232. task->synchronous = 1;
  233. task->cl = &cl;
  234. task->buffers[0].handle = *current_config->handle;
  235. task->buffers[0].mode = STARPU_RW;
  236. if (id != -1)
  237. {
  238. task->execute_on_a_specific_worker = 1;
  239. task->workerid = workerid;
  240. }
  241. factor = -factor;
  242. task->cl_arg = &factor;
  243. task->cl_arg_size = sizeof(&factor);
  244. *taskp = task;
  245. return 0;
  246. }
  247. /*
  248. * <device1>_to_<device2> functions.
  249. * They all create and submit a task that has to be executed on <device2>,
  250. * forcing a copy between <device1> and <device2>.
  251. * XXX : could we sometimes use starp_insert_task() ? It seems hars because we
  252. * need to set the execute_on_a_specific_worker field...
  253. */
  254. #ifdef STARPU_USE_CUDA
  255. static enum exit_code
  256. ram_to_cuda(void)
  257. {
  258. int err;
  259. struct starpu_task *task;
  260. err = create_task(&task, STARPU_CUDA_WORKER, 0);
  261. if (err != 0)
  262. return TASK_CREATION_FAILURE;
  263. err = starpu_task_submit(task);
  264. if (err != 0)
  265. return TASK_SUBMISSION_FAILURE;
  266. fprintf(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  267. return current_config->copy_failed;
  268. }
  269. #if HAVE_CUDA_MEMCPY_PEER
  270. static enum exit_code
  271. cuda_to_cuda(void)
  272. {
  273. int err;
  274. struct starpu_task *task;
  275. err = create_task(&task, STARPU_CUDA_WORKER, 1);
  276. if (err != 0)
  277. return TASK_CREATION_FAILURE;
  278. err = starpu_task_submit(task);
  279. if (err != 0)
  280. return TASK_SUBMISSION_FAILURE;
  281. fprintf(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  282. return current_config->copy_failed;
  283. }
  284. #endif
  285. static enum exit_code
  286. cuda_to_ram(void)
  287. {
  288. int err;
  289. struct starpu_task *task;
  290. err = create_task(&task, STARPU_CPU_WORKER, -1);
  291. if (err != 0)
  292. return TASK_CREATION_FAILURE;
  293. err = starpu_task_submit(task);
  294. if (err != 0)
  295. return TASK_SUBMISSION_FAILURE;
  296. fprintf(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  297. return current_config->copy_failed;
  298. }
  299. #endif /* !STARPU_USE_CUDA */
  300. #ifdef STARPU_USE_OPENCL
  301. static enum exit_code
  302. ram_to_opencl()
  303. {
  304. int err;
  305. struct starpu_task *task;
  306. err = create_task(&task, STARPU_OPENCL_WORKER, 0);
  307. if (err != 0)
  308. return TASK_CREATION_FAILURE;
  309. err = starpu_task_submit(task);
  310. if (err != 0)
  311. return TASK_SUBMISSION_FAILURE;
  312. fprintf(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  313. return current_config->copy_failed;
  314. }
  315. static enum exit_code
  316. opencl_to_ram()
  317. {
  318. int err;
  319. struct starpu_task *task;
  320. err = create_task(&task, STARPU_CPU_WORKER, -1);
  321. if (err != 0)
  322. return TASK_CREATION_FAILURE;
  323. err = starpu_task_submit(task);
  324. if (err != 0)
  325. return TASK_SUBMISSION_FAILURE;
  326. fprintf(stderr, "[%s] : %d\n", __func__, current_config->copy_failed);
  327. return current_config->copy_failed;
  328. }
  329. #endif /* !STARPU_USE_OPENCL */
  330. /* End of the <device1>_to_<device2> functions. */
  331. static void
  332. run_cuda_async(void)
  333. {
  334. /* RAM -> CUDA (-> CUDA) -> RAM */
  335. int err;
  336. #ifdef STARPU_USE_CUDA
  337. err = ram_to_cuda();
  338. set_field(&summary, &summary.cpu_to_cuda_async, err);
  339. /* If this failed, there is no point in continuing. */
  340. if (err != SUCCESS)
  341. return;
  342. #ifdef HAVE_CUDA_MEMCPY_PEER
  343. err = cuda_to_cuda();
  344. set_field(&summary, &summary.cuda_to_cuda_async, err);
  345. /* Even if cuda_to_cuda() failed, a valid copy is left on the first
  346. * cuda device, which means we can safely test cuda_to_ram() */
  347. #else
  348. summary.cuda_to_cuda_async = UNTESTED;
  349. #endif /* !HAVE_CUDA_MEMCPY_PEER */
  350. err = cuda_to_ram();
  351. set_field(&summary, &summary.cuda_to_cpu_async, err);
  352. #endif /* !STARPU_USE_CUDA */
  353. }
  354. static void
  355. run_opencl_async(void)
  356. {
  357. /* RAM -> OpenCL -> RAM */
  358. int err;
  359. #if STARPU_USE_OPENCL
  360. err = ram_to_opencl();
  361. set_field(&summary, &summary.cpu_to_opencl_async, err);
  362. if (err != SUCCESS)
  363. return;
  364. err = opencl_to_ram();
  365. set_field(&summary, &summary.opencl_to_cpu_async, err);
  366. #endif /* !STARPU_USE_OPENCL */
  367. }
  368. static void
  369. run_async(void)
  370. {
  371. run_cuda_async();
  372. run_opencl_async();
  373. }
  374. static int
  375. load_conf(struct test_config *config)
  376. {
  377. if (!config ||
  378. !config->cpu_func ||
  379. #ifdef STARPU_USE_CUDA
  380. !config->cuda_func ||
  381. #endif
  382. #ifdef STARPU_USE_OPENCL
  383. !config->opencl_func ||
  384. #endif
  385. !config->handle)
  386. {
  387. return 1;
  388. }
  389. current_config = config;
  390. return 0;
  391. }
  392. data_interface_test_summary*
  393. run_tests(struct test_config *conf)
  394. {
  395. if (load_conf(conf) == 1)
  396. {
  397. fprintf(stderr, "Failed to load conf.\n");
  398. return NULL;
  399. }
  400. run_async();
  401. return &summary;
  402. }