cumo 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -2
  3. data/README.md +133 -0
  4. data/bench/cumo_sync_probe.rb +71 -8
  5. data/ext/cumo/cuda/cublas.c +0 -40
  6. data/ext/cumo/cuda/cudnn.c +48 -0
  7. data/ext/cumo/cuda/cudnn_impl.cpp +91 -62
  8. data/ext/cumo/cuda/runtime.c +3 -4
  9. data/ext/cumo/cumo.c +34 -0
  10. data/ext/cumo/extconf.rb +2 -0
  11. data/ext/cumo/include/cumo/cuda/cudnn.h +21 -2
  12. data/ext/cumo/include/cumo/cuda/cumo_thrust.hpp +1 -0
  13. data/ext/cumo/include/cumo/cuda/runtime.h +28 -0
  14. data/ext/cumo/include/cumo/intern.h +4 -1
  15. data/ext/cumo/include/cumo/narray.h +59 -3
  16. data/ext/cumo/include/cumo/template.h +35 -10
  17. data/ext/cumo/include/cumo/template_kernel.h +96 -0
  18. data/ext/cumo/include/cumo/types/bit.h +3 -0
  19. data/ext/cumo/include/cumo/types/bit_kernel.h +3 -0
  20. data/ext/cumo/include/cumo/types/complex_macro.h +2 -0
  21. data/ext/cumo/include/cumo/types/complex_macro_kernel.h +2 -0
  22. data/ext/cumo/include/cumo/types/float_macro.h +2 -0
  23. data/ext/cumo/include/cumo/types/float_macro_kernel.h +2 -0
  24. data/ext/cumo/include/cumo/types/half_def.h +129 -0
  25. data/ext/cumo/include/cumo/types/half_def_kernel.h +28 -0
  26. data/ext/cumo/include/cumo/types/half_macro.h +188 -0
  27. data/ext/cumo/include/cumo/types/half_macro_kernel.h +198 -0
  28. data/ext/cumo/include/cumo/types/hfloat.h +22 -0
  29. data/ext/cumo/include/cumo/types/hfloat_kernel.h +16 -0
  30. data/ext/cumo/include/cumo/types/real_accum.h +2 -0
  31. data/ext/cumo/include/cumo/types/real_accum_kernel.h +2 -0
  32. data/ext/cumo/include/cumo/types/robj_macro.h +3 -0
  33. data/ext/cumo/include/cumo/types/xint_macro.h +3 -0
  34. data/ext/cumo/include/cumo/types/xint_macro_kernel.h +2 -0
  35. data/ext/cumo/include/cumo.h +2 -2
  36. data/ext/cumo/narray/data.c +49 -174
  37. data/ext/cumo/narray/gen/def/bit.rb +2 -0
  38. data/ext/cumo/narray/gen/def/dcomplex.rb +2 -0
  39. data/ext/cumo/narray/gen/def/dfloat.rb +8 -0
  40. data/ext/cumo/narray/gen/def/hfloat.rb +47 -0
  41. data/ext/cumo/narray/gen/def/int16.rb +2 -0
  42. data/ext/cumo/narray/gen/def/int32.rb +2 -0
  43. data/ext/cumo/narray/gen/def/int64.rb +2 -0
  44. data/ext/cumo/narray/gen/def/int8.rb +2 -0
  45. data/ext/cumo/narray/gen/def/robject.rb +2 -0
  46. data/ext/cumo/narray/gen/def/scomplex.rb +2 -0
  47. data/ext/cumo/narray/gen/def/sfloat.rb +8 -0
  48. data/ext/cumo/narray/gen/def/uint16.rb +3 -1
  49. data/ext/cumo/narray/gen/def/uint32.rb +3 -1
  50. data/ext/cumo/narray/gen/def/uint64.rb +2 -0
  51. data/ext/cumo/narray/gen/def/uint8.rb +3 -1
  52. data/ext/cumo/narray/gen/erbpp2.rb +6 -0
  53. data/ext/cumo/narray/gen/spec.rb +1 -0
  54. data/ext/cumo/narray/gen/tmpl/accum.c +1 -1
  55. data/ext/cumo/narray/gen/tmpl/accum_arg.c +1 -1
  56. data/ext/cumo/narray/gen/tmpl/accum_arg_kernel.cu +5 -5
  57. data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +14 -1
  58. data/ext/cumo/narray/gen/tmpl/accum_index.c +1 -1
  59. data/ext/cumo/narray/gen/tmpl/accum_index_kernel.cu +5 -5
  60. data/ext/cumo/narray/gen/tmpl/alloc_func.c +4 -7
  61. data/ext/cumo/narray/gen/tmpl/allocate.c +1 -2
  62. data/ext/cumo/narray/gen/tmpl/aref_cpu.c +1 -1
  63. data/ext/cumo/narray/gen/tmpl/batch_norm.c +11 -23
  64. data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +12 -24
  65. data/ext/cumo/narray/gen/tmpl/binary_kernel.cu +69 -2
  66. data/ext/cumo/narray/gen/tmpl/clip.c +3 -3
  67. data/ext/cumo/narray/gen/tmpl/cond_unary.c +1 -1
  68. data/ext/cumo/narray/gen/tmpl/conv.c +11 -17
  69. data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +11 -17
  70. data/ext/cumo/narray/gen/tmpl/conv_transpose.c +11 -17
  71. data/ext/cumo/narray/gen/tmpl/cum.c +24 -1
  72. data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +29 -4
  73. data/ext/cumo/narray/gen/tmpl/each.c +2 -3
  74. data/ext/cumo/narray/gen/tmpl/each_with_index.c +5 -21
  75. data/ext/cumo/narray/gen/tmpl/extract_cpu.c +1 -1
  76. data/ext/cumo/narray/gen/tmpl/extract_data.c +1 -1
  77. data/ext/cumo/narray/gen/tmpl/fill.c +1 -1
  78. data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +9 -21
  79. data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +42 -37
  80. data/ext/cumo/narray/gen/tmpl/format.c +1 -1
  81. data/ext/cumo/narray/gen/tmpl/format_to_a.c +1 -1
  82. data/ext/cumo/narray/gen/tmpl/gemm.c +78 -27
  83. data/ext/cumo/narray/gen/tmpl/init_class.c +0 -7
  84. data/ext/cumo/narray/gen/tmpl/init_upcast.c +6 -0
  85. data/ext/cumo/narray/gen/tmpl/inspect.c +1 -1
  86. data/ext/cumo/narray/gen/tmpl/lib.c +11 -1
  87. data/ext/cumo/narray/gen/tmpl/map_with_index.c +7 -14
  88. data/ext/cumo/narray/gen/tmpl/median.c +9 -47
  89. data/ext/cumo/narray/gen/tmpl/minmax.c +1 -1
  90. data/ext/cumo/narray/gen/tmpl/poly.c +1 -1
  91. data/ext/cumo/narray/gen/tmpl/pooling_backward.c +2 -14
  92. data/ext/cumo/narray/gen/tmpl/pooling_forward.c +2 -14
  93. data/ext/cumo/narray/gen/tmpl/qsort.c +0 -1
  94. data/ext/cumo/narray/gen/tmpl/rand.c +20 -2
  95. data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +13 -0
  96. data/ext/cumo/narray/gen/tmpl/rand_norm.c +4 -4
  97. data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +7 -5
  98. data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +83 -97
  99. data/ext/cumo/narray/gen/tmpl/sort.c +1 -1
  100. data/ext/cumo/narray/gen/tmpl/sort_index.c +6 -3
  101. data/ext/cumo/narray/gen/tmpl/store_array.c +21 -22
  102. data/ext/cumo/narray/gen/tmpl/store_bit.c +1 -1
  103. data/ext/cumo/narray/gen/tmpl/store_from.c +1 -1
  104. data/ext/cumo/narray/gen/tmpl/store_from_kernel.cu +37 -2
  105. data/ext/cumo/narray/gen/tmpl/to_a.c +1 -1
  106. data/ext/cumo/narray/gen/tmpl/unary.c +20 -3
  107. data/ext/cumo/narray/gen/tmpl/unary2.c +1 -1
  108. data/ext/cumo/narray/gen/tmpl/unary_s.c +1 -1
  109. data/ext/cumo/narray/gen/tmpl_bit/allocate.c +2 -3
  110. data/ext/cumo/narray/gen/tmpl_bit/aref_cpu.c +1 -1
  111. data/ext/cumo/narray/gen/tmpl_bit/bit_count_cpu.c +1 -1
  112. data/ext/cumo/narray/gen/tmpl_bit/each.c +2 -2
  113. data/ext/cumo/narray/gen/tmpl_bit/each_with_index.c +5 -20
  114. data/ext/cumo/narray/gen/tmpl_bit/extract_cpu.c +1 -1
  115. data/ext/cumo/narray/gen/tmpl_bit/format.c +1 -1
  116. data/ext/cumo/narray/gen/tmpl_bit/format_to_a.c +1 -1
  117. data/ext/cumo/narray/gen/tmpl_bit/inspect.c +1 -1
  118. data/ext/cumo/narray/gen/tmpl_bit/mask.c +38 -17
  119. data/ext/cumo/narray/gen/tmpl_bit/mask_kernel.cu +4 -4
  120. data/ext/cumo/narray/gen/tmpl_bit/store_array.c +10 -11
  121. data/ext/cumo/narray/gen/tmpl_bit/store_from.c +1 -1
  122. data/ext/cumo/narray/gen/tmpl_bit/to_a.c +1 -1
  123. data/ext/cumo/narray/gen/tmpl_bit/where.c +66 -13
  124. data/ext/cumo/narray/gen/tmpl_bit/where2.c +57 -20
  125. data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +6 -6
  126. data/ext/cumo/narray/index.c +63 -18
  127. data/ext/cumo/narray/index_kernel.cu +15 -0
  128. data/ext/cumo/narray/math.c +2 -0
  129. data/ext/cumo/narray/narray.c +289 -183
  130. data/ext/cumo/narray/ndloop.c +91 -26
  131. data/ext/cumo/narray/sort_kernel.cu +39 -18
  132. data/ext/cumo/narray/struct.c +14 -17
  133. data/lib/cumo/cuda/cudnn.rb +1 -1
  134. data/lib/cumo/narray/extra.rb +11 -2
  135. data/test/bit_test.rb +147 -0
  136. data/test/cudnn_test.rb +132 -46
  137. data/test/extra_test.rb +1 -1
  138. data/test/hfloat_test.rb +717 -0
  139. data/test/math_test.rb +4 -1
  140. data/test/narray_alt_coverage_test.rb +7 -0
  141. data/test/narray_test.rb +1018 -11
  142. data/test/test_helper.rb +73 -0
  143. metadata +11 -1
@@ -158,7 +158,8 @@ cumo_cuda_cudnn_CreateConvolutionDescriptor(
158
158
  size_t ndim,
159
159
  int* int_stride,
160
160
  int* int_pad,
161
- cudnnDataType_t cudnn_dtype) {
161
+ cudnnDataType_t compute_dtype,
162
+ cudnnMathType_t math_type) {
162
163
  cudnnStatus_t status = CUDNN_STATUS_SUCCESS;
163
164
  int int_dilation[CUMO_NA_MAX_DIMENSION];
164
165
  for (size_t idim = 0; idim < ndim; ++idim) {
@@ -178,7 +179,7 @@ cumo_cuda_cudnn_CreateConvolutionDescriptor(
178
179
  int_dilation[0],
179
180
  int_dilation[1],
180
181
  CUDNN_CROSS_CORRELATION,
181
- cudnn_dtype);
182
+ compute_dtype);
182
183
  } else {
183
184
  status = cudnnSetConvolutionNdDescriptor(
184
185
  *desc,
@@ -187,10 +188,14 @@ cumo_cuda_cudnn_CreateConvolutionDescriptor(
187
188
  int_stride,
188
189
  int_dilation,
189
190
  CUDNN_CROSS_CORRELATION,
190
- cudnn_dtype);
191
+ compute_dtype);
191
192
  }
193
+ if (status != CUDNN_STATUS_SUCCESS) return status;
192
194
 
193
- return status;
195
+ // Tensor cores are not reached under CUDNN_DEFAULT_MATH, and asking for
196
+ // them where they do not apply is not an error, so every dtype names what
197
+ // it wants.
198
+ return cudnnSetConvolutionMathType(*desc, math_type);
194
199
  }
195
200
 
196
201
  cudnnStatus_t
@@ -304,9 +309,49 @@ struct AlgoCacheKeyHash {
304
309
  }
305
310
  };
306
311
 
307
- using FwdAlgoCacheMap = std::unordered_map<AlgoCacheKey, std::pair<cudnnConvolutionFwdAlgo_t, size_t>, AlgoCacheKeyHash>;
308
- using BwdDataAlgoCacheMap = std::unordered_map<AlgoCacheKey, std::pair<cudnnConvolutionBwdDataAlgo_t, size_t>, AlgoCacheKeyHash>;
309
- using BwdFilterAlgoCacheMap = std::unordered_map<AlgoCacheKey, std::pair<cudnnConvolutionBwdFilterAlgo_t, size_t>, AlgoCacheKeyHash>;
312
+ // The math type belongs with the algorithm: the descriptor's own setting is
313
+ // only a permission, and cuDNN reports which one the chosen algorithm actually
314
+ // used. Handing that algorithm to the convolution under a different math type
315
+ // is rejected, or answers something else.
316
+ // A template cannot be declared with C linkage, and this file's exports are
317
+ // wrapped in extern "C".
318
+ extern "C++" {
319
+ template <typename Algo>
320
+ struct AlgoCacheEntry {
321
+ Algo algo;
322
+ size_t memory;
323
+ cudnnMathType_t math_type;
324
+ };
325
+ }
326
+
327
+ using FwdAlgoCacheMap = std::unordered_map<AlgoCacheKey, AlgoCacheEntry<cudnnConvolutionFwdAlgo_t>, AlgoCacheKeyHash>;
328
+ using BwdDataAlgoCacheMap = std::unordered_map<AlgoCacheKey, AlgoCacheEntry<cudnnConvolutionBwdDataAlgo_t>, AlgoCacheKeyHash>;
329
+ using BwdFilterAlgoCacheMap = std::unordered_map<AlgoCacheKey, AlgoCacheEntry<cudnnConvolutionBwdFilterAlgo_t>, AlgoCacheKeyHash>;
330
+
331
+ // Every search builds the same key. Leaving each of the three to do it by hand
332
+ // is how the device id came to be set in one of them and not the others.
333
+ static AlgoCacheKey
334
+ MakeAlgoCacheKey(
335
+ cumo_narray_t* nx, cumo_narray_t* nw, cumo_narray_t* ny,
336
+ int* int_stride, int* int_pad, size_t ndim,
337
+ cudnnDataType_t cudnn_dtype, size_t max_workspace_size)
338
+ {
339
+ auto key = AlgoCacheKey{};
340
+ cumo_cuda_runtime_check_status(cudaGetDevice(&(key.device_id)));
341
+ key.ndim = ndim;
342
+ for (size_t idim = 0; idim < ndim + 2; ++idim) {
343
+ key.x_shape[idim] = nx->shape[idim];
344
+ key.w_shape[idim] = nw->shape[idim];
345
+ key.y_shape[idim] = ny->shape[idim];
346
+ }
347
+ for (size_t idim = 0; idim < ndim; ++idim) {
348
+ key.pad[idim] = int_pad[idim];
349
+ key.stride[idim] = int_stride[idim];
350
+ }
351
+ key.dtype = cudnn_dtype;
352
+ key.max_workspace_size = max_workspace_size;
353
+ return key;
354
+ }
310
355
 
311
356
  static FwdAlgoCacheMap fwd_algo_cache_map_{};
312
357
  static BwdDataAlgoCacheMap bwd_data_algo_cache_map_{};
@@ -335,28 +380,19 @@ cumo_cuda_cudnn_FindConvolutionForwardAlgorithm(
335
380
  CumoGetNArray(w, nw);
336
381
  CumoGetNArray(y, ny);
337
382
 
338
- auto key = AlgoCacheKey{};
339
- cumo_cuda_runtime_check_status(cudaGetDevice(&(key.device_id)));
340
- key.ndim = ndim;
341
- for (size_t idim = 0; idim < ndim + 2; ++idim) {
342
- key.x_shape[idim] = nx->shape[idim];
343
- key.w_shape[idim] = nw->shape[idim];
344
- key.y_shape[idim] = ny->shape[idim];
345
- }
346
- for (size_t idim = 0; idim < ndim; ++idim) {
347
- key.pad[idim]= int_pad[idim];
348
- key.stride[idim]= int_stride[idim];
349
- }
350
- key.dtype = cudnn_dtype;
351
- key.max_workspace_size = max_workspace_size;
383
+ auto key = MakeAlgoCacheKey(nx, nw, ny, int_stride, int_pad, ndim,
384
+ cudnn_dtype, max_workspace_size);
352
385
 
353
386
  auto& algo_cache_map = fwd_algo_cache_map_;
354
387
  // TODO: thread-safe
355
388
  auto it = algo_cache_map.find(key);
356
389
  if (it != algo_cache_map.end()) {
357
- auto pair = it->second;
358
- perf_result->algo = pair.first;
359
- perf_result->memory = pair.second;
390
+ auto entry = it->second;
391
+ // clear the fields the search would have filled but the cache drops
392
+ *perf_result = {};
393
+ perf_result->algo = entry.algo;
394
+ perf_result->memory = entry.memory;
395
+ perf_result->mathType = entry.math_type;
360
396
  return CUDNN_STATUS_SUCCESS;
361
397
  }
362
398
 
@@ -382,10 +418,13 @@ cumo_cuda_cudnn_FindConvolutionForwardAlgorithm(
382
418
  max_workspace_size);
383
419
  cumo_cuda_runtime_free(workspace);
384
420
  if (status != CUDNN_STATUS_SUCCESS) return status;
385
- assert(returned_algo_count == 1);
421
+ // A search that answers success with nothing to report would leave
422
+ // perf_result untouched, and its algo and math type are used below.
423
+ if (returned_algo_count < 1) return CUDNN_STATUS_NOT_SUPPORTED;
424
+ if (perf_result->status != CUDNN_STATUS_SUCCESS) return perf_result->status;
386
425
 
387
426
  // TODO: thread-safe
388
- algo_cache_map[key] = {perf_result->algo, perf_result->memory};
427
+ algo_cache_map[key] = {perf_result->algo, perf_result->memory, perf_result->mathType};
389
428
  return status;
390
429
  }
391
430
 
@@ -412,27 +451,19 @@ cumo_cuda_cudnn_FindConvolutionBackwardDataAlgorithm(
412
451
  CumoGetNArray(w, nw);
413
452
  CumoGetNArray(y, ny);
414
453
 
415
- auto key = AlgoCacheKey{};
416
- key.ndim = ndim;
417
- for (size_t idim = 0; idim < ndim + 2; ++idim) {
418
- key.x_shape[idim] = nx->shape[idim];
419
- key.w_shape[idim] = nw->shape[idim];
420
- key.y_shape[idim] = ny->shape[idim];
421
- }
422
- for (size_t idim = 0; idim < ndim; ++idim) {
423
- key.pad[idim]= int_pad[idim];
424
- key.stride[idim]= int_stride[idim];
425
- }
426
- key.dtype = cudnn_dtype;
427
- key.max_workspace_size = max_workspace_size;
454
+ auto key = MakeAlgoCacheKey(nx, nw, ny, int_stride, int_pad, ndim,
455
+ cudnn_dtype, max_workspace_size);
428
456
 
429
457
  auto& algo_cache_map = bwd_data_algo_cache_map_;
430
458
  // TODO: thread-safe
431
459
  auto it = algo_cache_map.find(key);
432
460
  if (it != algo_cache_map.end()) {
433
- auto pair = it->second;
434
- perf_result->algo = pair.first;
435
- perf_result->memory = pair.second;
461
+ auto entry = it->second;
462
+ // clear the fields the search would have filled but the cache drops
463
+ *perf_result = {};
464
+ perf_result->algo = entry.algo;
465
+ perf_result->memory = entry.memory;
466
+ perf_result->mathType = entry.math_type;
436
467
  return CUDNN_STATUS_SUCCESS;
437
468
  }
438
469
 
@@ -458,10 +489,13 @@ cumo_cuda_cudnn_FindConvolutionBackwardDataAlgorithm(
458
489
  max_workspace_size);
459
490
  cumo_cuda_runtime_free(workspace);
460
491
  if (status != CUDNN_STATUS_SUCCESS) return status;
461
- assert(returned_algo_count == 1);
492
+ // A search that answers success with nothing to report would leave
493
+ // perf_result untouched, and its algo and math type are used below.
494
+ if (returned_algo_count < 1) return CUDNN_STATUS_NOT_SUPPORTED;
495
+ if (perf_result->status != CUDNN_STATUS_SUCCESS) return perf_result->status;
462
496
 
463
497
  // TODO: thread-safe
464
- algo_cache_map[key] = {perf_result->algo, perf_result->memory};
498
+ algo_cache_map[key] = {perf_result->algo, perf_result->memory, perf_result->mathType};
465
499
  return status;
466
500
  }
467
501
 
@@ -488,27 +522,19 @@ cumo_cuda_cudnn_FindConvolutionBackwardFilterAlgorithm(
488
522
  CumoGetNArray(gy, ngy);
489
523
  CumoGetNArray(gw, ngw);
490
524
 
491
- auto key = AlgoCacheKey{};
492
- key.ndim = ndim;
493
- for (size_t idim = 0; idim < ndim + 2; ++idim) {
494
- key.x_shape[idim] = nx->shape[idim];
495
- key.w_shape[idim] = ngw->shape[idim];
496
- key.y_shape[idim] = ngy->shape[idim];
497
- }
498
- for (size_t idim = 0; idim < ndim; ++idim) {
499
- key.pad[idim]= int_pad[idim];
500
- key.stride[idim]= int_stride[idim];
501
- }
502
- key.dtype = cudnn_dtype;
503
- key.max_workspace_size = max_workspace_size;
525
+ auto key = MakeAlgoCacheKey(nx, ngw, ngy, int_stride, int_pad, ndim,
526
+ cudnn_dtype, max_workspace_size);
504
527
 
505
528
  auto& algo_cache_map = bwd_filter_algo_cache_map_;
506
529
  // TODO: thread-safe
507
530
  auto it = algo_cache_map.find(key);
508
531
  if (it != algo_cache_map.end()) {
509
- auto pair = it->second;
510
- perf_result->algo = pair.first;
511
- perf_result->memory = pair.second;
532
+ auto entry = it->second;
533
+ // clear the fields the search would have filled but the cache drops
534
+ *perf_result = {};
535
+ perf_result->algo = entry.algo;
536
+ perf_result->memory = entry.memory;
537
+ perf_result->mathType = entry.math_type;
512
538
  return CUDNN_STATUS_SUCCESS;
513
539
  }
514
540
 
@@ -534,10 +560,13 @@ cumo_cuda_cudnn_FindConvolutionBackwardFilterAlgorithm(
534
560
  max_workspace_size);
535
561
  cumo_cuda_runtime_free(workspace);
536
562
  if (status != CUDNN_STATUS_SUCCESS) return status;
537
- assert(returned_algo_count == 1);
563
+ // A search that answers success with nothing to report would leave
564
+ // perf_result untouched, and its algo and math type are used below.
565
+ if (returned_algo_count < 1) return CUDNN_STATUS_NOT_SUPPORTED;
566
+ if (perf_result->status != CUDNN_STATUS_SUCCESS) return perf_result->status;
538
567
 
539
568
  // TODO: thread-safe
540
- algo_cache_map[key] = {perf_result->algo, perf_result->memory};
569
+ algo_cache_map[key] = {perf_result->algo, perf_result->memory, perf_result->mathType};
541
570
  return status;
542
571
  }
543
572
 
@@ -5,6 +5,7 @@
5
5
 
6
6
  VALUE cumo_cuda_eRuntimeError;
7
7
  VALUE cumo_cuda_mRuntime;
8
+ uint64_t cumo_cuda_sync_epoch = 0;
8
9
  #define eRuntimeError cumo_cuda_eRuntimeError
9
10
  #define mRuntime cumo_cuda_mRuntime
10
11
 
@@ -42,7 +43,7 @@ cumo_cuda_runtime_error_flag_new(void)
42
43
  bool
43
44
  cumo_cuda_runtime_error_flag_get(int *flag)
44
45
  {
45
- check_status(cudaDeviceSynchronize());
46
+ cumo_cuda_runtime_device_synchronize();
46
47
  return (*flag != 0);
47
48
  }
48
49
 
@@ -189,9 +190,7 @@ rb_cudaSetDevice(VALUE self, VALUE device)
189
190
  static VALUE
190
191
  rb_cudaDeviceSynchronize(VALUE self)
191
192
  {
192
- cudaError_t status;
193
- status = cudaDeviceSynchronize();
194
- check_status(status);
193
+ cumo_cuda_runtime_device_synchronize();
195
194
  return Qnil;
196
195
  }
197
196
 
data/ext/cumo/cumo.c CHANGED
@@ -20,11 +20,27 @@ void Init_cumo_uint8();
20
20
  void Init_cumo_uint16();
21
21
  void Init_cumo_uint32();
22
22
  void Init_cumo_uint64();
23
+ void Init_cumo_hfloat();
23
24
  void Init_cumo_sfloat();
24
25
  void Init_cumo_scomplex();
25
26
  void Init_cumo_dfloat();
26
27
  void Init_cumo_dcomplex();
27
28
  void Init_cumo_robject();
29
+ void Init_cumo_dcomplex_upcast();
30
+ void Init_cumo_dfloat_upcast();
31
+ void Init_cumo_scomplex_upcast();
32
+ void Init_cumo_sfloat_upcast();
33
+ void Init_cumo_hfloat_upcast();
34
+ void Init_cumo_int64_upcast();
35
+ void Init_cumo_uint64_upcast();
36
+ void Init_cumo_int32_upcast();
37
+ void Init_cumo_uint32_upcast();
38
+ void Init_cumo_int16_upcast();
39
+ void Init_cumo_uint16_upcast();
40
+ void Init_cumo_int8_upcast();
41
+ void Init_cumo_uint8_upcast();
42
+ void Init_cumo_bit_upcast();
43
+ void Init_cumo_robject_upcast();
28
44
  void Init_cumo_na_math();
29
45
  void Init_cumo_na_rand();
30
46
  void Init_cumo_na_array();
@@ -150,6 +166,7 @@ Init_cumo()
150
166
  Init_cumo_dfloat();
151
167
  Init_cumo_scomplex();
152
168
  Init_cumo_sfloat();
169
+ Init_cumo_hfloat();
153
170
 
154
171
  Init_cumo_int64();
155
172
  Init_cumo_uint64();
@@ -163,6 +180,23 @@ Init_cumo()
163
180
  Init_cumo_bit();
164
181
  Init_cumo_robject();
165
182
 
183
+ // Every class exists now, so the UPCAST tables can name all of them.
184
+ Init_cumo_dcomplex_upcast();
185
+ Init_cumo_dfloat_upcast();
186
+ Init_cumo_scomplex_upcast();
187
+ Init_cumo_sfloat_upcast();
188
+ Init_cumo_hfloat_upcast();
189
+ Init_cumo_int64_upcast();
190
+ Init_cumo_uint64_upcast();
191
+ Init_cumo_int32_upcast();
192
+ Init_cumo_uint32_upcast();
193
+ Init_cumo_int16_upcast();
194
+ Init_cumo_uint16_upcast();
195
+ Init_cumo_int8_upcast();
196
+ Init_cumo_uint8_upcast();
197
+ Init_cumo_bit_upcast();
198
+ Init_cumo_robject_upcast();
199
+
166
200
  Init_cumo_na_math();
167
201
 
168
202
  Init_cumo_na_rand();
data/ext/cumo/extconf.rb CHANGED
@@ -64,6 +64,7 @@ narray/types/uint8
64
64
  narray/types/uint16
65
65
  narray/types/uint32
66
66
  narray/types/uint64
67
+ narray/types/hfloat
67
68
  narray/types/sfloat
68
69
  narray/types/dfloat
69
70
  narray/types/scomplex
@@ -78,6 +79,7 @@ narray/types/uint8_kernel
78
79
  narray/types/uint16_kernel
79
80
  narray/types/uint32_kernel
80
81
  narray/types/uint64_kernel
82
+ narray/types/hfloat_kernel
81
83
  narray/types/sfloat_kernel
82
84
  narray/types/dfloat_kernel
83
85
  narray/types/scomplex_kernel
@@ -21,7 +21,14 @@ extern VALUE cumo_cuda_eCUDNNError;
21
21
 
22
22
  extern VALUE cumo_na_eShapeError;
23
23
 
24
- #define CUMO_CUDA_CUDNN_DEFAULT_MAX_WORKSPACE_SIZE 8 * 1024 * 1024
24
+ #define CUMO_CUDA_CUDNN_DEFAULT_MAX_WORKSPACE_SIZE (8 * 1024 * 1024)
25
+
26
+ // How much scratch cuDNN may use to pick a convolution algorithm. The fastest
27
+ // half algorithms are the ones that need the most, so the default keeps every
28
+ // dtype on the algorithms it has always used and CUMO_CUDNN_MAX_WORKSPACE_SIZE
29
+ // raises the ceiling for whoever wants them.
30
+ size_t
31
+ cumo_cuda_cudnn_max_workspace_size();
25
32
 
26
33
  // An output array given by the caller is written through a descriptor built
27
34
  // from another operand, or as if it were contiguous, so cuDNN never learns how
@@ -76,6 +83,17 @@ cumo_cuda_cudnn_check_input(VALUE in, VALUE type, size_t ndim, size_t *shape)
76
83
  }
77
84
  }
78
85
 
86
+ // cuDNN derives the batch norm parameter descriptor from x, and widens it to
87
+ // float for a half x, so the parameters do not always take x's own class.
88
+ static inline void
89
+ cumo_cuda_cudnn_check_param_type(VALUE param, VALUE type, const char* name)
90
+ {
91
+ if (rb_obj_class(param) != type) {
92
+ rb_raise(rb_eTypeError, "%s must be %s, not %s",
93
+ name, rb_class2name(type), rb_obj_classname(param));
94
+ }
95
+ }
96
+
79
97
  void
80
98
  cumo_cuda_cudnn_check_status(cudnnStatus_t status);
81
99
 
@@ -172,7 +190,8 @@ cumo_cuda_cudnn_CreateConvolutionDescriptor(
172
190
  size_t ndim,
173
191
  int* int_stride,
174
192
  int* int_pad,
175
- cudnnDataType_t cudnn_dtype);
193
+ cudnnDataType_t compute_dtype,
194
+ cudnnMathType_t math_type);
176
195
 
177
196
  cudnnStatus_t
178
197
  cumo_cuda_cudnn_CreatePoolingDescriptor(
@@ -8,6 +8,7 @@
8
8
  #include <thrust/inner_product.h>
9
9
  #include <thrust/iterator/counting_iterator.h>
10
10
  #include <thrust/iterator/transform_iterator.h>
11
+ #include <thrust/iterator/transform_output_iterator.h>
11
12
  #include <thrust/iterator/permutation_iterator.h>
12
13
  #include <thrust/reduce.h>
13
14
  #include <thrust/scan.h>
@@ -13,6 +13,13 @@ extern "C" {
13
13
 
14
14
  extern VALUE cumo_cuda_eRuntimeError;
15
15
 
16
+ // How many times the whole device has been seen to settle, which is what a host
17
+ // read of managed memory needs. One settling covers every kernel and copy issued
18
+ // before it, so code that recorded the count when it queued work can skip a wait
19
+ // the moment the count has moved on. Only cumo_cuda_runtime_device_synchronize
20
+ // advances it, and a count that is behind costs a wait rather than correctness.
21
+ extern uint64_t cumo_cuda_sync_epoch;
22
+
16
23
  static inline void
17
24
  cumo_cuda_runtime_check_status(cudaError_t status)
18
25
  {
@@ -21,6 +28,27 @@ cumo_cuda_runtime_check_status(cudaError_t status)
21
28
  }
22
29
  }
23
30
 
31
+ static inline void
32
+ cumo_cuda_runtime_device_synchronize(void)
33
+ {
34
+ cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
35
+ cumo_cuda_sync_epoch++;
36
+ }
37
+
38
+ // Asking costs less than half of waiting, and there is nothing to wait for
39
+ // whenever the block stayed off the device. Neither answer advances the settle
40
+ // count: stream 0 going quiet is not the whole device settling, and a host read
41
+ // of managed memory needs the latter where concurrentManagedAccess is 0.
42
+ static inline int
43
+ cumo_cuda_runtime_sync_if_busy(void)
44
+ {
45
+ if (cudaStreamQuery(0) == cudaSuccess) {
46
+ return 0;
47
+ }
48
+ cumo_cuda_runtime_check_status(cudaStreamSynchronize(0));
49
+ return 1;
50
+ }
51
+
24
52
  static inline int
25
53
  cumo_cuda_runtime_get_device_count()
26
54
  {
@@ -33,7 +33,9 @@ char* cumo_na_get_offset_pointer_for_read_write(VALUE);
33
33
 
34
34
  void cumo_na_copy_flags(VALUE src, VALUE dst);
35
35
 
36
+ int cumo_na_last_dim_with_elements(const cumo_narray_t *na, int start_dim);
36
37
  VALUE cumo_na_check_ladder(VALUE self, int start_dim);
38
+ void cumo_na_set_newaxis_strides(cumo_narray_view_t *na2, const int *newaxis, int n_newaxis, int ndim, ssize_t elmsz);
37
39
  VALUE cumo_na_check_contiguous(VALUE self);
38
40
  VALUE cumo_na_as_contiguous_array(VALUE a);
39
41
 
@@ -78,8 +80,9 @@ void cumo_na_parse_enumerator_step(VALUE enum_obj, VALUE *pstep);
78
80
 
79
81
  // used in aref, aset
80
82
  int cumo_na_get_result_dimension(VALUE self, int argc, VALUE *argv, ssize_t stride, size_t *pos_idx);
83
+ void cumo_na_index_check_stray(cumo_narray_view_t *nv);
81
84
  void cumo_na_index_mark_filled(cumo_narray_view_t *nv);
82
- void cumo_na_index_wait_fill(const cumo_narray_view_t *nv);
85
+ void cumo_na_index_wait_fill(cumo_narray_view_t *nv);
83
86
  VALUE cumo_na_aref_main(int nidx, VALUE *idx, VALUE self, int keep_dim, int result_nd, size_t pos);
84
87
  VALUE cumo_na_at_main(int nidx, VALUE *idx, VALUE self, int keep_dim, int result_nd, size_t pos);
85
88
 
@@ -189,6 +189,7 @@ extern const rb_data_type_t cumo_na_data_type;
189
189
  extern VALUE cumo_cBit;
190
190
  extern VALUE cumo_cDFloat;
191
191
  extern VALUE cumo_cSFloat;
192
+ extern VALUE cumo_cHFloat;
192
193
  extern VALUE cumo_cDComplex;
193
194
  extern VALUE cumo_cSComplex;
194
195
  extern VALUE cumo_cInt64;
@@ -220,7 +221,6 @@ typedef struct {
220
221
  unsigned char ndim; // # of dimensions
221
222
  unsigned char type;
222
223
  unsigned char flag[2]; // flags
223
- unsigned short elmsz; // element size
224
224
  size_t size; // # of total elements
225
225
  size_t *shape; // # of elements for each dimension
226
226
  VALUE reduce;
@@ -230,7 +230,6 @@ typedef struct {
230
230
  typedef struct {
231
231
  cumo_narray_t base;
232
232
  char *ptr;
233
- bool owned;
234
233
  } cumo_narray_data_t;
235
234
 
236
235
 
@@ -255,6 +254,12 @@ typedef struct {
255
254
  size_t reach_end;
256
255
  uint64_t index_sync_epoch; // synchronizes counted when the index fills were
257
256
  // issued; UINT64_MAX when that is not known
257
+ // A view made from another one can point at the index arrays it already
258
+ // has rather than copy them. index_owner keeps that view alive for as long
259
+ // as this one borrows from it, and index_owned says which dimensions are
260
+ // this view's own to free.
261
+ VALUE index_owner;
262
+ uint64_t index_owned;
258
263
  } cumo_narray_view_t;
259
264
 
260
265
 
@@ -372,7 +377,6 @@ _cumo_na_get_narray_t(VALUE obj, unsigned char cumo_na_type)
372
377
  #define CUMO_NA_DATA(na) ((cumo_narray_data_t*)(na))
373
378
  #define CUMO_NA_VIEW(na) ((cumo_narray_view_t*)(na))
374
379
  #define CUMO_NA_DATA_PTR(na) (CUMO_NA_DATA(na)->ptr)
375
- #define CUMO_NA_DATA_OWNED(na) (CUMO_NA_DATA(na)->owned)
376
380
  #define CUMO_NA_VIEW_DATA(na) (CUMO_NA_VIEW(na)->data)
377
381
  #define CUMO_NA_VIEW_OFFSET(na) (CUMO_NA_VIEW(na)->offset)
378
382
  #define CUMO_NA_VIEW_STRIDX(na) (CUMO_NA_VIEW(na)->stridx)
@@ -507,9 +511,61 @@ typedef unsigned int CUMO_BIT_DIGIT;
507
511
  #define CUMO_BALL (~(CUMO_BIT_DIGIT)0)
508
512
  #define CUMO_SLB(n) (((n)==CUMO_NB)?~(CUMO_BIT_DIGIT)0:(~(~(CUMO_BIT_DIGIT)0<<(n))))
509
513
 
514
+ // Bits are packed into digits, so a buffer holding n of them is as long as the
515
+ // digits they fill, rounded up. n has to be positive: an empty array has no
516
+ // buffer, and the subtraction below would wrap.
517
+ #define CUMO_BIT_BYTES(n) \
518
+ ((((n) - 1) / 8 / sizeof(CUMO_BIT_DIGIT) + 1) * sizeof(CUMO_BIT_DIGIT))
519
+
510
520
  #include "cumo/ndloop.h"
511
521
  #include "cumo/intern.h"
512
522
 
523
+ // An index array a view holds is freed only if it was set through here, so a
524
+ // view that puts one in stridx any other way leaks it. The bit names the slot,
525
+ // so code that permutes stridx has to permute index_owned with it.
526
+ static inline void
527
+ cumo_na_index_own(cumo_narray_view_t *nv, int i, size_t *idx)
528
+ {
529
+ CUMO_SDX_SET_INDEX(nv->stridx[i], idx);
530
+ nv->index_owned |= (uint64_t)1 << i;
531
+ }
532
+
533
+ // One dimension of nv now points at an index array that from reaches, so from
534
+ // has to outlive nv. A lender that owns nothing itself is skipped for its own
535
+ // lender, which keeps the common chain one link long. A lender that owns even
536
+ // one dimension is named directly, since only it keeps that one alive.
537
+ //
538
+ // A view borrows every index array or none. Borrowing some while building
539
+ // others holds the lender for arrays this view has already replaced, which
540
+ // costs more memory than the copy it saves. One lender is recorded, so a view
541
+ // may not borrow from two.
542
+ static inline void
543
+ cumo_na_index_borrow(cumo_narray_view_t *nv, VALUE from)
544
+ {
545
+ cumo_narray_view_t *nv1;
546
+
547
+ if (RTEST(nv->index_owner)) {
548
+ return;
549
+ }
550
+ CumoGetNArrayView(from, nv1);
551
+ nv->index_owner = (nv1->index_owned == 0 && RTEST(nv1->index_owner))
552
+ ? nv1->index_owner : from;
553
+ }
554
+
555
+ // Called once a view built from another one has all its dimensions. A view that
556
+ // owns index arrays built them here, so a fill is in flight and the first read
557
+ // waits. One that borrows them all is as settled as the view it borrowed from.
558
+ static inline void
559
+ cumo_na_index_mark_derived(cumo_narray_view_t *nv, cumo_narray_view_t *nv1)
560
+ {
561
+ if (nv->index_owned != 0) {
562
+ cumo_na_index_mark_filled(nv);
563
+ } else {
564
+ cumo_na_index_check_stray(nv);
565
+ nv->index_sync_epoch = nv1->index_sync_epoch;
566
+ }
567
+ }
568
+
513
569
  // for Ractor support code
514
570
  #ifndef HAVE_RB_EXT_RACTOR_SAFE
515
571
  # undef RUBY_TYPED_FROZEN_SHAREABLE
@@ -36,17 +36,24 @@
36
36
 
37
37
  // pos is the first position in iteration order, so a step below zero walks down
38
38
  // from there and rebasing the pointer onto pos would underflow the position of
39
- // every element past the first word. A step of zero comes with an index, whose
40
- // entries only ever move forward.
39
+ // every element past the first word. An index holds positions relative to pos
40
+ // and one of them is below zero as soon as the view it came from walks an axis
41
+ // backwards, so pos + idx is right in unsigned arithmetic only while pos is
42
+ // whole. The test reads the iterator rather than the caller's copy of step,
43
+ // which is how a template declaring it as size_t used to lose the sign.
44
+ #define CUMO_REBASE_BIT_PTR( lp, i, ad, ps ) \
45
+ if (((lp)->args[i].iter[0]).step >= 0 && \
46
+ ((lp)->args[i].iter[0]).idx == NULL) { \
47
+ ad += ps/CUMO_NB; \
48
+ ps %= CUMO_NB; \
49
+ }
50
+
41
51
  #define CUMO_INIT_PTR_BIT( lp, i, ad, ps, st ) \
42
52
  { \
43
53
  ps = ((lp)->args[i].iter[0]).pos; \
44
54
  st = ((lp)->args[i].iter[0]).step; \
45
55
  ad = (CUMO_BIT_DIGIT*)(((lp)->args[i]).ptr); \
46
- if (st >= 0) { \
47
- ad += ps/CUMO_NB; \
48
- ps %= CUMO_NB; \
49
- } \
56
+ CUMO_REBASE_BIT_PTR(lp, i, ad, ps); \
50
57
  }
51
58
 
52
59
  #define CUMO_INIT_PTR_BIT_IDX( lp, i, ad, ps, st, id ) \
@@ -55,10 +62,7 @@
55
62
  st = ((lp)->args[i].iter[0]).step; \
56
63
  id = ((lp)->args[i].iter[0]).idx; \
57
64
  ad = (CUMO_BIT_DIGIT*)(((lp)->args[i]).ptr); \
58
- if (st >= 0) { \
59
- ad += ps/CUMO_NB; \
60
- ps %= CUMO_NB; \
61
- } \
65
+ CUMO_REBASE_BIT_PTR(lp, i, ad, ps); \
62
66
  }
63
67
 
64
68
  // A where compaction takes three launches, so below this many elements walking
@@ -179,4 +183,25 @@ cumo_na_store_rary_fetch(VALUE ary, size_t i, VALUE *x)
179
183
  return true;
180
184
  }
181
185
 
186
+ // A 0-dimensional walk still yields one index, so the depth floors at 0 rather
187
+ // than going negative and writing c[-1].
188
+ static inline void
189
+ cumo_na_with_index_dims(int ndim, int *nd, int *md)
190
+ {
191
+ *nd = (ndim > 0) ? ndim - 1 : ndim;
192
+ *md = *nd + 2;
193
+ }
194
+
195
+ static inline VALUE
196
+ cumo_na_yield_with_index(VALUE x, size_t *c, VALUE *a, int nd, int md)
197
+ {
198
+ int j;
199
+
200
+ a[0] = x;
201
+ for (j=0; j<=nd; j++) {
202
+ a[j+1] = SIZET2NUM(c[j]);
203
+ }
204
+ return rb_yield(rb_ary_new4(md,a));
205
+ }
206
+
182
207
  #endif /* ifndef CUMO_TEMPLATE_H */