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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +590 -152
- data/CHANGELOG.md +63 -0
- data/README.md +245 -0
- data/bench/cumo_probe.rb +1 -1
- data/cumo.gemspec +7 -1
- data/ext/cumo/cuda/cudnn.c +18 -0
- data/ext/cumo/cuda/cudnn_impl.cpp +60 -14
- data/ext/cumo/cuda/driver.c +11 -9
- data/ext/cumo/cuda/memory_pool_impl.cpp +32 -22
- data/ext/cumo/cuda/memory_pool_impl.hpp +3 -1
- data/ext/cumo/cuda/memory_pool_impl_test.cpp +19 -25
- data/ext/cumo/cuda/runtime.c +56 -0
- data/ext/cumo/cumo.c +4 -0
- data/ext/cumo/extconf.rb +2 -0
- data/ext/cumo/include/cumo/bit_reduce_kernel.h +6 -6
- data/ext/cumo/include/cumo/check.h +11 -3
- data/ext/cumo/include/cumo/cuda/cudnn.h +43 -6
- data/ext/cumo/include/cumo/cuda/runtime.h +2 -0
- data/ext/cumo/include/cumo/indexer.h +16 -0
- data/ext/cumo/include/cumo/intern.h +11 -0
- data/ext/cumo/include/cumo/narray.h +1 -0
- data/ext/cumo/include/cumo/reduce_kernel.h +283 -54
- data/ext/cumo/include/cumo/row_kernel.h +202 -0
- data/ext/cumo/include/cumo/row_method.h +149 -0
- data/ext/cumo/include/cumo/template_kernel.h +26 -0
- data/ext/cumo/include/cumo/types/bf16_def.h +82 -0
- data/ext/cumo/include/cumo/types/bf16_def_kernel.h +28 -0
- data/ext/cumo/include/cumo/types/bf16_macro.h +14 -0
- data/ext/cumo/include/cumo/types/bf16_macro_kernel.h +19 -0
- data/ext/cumo/include/cumo/types/bfloat.h +22 -0
- data/ext/cumo/include/cumo/types/bfloat_kernel.h +16 -0
- data/ext/cumo/include/cumo/types/bit.h +2 -0
- data/ext/cumo/include/cumo/types/bit_kernel.h +2 -0
- data/ext/cumo/include/cumo/types/complex_macro.h +2 -0
- data/ext/cumo/include/cumo/types/complex_macro_kernel.h +2 -0
- data/ext/cumo/include/cumo/types/f16_macro.h +191 -0
- data/ext/cumo/include/cumo/types/f16_macro_kernel.h +214 -0
- data/ext/cumo/include/cumo/types/float_def.h +8 -0
- data/ext/cumo/include/cumo/types/float_def_kernel.h +8 -0
- data/ext/cumo/include/cumo/types/float_macro.h +26 -17
- data/ext/cumo/include/cumo/types/float_macro_kernel.h +26 -19
- data/ext/cumo/include/cumo/types/half_macro.h +6 -180
- data/ext/cumo/include/cumo/types/half_macro_kernel.h +6 -185
- data/ext/cumo/include/cumo/types/robj_macro.h +2 -0
- data/ext/cumo/include/cumo/types/xint_macro.h +2 -6
- data/ext/cumo/include/cumo/types/xint_macro_kernel.h +2 -0
- data/ext/cumo/include/cumo.h +2 -2
- data/ext/cumo/narray/data.c +15 -6
- data/ext/cumo/narray/data_kernel.cu +110 -0
- data/ext/cumo/narray/gen/def/bfloat.rb +57 -0
- data/ext/cumo/narray/gen/def/bit.rb +12 -1
- data/ext/cumo/narray/gen/def/dcomplex.rb +12 -1
- data/ext/cumo/narray/gen/def/dfloat.rb +11 -1
- data/ext/cumo/narray/gen/def/hfloat.rb +11 -1
- data/ext/cumo/narray/gen/def/int16.rb +12 -1
- data/ext/cumo/narray/gen/def/int32.rb +12 -1
- data/ext/cumo/narray/gen/def/int64.rb +12 -1
- data/ext/cumo/narray/gen/def/int8.rb +12 -1
- data/ext/cumo/narray/gen/def/robject.rb +12 -1
- data/ext/cumo/narray/gen/def/scomplex.rb +12 -1
- data/ext/cumo/narray/gen/def/sfloat.rb +11 -1
- data/ext/cumo/narray/gen/def/uint16.rb +12 -1
- data/ext/cumo/narray/gen/def/uint32.rb +12 -1
- data/ext/cumo/narray/gen/def/uint64.rb +12 -1
- data/ext/cumo/narray/gen/def/uint8.rb +12 -1
- data/ext/cumo/narray/gen/narray_def.rb +35 -6
- data/ext/cumo/narray/gen/spec.rb +14 -14
- data/ext/cumo/narray/gen/tmpl/accum_arg.c +1 -1
- data/ext/cumo/narray/gen/tmpl/accum_binary.c +63 -5
- data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +29 -13
- data/ext/cumo/narray/gen/tmpl/accum_index.c +1 -1
- data/ext/cumo/narray/gen/tmpl/batch_norm.c +4 -4
- data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +5 -5
- data/ext/cumo/narray/gen/tmpl/binary.c +3 -9
- data/ext/cumo/narray/gen/tmpl/complex_accum_kernel.cu +15 -5
- data/ext/cumo/narray/gen/tmpl/conv.c +90 -123
- data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +91 -83
- data/ext/cumo/narray/gen/tmpl/conv_transpose.c +90 -123
- data/ext/cumo/narray/gen/tmpl/cum.c +8 -7
- data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +7 -6
- data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +4 -4
- data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +22 -10
- data/ext/cumo/narray/gen/tmpl/gemm.c +11 -41
- data/ext/cumo/narray/gen/tmpl/layer_norm.c +38 -0
- data/ext/cumo/narray/gen/tmpl/layer_norm_kernel.cu +54 -0
- data/ext/cumo/narray/gen/tmpl/lib.c +1 -0
- data/ext/cumo/narray/gen/tmpl/lib_kernel.cu +1 -0
- data/ext/cumo/narray/gen/tmpl/median.c +1 -1
- data/ext/cumo/narray/gen/tmpl/poly.c +0 -3
- data/ext/cumo/narray/gen/tmpl/poly_kernel.cu +1 -1
- data/ext/cumo/narray/gen/tmpl/pooling_backward.c +3 -2
- data/ext/cumo/narray/gen/tmpl/pooling_forward.c +3 -2
- data/ext/cumo/narray/gen/tmpl/quantize_symmetric.c +74 -0
- data/ext/cumo/narray/gen/tmpl/quantize_symmetric_kernel.cu +67 -0
- data/ext/cumo/narray/gen/tmpl/rand.c +9 -9
- data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +9 -9
- data/ext/cumo/narray/gen/tmpl/rand_norm.c +4 -4
- data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +7 -7
- data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +6 -6
- data/ext/cumo/narray/gen/tmpl/rms_norm.c +38 -0
- data/ext/cumo/narray/gen/tmpl/rms_norm_kernel.cu +57 -0
- data/ext/cumo/narray/gen/tmpl/softmax.c +21 -0
- data/ext/cumo/narray/gen/tmpl/softmax_kernel.cu +157 -0
- data/ext/cumo/narray/gen/tmpl/sort.c +9 -32
- data/ext/cumo/narray/gen/tmpl/sort_index.c +16 -95
- data/ext/cumo/narray/gen/tmpl/store_array.c +12 -9
- data/ext/cumo/narray/gen/tmpl/store_from.c +1 -16
- data/ext/cumo/narray/gen/tmpl_bit/mask.c +11 -11
- data/ext/cumo/narray/gen/tmpl_bit/store_array.c +5 -9
- data/ext/cumo/narray/gen/tmpl_bit/where.c +50 -11
- data/ext/cumo/narray/gen/tmpl_bit/where2.c +11 -12
- data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +1 -2
- data/ext/cumo/narray/index.c +35 -25
- data/ext/cumo/narray/index_kernel.cu +27 -0
- data/ext/cumo/narray/math.c +40 -8
- data/ext/cumo/narray/narray.c +85 -15
- data/ext/cumo/narray/ndloop.c +210 -5
- data/ext/cumo/narray/sort_kernel.cu +61 -41
- data/ext/cumo/narray/struct.c +3 -0
- data/lib/cumo/cuda/cudnn.rb +1 -1
- data/lib/cumo/narray/extra.rb +2 -2
- data/test/bfloat_test.rb +238 -0
- data/test/bit_test.rb +52 -14
- data/test/cuda/memory_pool_test.rb +8 -7
- data/test/cuda/runtime_test.rb +25 -0
- data/test/cudnn_test.rb +108 -24
- data/test/extra_test.rb +1 -1
- data/test/fused_test.rb +587 -0
- data/test/math_test.rb +182 -2
- data/test/narray_alt_coverage_test.rb +1 -0
- data/test/narray_test.rb +344 -22
- data/test/test_helper.rb +1 -0
- metadata +32 -5
- 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
|
-
|
|
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
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
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
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
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() ==
|
|
377
|
+
assert(arena.size() == 2);
|
|
378
378
|
assert(arena[0].size() == 1);
|
|
379
|
-
assert(arena[1].size() ==
|
|
380
|
-
assert(
|
|
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] ==
|
|
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() ==
|
|
385
|
+
assert(arena.size() == 1);
|
|
388
386
|
assert(arena[0].size() == 1);
|
|
389
|
-
assert(
|
|
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() ==
|
|
398
|
-
assert(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
assert(
|
|
403
|
-
assert(
|
|
404
|
-
assert(
|
|
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() {
|
data/ext/cumo/cuda/runtime.c
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -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, ...);
|