cumo 0.7.0 → 0.9.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +590 -152
  3. data/CHANGELOG.md +63 -0
  4. data/README.md +245 -0
  5. data/bench/cumo_probe.rb +1 -1
  6. data/cumo.gemspec +7 -1
  7. data/ext/cumo/cuda/cudnn.c +18 -0
  8. data/ext/cumo/cuda/cudnn_impl.cpp +60 -14
  9. data/ext/cumo/cuda/driver.c +11 -9
  10. data/ext/cumo/cuda/memory_pool_impl.cpp +32 -22
  11. data/ext/cumo/cuda/memory_pool_impl.hpp +3 -1
  12. data/ext/cumo/cuda/memory_pool_impl_test.cpp +19 -25
  13. data/ext/cumo/cuda/runtime.c +56 -0
  14. data/ext/cumo/cumo.c +4 -0
  15. data/ext/cumo/extconf.rb +2 -0
  16. data/ext/cumo/include/cumo/bit_reduce_kernel.h +6 -6
  17. data/ext/cumo/include/cumo/check.h +11 -3
  18. data/ext/cumo/include/cumo/cuda/cudnn.h +43 -6
  19. data/ext/cumo/include/cumo/cuda/runtime.h +2 -0
  20. data/ext/cumo/include/cumo/indexer.h +16 -0
  21. data/ext/cumo/include/cumo/intern.h +11 -0
  22. data/ext/cumo/include/cumo/narray.h +1 -0
  23. data/ext/cumo/include/cumo/reduce_kernel.h +283 -54
  24. data/ext/cumo/include/cumo/row_kernel.h +202 -0
  25. data/ext/cumo/include/cumo/row_method.h +149 -0
  26. data/ext/cumo/include/cumo/template_kernel.h +26 -0
  27. data/ext/cumo/include/cumo/types/bf16_def.h +82 -0
  28. data/ext/cumo/include/cumo/types/bf16_def_kernel.h +28 -0
  29. data/ext/cumo/include/cumo/types/bf16_macro.h +14 -0
  30. data/ext/cumo/include/cumo/types/bf16_macro_kernel.h +19 -0
  31. data/ext/cumo/include/cumo/types/bfloat.h +22 -0
  32. data/ext/cumo/include/cumo/types/bfloat_kernel.h +16 -0
  33. data/ext/cumo/include/cumo/types/bit.h +2 -0
  34. data/ext/cumo/include/cumo/types/bit_kernel.h +2 -0
  35. data/ext/cumo/include/cumo/types/complex_macro.h +2 -0
  36. data/ext/cumo/include/cumo/types/complex_macro_kernel.h +2 -0
  37. data/ext/cumo/include/cumo/types/f16_macro.h +191 -0
  38. data/ext/cumo/include/cumo/types/f16_macro_kernel.h +214 -0
  39. data/ext/cumo/include/cumo/types/float_def.h +8 -0
  40. data/ext/cumo/include/cumo/types/float_def_kernel.h +8 -0
  41. data/ext/cumo/include/cumo/types/float_macro.h +26 -17
  42. data/ext/cumo/include/cumo/types/float_macro_kernel.h +26 -19
  43. data/ext/cumo/include/cumo/types/half_macro.h +6 -180
  44. data/ext/cumo/include/cumo/types/half_macro_kernel.h +6 -185
  45. data/ext/cumo/include/cumo/types/robj_macro.h +2 -0
  46. data/ext/cumo/include/cumo/types/xint_macro.h +2 -6
  47. data/ext/cumo/include/cumo/types/xint_macro_kernel.h +2 -0
  48. data/ext/cumo/include/cumo.h +2 -2
  49. data/ext/cumo/narray/data.c +15 -6
  50. data/ext/cumo/narray/data_kernel.cu +110 -0
  51. data/ext/cumo/narray/gen/def/bfloat.rb +57 -0
  52. data/ext/cumo/narray/gen/def/bit.rb +12 -1
  53. data/ext/cumo/narray/gen/def/dcomplex.rb +12 -1
  54. data/ext/cumo/narray/gen/def/dfloat.rb +11 -1
  55. data/ext/cumo/narray/gen/def/hfloat.rb +11 -1
  56. data/ext/cumo/narray/gen/def/int16.rb +12 -1
  57. data/ext/cumo/narray/gen/def/int32.rb +12 -1
  58. data/ext/cumo/narray/gen/def/int64.rb +12 -1
  59. data/ext/cumo/narray/gen/def/int8.rb +12 -1
  60. data/ext/cumo/narray/gen/def/robject.rb +12 -1
  61. data/ext/cumo/narray/gen/def/scomplex.rb +12 -1
  62. data/ext/cumo/narray/gen/def/sfloat.rb +11 -1
  63. data/ext/cumo/narray/gen/def/uint16.rb +12 -1
  64. data/ext/cumo/narray/gen/def/uint32.rb +12 -1
  65. data/ext/cumo/narray/gen/def/uint64.rb +12 -1
  66. data/ext/cumo/narray/gen/def/uint8.rb +12 -1
  67. data/ext/cumo/narray/gen/narray_def.rb +35 -6
  68. data/ext/cumo/narray/gen/spec.rb +14 -14
  69. data/ext/cumo/narray/gen/tmpl/accum_arg.c +1 -1
  70. data/ext/cumo/narray/gen/tmpl/accum_binary.c +63 -5
  71. data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +29 -13
  72. data/ext/cumo/narray/gen/tmpl/accum_index.c +1 -1
  73. data/ext/cumo/narray/gen/tmpl/batch_norm.c +4 -4
  74. data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +5 -5
  75. data/ext/cumo/narray/gen/tmpl/binary.c +3 -9
  76. data/ext/cumo/narray/gen/tmpl/complex_accum_kernel.cu +15 -5
  77. data/ext/cumo/narray/gen/tmpl/conv.c +90 -123
  78. data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +91 -83
  79. data/ext/cumo/narray/gen/tmpl/conv_transpose.c +90 -123
  80. data/ext/cumo/narray/gen/tmpl/cum.c +8 -7
  81. data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +7 -6
  82. data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +4 -4
  83. data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +22 -10
  84. data/ext/cumo/narray/gen/tmpl/gemm.c +11 -41
  85. data/ext/cumo/narray/gen/tmpl/layer_norm.c +38 -0
  86. data/ext/cumo/narray/gen/tmpl/layer_norm_kernel.cu +54 -0
  87. data/ext/cumo/narray/gen/tmpl/lib.c +1 -0
  88. data/ext/cumo/narray/gen/tmpl/lib_kernel.cu +1 -0
  89. data/ext/cumo/narray/gen/tmpl/median.c +1 -1
  90. data/ext/cumo/narray/gen/tmpl/poly.c +0 -3
  91. data/ext/cumo/narray/gen/tmpl/poly_kernel.cu +1 -1
  92. data/ext/cumo/narray/gen/tmpl/pooling_backward.c +3 -2
  93. data/ext/cumo/narray/gen/tmpl/pooling_forward.c +3 -2
  94. data/ext/cumo/narray/gen/tmpl/quantize_symmetric.c +74 -0
  95. data/ext/cumo/narray/gen/tmpl/quantize_symmetric_kernel.cu +67 -0
  96. data/ext/cumo/narray/gen/tmpl/rand.c +9 -9
  97. data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +9 -9
  98. data/ext/cumo/narray/gen/tmpl/rand_norm.c +4 -4
  99. data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +7 -7
  100. data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +6 -6
  101. data/ext/cumo/narray/gen/tmpl/rms_norm.c +38 -0
  102. data/ext/cumo/narray/gen/tmpl/rms_norm_kernel.cu +57 -0
  103. data/ext/cumo/narray/gen/tmpl/softmax.c +21 -0
  104. data/ext/cumo/narray/gen/tmpl/softmax_kernel.cu +157 -0
  105. data/ext/cumo/narray/gen/tmpl/sort.c +9 -32
  106. data/ext/cumo/narray/gen/tmpl/sort_index.c +16 -95
  107. data/ext/cumo/narray/gen/tmpl/store_array.c +12 -9
  108. data/ext/cumo/narray/gen/tmpl/store_from.c +1 -16
  109. data/ext/cumo/narray/gen/tmpl_bit/mask.c +11 -11
  110. data/ext/cumo/narray/gen/tmpl_bit/store_array.c +5 -9
  111. data/ext/cumo/narray/gen/tmpl_bit/where.c +50 -11
  112. data/ext/cumo/narray/gen/tmpl_bit/where2.c +11 -12
  113. data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +1 -2
  114. data/ext/cumo/narray/index.c +35 -25
  115. data/ext/cumo/narray/index_kernel.cu +27 -0
  116. data/ext/cumo/narray/math.c +40 -8
  117. data/ext/cumo/narray/narray.c +85 -15
  118. data/ext/cumo/narray/ndloop.c +210 -5
  119. data/ext/cumo/narray/sort_kernel.cu +61 -41
  120. data/ext/cumo/narray/struct.c +3 -0
  121. data/lib/cumo/cuda/cudnn.rb +1 -1
  122. data/lib/cumo/narray/extra.rb +2 -2
  123. data/test/bfloat_test.rb +238 -0
  124. data/test/bit_test.rb +52 -14
  125. data/test/cuda/memory_pool_test.rb +8 -7
  126. data/test/cuda/runtime_test.rb +25 -0
  127. data/test/cudnn_test.rb +108 -24
  128. data/test/extra_test.rb +1 -1
  129. data/test/fused_test.rb +587 -0
  130. data/test/math_test.rb +182 -2
  131. data/test/narray_alt_coverage_test.rb +1 -0
  132. data/test/narray_test.rb +344 -22
  133. data/test/test_helper.rb +1 -0
  134. metadata +32 -5
  135. data/ext/cumo/narray/gen/tmpl/qsort.c +0 -205
@@ -108,7 +108,16 @@ bool SingleDeviceMemoryPool::RemoveFromFreeList(size_t size, std::shared_ptr<Chu
108
108
  }
109
109
  assert(arena.size() > arena_index);
110
110
  FreeList& free_list = arena[arena_index];
111
- return EraseFromFreeList(free_list, chunk);
111
+ if (!EraseFromFreeList(free_list, chunk)) {
112
+ return false;
113
+ }
114
+ // A bin left empty here would be walked by every later search, and the
115
+ // merge path below Free empties one whenever it takes a neighbour out.
116
+ if (free_list.empty()) {
117
+ arena.erase(arena.begin() + arena_index);
118
+ arena_index_map.erase(arena_index_map.begin() + arena_index);
119
+ }
120
+ return true;
112
121
  }
113
122
 
114
123
  intptr_t SingleDeviceMemoryPool::Malloc(size_t size, cudaStream_t stream_ptr) {
@@ -132,6 +141,7 @@ intptr_t SingleDeviceMemoryPool::Malloc(size_t size, cudaStream_t stream_ptr) {
132
141
 
133
142
  // find best-fit, or a smallest larger allocation
134
143
  Arena& arena = GetArena(stream_ptr);
144
+ ArenaIndexMap& arena_index_map = GetArenaIndexMap(stream_ptr);
135
145
  size_t arena_index = GetArenaIndex(size, stream_ptr);
136
146
  size_t arena_length = arena.size();
137
147
  for (size_t i = arena_index; i < arena_length; ++i) {
@@ -141,10 +151,15 @@ intptr_t SingleDeviceMemoryPool::Malloc(size_t size, cudaStream_t stream_ptr) {
141
151
  }
142
152
  chunk = PopFromFreeList(free_list);
143
153
  if (free_list.empty()) {
144
- // An emptied bin stays in the arena otherwise, and every later
145
- // search walks it. Dropping it here invalidates arena and
146
- // free_list, so nothing below this loop may touch them.
147
- CompactIndex(stream_ptr, false);
154
+ // Drop just this bin. Rebuilding the whole arena instead
155
+ // copied every other bin's free list, and a free list holds
156
+ // shared pointers, so a fragmented pool touched the reference
157
+ // count of every chunk it held on every allocation.
158
+ // This leaves free_list, the elements of arena_index_map and
159
+ // every iterator from i on dangling, so nothing below may
160
+ // touch them.
161
+ arena.erase(arena.begin() + i);
162
+ arena_index_map.erase(arena_index_map.begin() + i);
148
163
  }
149
164
  break;
150
165
  }
@@ -252,7 +267,7 @@ bool SingleDeviceMemoryPool::Free(intptr_t ptr) {
252
267
  return true;
253
268
  }
254
269
 
255
- void SingleDeviceMemoryPool::CompactIndex(cudaStream_t stream_ptr, bool free) {
270
+ void SingleDeviceMemoryPool::CompactIndex(cudaStream_t stream_ptr) {
256
271
  // need lock ouside this function
257
272
  if (!HasArena(stream_ptr)) return;
258
273
 
@@ -266,22 +281,17 @@ void SingleDeviceMemoryPool::CompactIndex(cudaStream_t stream_ptr, bool free) {
266
281
  if (free_list.empty()) {
267
282
  continue;
268
283
  }
269
- if (free) {
270
- FreeList keep_list;
271
- for (auto chunk : free_list) {
272
- if (chunk->prev() != nullptr || chunk->next() != nullptr) {
273
- keep_list.emplace_back(chunk);
274
- }
275
- }
276
- if (keep_list.size() == 0) {
277
- continue;
284
+ FreeList keep_list;
285
+ for (auto chunk : free_list) {
286
+ if (chunk->prev() != nullptr || chunk->next() != nullptr) {
287
+ keep_list.emplace_back(chunk);
278
288
  }
279
- new_arena_index_map.emplace_back(arena_index_map[arena_index]);
280
- new_arena.emplace_back(keep_list);
281
- } else {
282
- new_arena_index_map.emplace_back(arena_index_map[arena_index]);
283
- new_arena.emplace_back(free_list);
284
289
  }
290
+ if (keep_list.size() == 0) {
291
+ continue;
292
+ }
293
+ new_arena_index_map.emplace_back(arena_index_map[arena_index]);
294
+ new_arena.emplace_back(keep_list);
285
295
  }
286
296
  if (new_arena.empty()) {
287
297
  index_.erase(stream_ptr);
@@ -299,7 +309,7 @@ void SingleDeviceMemoryPool::FreeAllBlocks() {
299
309
  std::vector<cudaStream_t> keys(free_.size());
300
310
  transform(free_.begin(), free_.end(), keys.begin(), [](auto pair) { return pair.first; });
301
311
  for (cudaStream_t stream_ptr : keys) {
302
- CompactIndex(stream_ptr, true);
312
+ CompactIndex(stream_ptr);
303
313
  }
304
314
  }
305
315
 
@@ -307,7 +317,7 @@ void SingleDeviceMemoryPool::FreeAllBlocks() {
307
317
  void SingleDeviceMemoryPool::FreeAllBlocks(cudaStream_t stream_ptr) {
308
318
  std::lock_guard<std::recursive_mutex> lock{mutex_};
309
319
 
310
- CompactIndex(stream_ptr, true);
320
+ CompactIndex(stream_ptr);
311
321
  }
312
322
 
313
323
  size_t SingleDeviceMemoryPool::GetNumFreeBlocks() {
@@ -284,7 +284,9 @@ public:
284
284
  // be found in the free list as the chunk is allocated.)
285
285
  bool RemoveFromFreeList(size_t size, std::shared_ptr<Chunk>& chunk, cudaStream_t stream_ptr);
286
286
 
287
- void CompactIndex(cudaStream_t stream_ptr, bool free);
287
+ // Drops every chunk that was never split from a larger one, and every
288
+ // bin left empty by that.
289
+ void CompactIndex(cudaStream_t stream_ptr);
288
290
  };
289
291
 
290
292
  // Memory pool for all GPU devices on the host.
@@ -337,10 +337,10 @@ public:
337
337
  assert(pool_->GetFreeBytes() == kRoundSize * 8);
338
338
  }
339
339
 
340
- // RemoveFromFreeList leaves an emptied bin in the arena -- it erases the
341
- // chunk and returns, where Malloc drops one through CompactIndex. The
342
- // assertions below spell that out, so they describe the arena as it is
343
- // rather than wait on a compaction this path is not getting.
340
+ // RemoveFromFreeList drops a bin its erase leaves empty, the same as
341
+ // Malloc. The merge path below Free empties one whenever it takes a
342
+ // neighbour out, so a bin left behind here would be walked by every later
343
+ // search.
344
344
  void TestRemoveFromFreeList() {
345
345
  Arena& arena = pool_->GetArena(stream_ptr_);
346
346
  ArenaIndexMap& arena_index_map = pool_->GetArenaIndexMap(stream_ptr_);
@@ -372,36 +372,30 @@ public:
372
372
  assert(arena_index_map[1] == 3);
373
373
  assert(arena_index_map[2] == 4);
374
374
 
375
- // remove two from two
375
+ // remove two from two, which empties that bin
376
376
  pool_->RemoveFromFreeList(chunk2->size(), chunk2, stream_ptr_);
377
- assert(arena.size() == 3);
377
+ assert(arena.size() == 2);
378
378
  assert(arena[0].size() == 1);
379
- assert(arena[1].size() == 0);
380
- assert(arena[2].size() == 1);
381
- assert(arena_index_map.size() == 3);
379
+ assert(arena[1].size() == 1);
380
+ assert(arena_index_map.size() == 2);
382
381
  assert(arena_index_map[0] == 2);
383
- assert(arena_index_map[1] == 3);
384
- assert(arena_index_map[2] == 4);
382
+ assert(arena_index_map[1] == 4);
385
383
 
386
384
  pool_->RemoveFromFreeList(chunk3->size(), chunk3, stream_ptr_);
387
- assert(arena.size() == 3);
385
+ assert(arena.size() == 1);
388
386
  assert(arena[0].size() == 1);
389
- assert(arena[1].size() == 0);
390
- assert(arena[2].size() == 0);
391
- assert(arena_index_map.size() == 3);
387
+ assert(arena_index_map.size() == 1);
392
388
  assert(arena_index_map[0] == 2);
393
- assert(arena_index_map[1] == 3);
394
- assert(arena_index_map[2] == 4);
395
389
 
396
390
  pool_->RemoveFromFreeList(chunk4->size(), chunk4, stream_ptr_);
397
- assert(arena.size() == 3);
398
- assert(arena[0].size() == 0);
399
- assert(arena[1].size() == 0);
400
- assert(arena[2].size() == 0);
401
- assert(arena_index_map.size() == 3);
402
- assert(arena_index_map[0] == 2);
403
- assert(arena_index_map[1] == 3);
404
- assert(arena_index_map[2] == 4);
391
+ assert(arena.size() == 0);
392
+ assert(arena_index_map.size() == 0);
393
+
394
+ // A chunk that is not in the free list leaves the arena alone.
395
+ pool_->AppendToFreeList(chunk1->size(), chunk1, stream_ptr_);
396
+ assert(!pool_->RemoveFromFreeList(chunk2->size(), chunk2, stream_ptr_));
397
+ assert(arena.size() == 1);
398
+ assert(arena[0].size() == 1);
405
399
  }
406
400
 
407
401
  void TestMalloc() {
@@ -2,6 +2,8 @@
2
2
  #include <assert.h>
3
3
  #include <cuda_runtime.h>
4
4
  #include "cumo/cuda/runtime.h"
5
+ #include "cumo/cuda/memory_pool.h"
6
+ #include "cumo/template_kernel.h"
5
7
 
6
8
  VALUE cumo_cuda_eRuntimeError;
7
9
  VALUE cumo_cuda_mRuntime;
@@ -20,6 +22,60 @@ cumo_cuda_runtime_check_kernel_launch(void)
20
22
  check_status(cudaGetLastError());
21
23
  }
22
24
 
25
+ // A pointer given twice would be freed twice, and the second free would reach
26
+ // past the pool to a block it still owns, so each one goes back once.
27
+ static void
28
+ release_held(char *p[5])
29
+ {
30
+ int i, j;
31
+ for (i = 0; i < 5; ++i) {
32
+ if (p[i] == NULL) { continue; }
33
+ for (j = 0; j < i; ++j) {
34
+ if (p[j] == p[i]) { break; }
35
+ }
36
+ if (j == i) { cumo_cuda_runtime_free_no_raise(p[i]); }
37
+ }
38
+ }
39
+
40
+ // What a launcher holding scratch has to call instead of the plain check, for a
41
+ // status it already took out of the slot: a library call in the window takes it
42
+ // first otherwise, and whoever reads it first is the only one who sees it.
43
+ // Raising is a longjmp, so the frees written below the check never run, and
44
+ // unwinding frees through the form that cannot raise so one failure cannot hide
45
+ // another.
46
+ void
47
+ cumo_cuda_runtime_check_taken_status_holding(int status, char *p0, char *p1, char *p2, char *p3, char *p4)
48
+ {
49
+ if ((cudaError_t)status != cudaSuccess) {
50
+ char *held[5];
51
+ held[0] = p0; held[1] = p1; held[2] = p2; held[3] = p3; held[4] = p4;
52
+ release_held(held);
53
+ }
54
+ check_status((cudaError_t)status);
55
+ }
56
+
57
+ // The slot is read once here rather than peeked and read again: the frees below
58
+ // can put a status of their own in it, and the launch is the one to report.
59
+ void
60
+ cumo_cuda_runtime_check_kernel_launch_holding(char *p0, char *p1, char *p2, char *p3, char *p4)
61
+ {
62
+ cumo_cuda_runtime_check_taken_status_holding((int)cudaGetLastError(), p0, p1, p2, p3, p4);
63
+ }
64
+
65
+ // How scratch goes back from a handler that may be unwinding: a free that
66
+ // raises there replaces the exception being carried, and leaves every free
67
+ // below it unrun. wait_for_stream asks for the wait a caller needs when the
68
+ // buffer is still being read, since the pool hands a freed chunk straight out.
69
+ void
70
+ cumo_cuda_runtime_return_scratch(char *ptr, int wait_for_stream, cudaError_t *status)
71
+ {
72
+ if (wait_for_stream) {
73
+ cudaError_t wait = cudaStreamSynchronize(0);
74
+ if (status != NULL && *status == cudaSuccess) { *status = wait; }
75
+ }
76
+ if (ptr != NULL) { cumo_cuda_runtime_free_no_raise(ptr); }
77
+ }
78
+
23
79
  int*
24
80
  cumo_cuda_runtime_error_flag_ptr(void)
25
81
  {
data/ext/cumo/cumo.c CHANGED
@@ -21,6 +21,7 @@ void Init_cumo_uint16();
21
21
  void Init_cumo_uint32();
22
22
  void Init_cumo_uint64();
23
23
  void Init_cumo_hfloat();
24
+ void Init_cumo_bfloat();
24
25
  void Init_cumo_sfloat();
25
26
  void Init_cumo_scomplex();
26
27
  void Init_cumo_dfloat();
@@ -31,6 +32,7 @@ void Init_cumo_dfloat_upcast();
31
32
  void Init_cumo_scomplex_upcast();
32
33
  void Init_cumo_sfloat_upcast();
33
34
  void Init_cumo_hfloat_upcast();
35
+ void Init_cumo_bfloat_upcast();
34
36
  void Init_cumo_int64_upcast();
35
37
  void Init_cumo_uint64_upcast();
36
38
  void Init_cumo_int32_upcast();
@@ -167,6 +169,7 @@ Init_cumo()
167
169
  Init_cumo_scomplex();
168
170
  Init_cumo_sfloat();
169
171
  Init_cumo_hfloat();
172
+ Init_cumo_bfloat();
170
173
 
171
174
  Init_cumo_int64();
172
175
  Init_cumo_uint64();
@@ -186,6 +189,7 @@ Init_cumo()
186
189
  Init_cumo_scomplex_upcast();
187
190
  Init_cumo_sfloat_upcast();
188
191
  Init_cumo_hfloat_upcast();
192
+ Init_cumo_bfloat_upcast();
189
193
  Init_cumo_int64_upcast();
190
194
  Init_cumo_uint64_upcast();
191
195
  Init_cumo_int32_upcast();
data/ext/cumo/extconf.rb CHANGED
@@ -65,6 +65,7 @@ narray/types/uint16
65
65
  narray/types/uint32
66
66
  narray/types/uint64
67
67
  narray/types/hfloat
68
+ narray/types/bfloat
68
69
  narray/types/sfloat
69
70
  narray/types/dfloat
70
71
  narray/types/scomplex
@@ -80,6 +81,7 @@ narray/types/uint16_kernel
80
81
  narray/types/uint32_kernel
81
82
  narray/types/uint64_kernel
82
83
  narray/types/hfloat_kernel
84
+ narray/types/bfloat_kernel
83
85
  narray/types/sfloat_kernel
84
86
  narray/types/dfloat_kernel
85
87
  narray/types/scomplex_kernel
@@ -442,7 +442,7 @@ static inline void cumo_bit_count_reduce(cumo_na_bit_reduction_arg_t arg, int in
442
442
  cumo_bit_detail::bit_count_partial_kernel<<<grid_size, block_size, shared_mem_size>>>(
443
443
  arg, p.ad, p.wa, invert, partial, p.n_split, p.chunk,
444
444
  (int)p.split_out_block_size, (int)p.split_reduce_block_size, p.unit_total_size);
445
- cumo_cuda_runtime_check_kernel_launch();
445
+ cumo_check_launch_holding(partial);
446
446
 
447
447
  cumo_na_reduction_arg_t combine;
448
448
  combine.in.ptr = reinterpret_cast<char*>(partial);
@@ -452,7 +452,7 @@ static inline void cumo_bit_count_reduce(cumo_na_bit_reduction_arg_t arg, int in
452
452
  combine.in_indexer.total_size = partial_total_size;
453
453
  combine.out = arg.out;
454
454
  combine.out_indexer = arg.out_indexer;
455
- cumo_reduce<uint64_t, uint64_t>(combine, cumo_bit_detail::BitCountImpl{});
455
+ cumo_reduce<uint64_t, uint64_t>(combine, cumo_bit_detail::BitCountImpl{}, reinterpret_cast<char*>(partial));
456
456
 
457
457
  cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
458
458
  }
@@ -483,12 +483,12 @@ static inline void cumo_bit_stat_reduce(cumo_na_bit_reduction_arg_t arg, int sta
483
483
  cumo_bit_detail::bit_count_partial_kernel<<<grid_size, block_size, shared_mem_size>>>(
484
484
  arg, p.ad, p.wa, 0, partial, p.n_split, p.chunk,
485
485
  (int)p.split_out_block_size, (int)p.split_reduce_block_size, p.unit_total_size);
486
- cumo_cuda_runtime_check_kernel_launch();
486
+ cumo_check_launch_holding(partial);
487
487
 
488
488
  int64_t combine_grid = (p.out_total_size + block_size - 1) / block_size;
489
489
  if (combine_grid > cumo_detail::max_grid_size) combine_grid = cumo_detail::max_grid_size;
490
490
  cumo_bit_detail::bit_stat_combine_kernel<<<combine_grid, block_size>>>(arg, p.ad, stat, partial, p.n_split);
491
- cumo_cuda_runtime_check_kernel_launch();
491
+ cumo_check_launch_holding(partial);
492
492
 
493
493
  cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
494
494
  }
@@ -519,12 +519,12 @@ static inline void cumo_bit_pred_reduce(cumo_na_bit_pred_reduction_arg_t arg, in
519
519
  cumo_bit_detail::bit_count_partial_kernel<<<grid_size, block_size, shared_mem_size>>>(
520
520
  arg, p.ad, p.wa, 0, partial, p.n_split, p.chunk,
521
521
  (int)p.split_out_block_size, (int)p.split_reduce_block_size, p.unit_total_size);
522
- cumo_cuda_runtime_check_kernel_launch();
522
+ cumo_check_launch_holding(partial);
523
523
 
524
524
  int64_t combine_grid = (p.out_total_size + block_size - 1) / block_size;
525
525
  if (combine_grid > cumo_detail::max_grid_size) combine_grid = cumo_detail::max_grid_size;
526
526
  cumo_bit_detail::bit_pred_combine_kernel<<<combine_grid, block_size>>>(arg, p.ad, all, partial, p.n_split);
527
- cumo_cuda_runtime_check_kernel_launch();
527
+ cumo_check_launch_holding(partial);
528
528
 
529
529
  cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
530
530
  }
@@ -11,10 +11,18 @@ extern "C" {
11
11
  #endif
12
12
  #endif
13
13
 
14
- #define CUMO_CHECK_NARRAY_TYPE(x,t) \
15
- if (rb_obj_class(x)!=(t)) { \
16
- rb_raise(rb_eTypeError,"invalid NArray type (class)"); \
14
+ // Two of these often sit side by side, so the message has to say which array
15
+ // was turned down as well as what it is.
16
+ static inline void
17
+ cumo_na_check_narray_type(VALUE x, VALUE type, const char *name)
18
+ {
19
+ if (rb_obj_class(x) != type) {
20
+ rb_raise(rb_eTypeError, "%s must be %" PRIsVALUE ", not %" PRIsVALUE,
21
+ name, type, rb_obj_class(x));
17
22
  }
23
+ }
24
+
25
+ #define CUMO_CHECK_NARRAY_TYPE(x,t,name) cumo_na_check_narray_type((x),(t),(name))
18
26
 
19
27
  #define CUMO_CHECK_SIZE_EQ(sz1,sz2) \
20
28
  if ((sz1) != (sz2)) { \
@@ -38,7 +38,7 @@ cumo_cuda_cudnn_check_output(VALUE out, VALUE type, size_t ndim, size_t *shape)
38
38
  {
39
39
  cumo_narray_t *na;
40
40
 
41
- CUMO_CHECK_NARRAY_TYPE(out, type);
41
+ CUMO_CHECK_NARRAY_TYPE(out, type, "output");
42
42
  CumoGetNArray(out, na);
43
43
  CUMO_CHECK_DIM_EQ((size_t)(na->ndim), ndim);
44
44
  for (size_t idim = 0; idim < ndim; ++idim) {
@@ -75,7 +75,7 @@ cumo_cuda_cudnn_check_input(VALUE in, VALUE type, size_t ndim, size_t *shape)
75
75
  {
76
76
  cumo_narray_t *na;
77
77
 
78
- CUMO_CHECK_NARRAY_TYPE(in, type);
78
+ CUMO_CHECK_NARRAY_TYPE(in, type, "input");
79
79
  CumoGetNArray(in, na);
80
80
  CUMO_CHECK_DIM_EQ((size_t)(na->ndim), ndim);
81
81
  for (size_t idim = 0; idim < ndim; ++idim) {
@@ -88,18 +88,48 @@ cumo_cuda_cudnn_check_input(VALUE in, VALUE type, size_t ndim, size_t *shape)
88
88
  static inline void
89
89
  cumo_cuda_cudnn_check_param_type(VALUE param, VALUE type, const char* name)
90
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
- }
91
+ CUMO_CHECK_NARRAY_TYPE(param, type, name);
95
92
  }
96
93
 
97
94
  void
98
95
  cumo_cuda_cudnn_check_status(cudnnStatus_t status);
99
96
 
97
+ // Raises, so the methods that clean up through a label take it before they
98
+ // hold a descriptor: a raise runs no label.
100
99
  cudnnHandle_t
101
100
  cumo_cuda_cudnn_handle();
102
101
 
102
+ // What a convolution holds while it works. A raise runs no error label, so the
103
+ // work goes under rb_ensure and the handler gives these back instead.
104
+ typedef struct {
105
+ cudnnTensorDescriptor_t x_desc;
106
+ cudnnTensorDescriptor_t y_desc;
107
+ cudnnTensorDescriptor_t b_desc;
108
+ cudnnFilterDescriptor_t w_desc;
109
+ cudnnConvolutionDescriptor_t conv_desc;
110
+ char *workspace;
111
+ cudaError_t wait_status;
112
+ } cumo_cuda_cudnn_conv_held_t;
113
+
114
+ // Takes the held as its argument so it can be handed to rb_ensure as it is.
115
+ VALUE
116
+ cumo_cuda_cudnn_release_conv_held(VALUE held);
117
+
118
+ // What conv and conv_transpose carry into the run the ensure above guards.
119
+ typedef struct {
120
+ cumo_cuda_cudnn_conv_held_t held;
121
+ cudnnHandle_t handle;
122
+ VALUE x_cont, w_cont, y, b_cont;
123
+ char *x_cont_ptr, *w_cont_ptr, *y_ptr, *b_cont_ptr;
124
+ size_t ndim;
125
+ int *int_stride, *int_pad;
126
+ cudnnStatus_t status;
127
+ } cumo_cuda_cudnn_conv_run_t;
128
+
129
+ // Adds nothing when the run carries no bias, so callers need no guard.
130
+ cudnnStatus_t
131
+ cumo_cuda_cudnn_AddBias(cumo_cuda_cudnn_conv_run_t *r, cudnnDataType_t dtype, const void *one);
132
+
103
133
  // VALUE is Ruby Array
104
134
  static inline void
105
135
  cumo_cuda_cudnn_get_int_ary(int* int_ary, VALUE ary, size_t ndim, int default_value)
@@ -178,6 +208,13 @@ cumo_cuda_cudnn_CreateTensorDescriptor(
178
208
  VALUE a,
179
209
  cudnnDataType_t cudnn_dtype);
180
210
 
211
+ cudnnStatus_t
212
+ cumo_cuda_cudnn_CreateTensorDescriptorFromShape(
213
+ cudnnTensorDescriptor_t *desc,
214
+ int ndim,
215
+ size_t *shape,
216
+ cudnnDataType_t cudnn_dtype);
217
+
181
218
  cudnnStatus_t
182
219
  cumo_cuda_cudnn_CreateFilterDescriptor(
183
220
  cudnnFilterDescriptor_t *desc,
@@ -28,6 +28,8 @@ cumo_cuda_runtime_check_status(cudaError_t status)
28
28
  }
29
29
  }
30
30
 
31
+ void cumo_cuda_runtime_return_scratch(char *ptr, int wait_for_stream, cudaError_t *status);
32
+
31
33
  static inline void
32
34
  cumo_cuda_runtime_device_synchronize(void)
33
35
  {
@@ -176,6 +176,22 @@ cumo_na_make_iarray_stridx(cumo_na_loop_args_t* arg)
176
176
  return iarray;
177
177
  }
178
178
 
179
+ // Whether any argument of the loop is backed by an index array. With
180
+ // INDEX_LOOP an operand can arrive carrying one, and cumo_na_iarray_t holds
181
+ // byte steps only: an indexed dimension has a step of zero there, so a loop
182
+ // that answers yes has to address its operands through the stridx form above.
183
+ static int
184
+ cumo_na_loop_has_index(cumo_na_loop_t *const lp)
185
+ {
186
+ int j, i;
187
+ for (j = 0; j < lp->narg; ++j) {
188
+ for (i = 0; i < lp->args[j].ndim; ++i) {
189
+ if (lp->args[j].iter[i].idx) return 1;
190
+ }
191
+ }
192
+ return 0;
193
+ }
194
+
179
195
  static cumo_na_bit_iarray_stridx_t
180
196
  cumo_na_make_bit_iarray_stridx(cumo_na_loop_args_t* arg)
181
197
  {
@@ -54,6 +54,8 @@ void cumo_na_release_lock(VALUE); // currently do nothing
54
54
  // used in reduce methods
55
55
  VALUE cumo_na_reduce_dimension(int argc, VALUE *argv, int naryc, VALUE *naryv,
56
56
  cumo_ndfunc_t *ndf, cumo_na_iter_func_t nan_iter);
57
+ VALUE cumo_na_get_reduce(VALUE v);
58
+ void cumo_na_set_reduce(VALUE v, VALUE reduce);
57
59
 
58
60
  VALUE cumo_na_reduce_options(VALUE axes, VALUE *opts, int naryc, VALUE *naryv,
59
61
  cumo_ndfunc_t *ndf);
@@ -66,6 +68,15 @@ VALUE cumo_na_ndloop4(cumo_ndfunc_t *nf, void *ptr, VALUE args);
66
68
 
67
69
  VALUE cumo_na_ndloop_cast_narray_to_rarray(cumo_ndfunc_t *nf, VALUE nary, VALUE fmt);
68
70
  VALUE cumo_na_ndloop_store_rarray(cumo_ndfunc_t *nf, VALUE nary, VALUE rary);
71
+
72
+ // One device buffer for a whole walk, not one per row. A row that gives its
73
+ // own back has to wait for the kernel still reading it, and that wait costs
74
+ // more than everything else the row does. cumo_na_ndloop_store_rarray owns
75
+ // one and hands it over as the loop's opt_ptr.
76
+ // Opaque: an iterator asks for a buffer and reads nothing else.
77
+ typedef struct cumo_na_stage_pool cumo_na_stage_pool_t;
78
+
79
+ char *cumo_na_stage_pool_get(cumo_na_stage_pool_t *pool, size_t bytes);
69
80
  VALUE cumo_na_ndloop_store_rarray2(cumo_ndfunc_t *nf, VALUE nary, VALUE rary, VALUE opt);
70
81
  VALUE cumo_na_ndloop_inspect(VALUE nary, cumo_na_text_func_t func, VALUE opt);
71
82
  VALUE cumo_na_ndloop_with_index(cumo_ndfunc_t *nf, int argc, ...);
@@ -190,6 +190,7 @@ extern VALUE cumo_cBit;
190
190
  extern VALUE cumo_cDFloat;
191
191
  extern VALUE cumo_cSFloat;
192
192
  extern VALUE cumo_cHFloat;
193
+ extern VALUE cumo_cBFloat;
193
194
  extern VALUE cumo_cDComplex;
194
195
  extern VALUE cumo_cSComplex;
195
196
  extern VALUE cumo_cInt64;