cumo 0.5.11 → 0.6.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/CHANGELOG.md +33 -0
- data/bench/cumo_shape_probe.rb +264 -0
- data/ext/cumo/cuda/cudnn_impl.cpp +9 -1
- data/ext/cumo/cuda/memory_pool_impl_test.cpp +4 -1
- data/ext/cumo/cuda/nvrtc.c +1 -1
- data/ext/cumo/depend.erb +7 -0
- data/ext/cumo/include/cumo/bit_reduce_kernel.h +119 -13
- data/ext/cumo/include/cumo/check.h +51 -0
- data/ext/cumo/include/cumo/cuda/cudnn.h +27 -42
- data/ext/cumo/include/cumo/indexer.h +29 -8
- data/ext/cumo/include/cumo/narray.h +10 -0
- data/ext/cumo/include/cumo/ndloop.h +1 -0
- data/ext/cumo/include/cumo/reduce_kernel.h +133 -50
- data/ext/cumo/include/cumo/types/float_macro.h +15 -2
- data/ext/cumo/include/cumo/types/float_macro_kernel.h +15 -2
- data/ext/cumo/include/cumo/types/int_macro.h +20 -0
- data/ext/cumo/include/cumo/types/int_macro_kernel.h +20 -0
- data/ext/cumo/include/cumo/types/robj_macro.h +8 -3
- data/ext/cumo/include/cumo.h +2 -2
- data/ext/cumo/narray/data.c +21 -25
- data/ext/cumo/narray/data_kernel.cu +113 -0
- data/ext/cumo/narray/gen/narray_def.rb +5 -0
- data/ext/cumo/narray/gen/spec.rb +10 -0
- data/ext/cumo/narray/gen/tmpl/batch_norm.c +25 -22
- data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +20 -17
- data/ext/cumo/narray/gen/tmpl/binary.c +3 -3
- data/ext/cumo/narray/gen/tmpl/binary2.c +1 -1
- data/ext/cumo/narray/gen/tmpl/binary_s.c +3 -3
- data/ext/cumo/narray/gen/tmpl/clip.c +6 -6
- data/ext/cumo/narray/gen/tmpl/cond_binary.c +3 -3
- data/ext/cumo/narray/gen/tmpl/cond_unary.c +1 -1
- data/ext/cumo/narray/gen/tmpl/conv.c +8 -8
- data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +10 -10
- data/ext/cumo/narray/gen/tmpl/conv_transpose.c +10 -10
- data/ext/cumo/narray/gen/tmpl/ewcomp.c +1 -1
- data/ext/cumo/narray/gen/tmpl/fill.c +1 -1
- data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +16 -13
- data/ext/cumo/narray/gen/tmpl/frexp.c +1 -1
- data/ext/cumo/narray/gen/tmpl/poly.c +1 -1
- data/ext/cumo/narray/gen/tmpl/pooling_backward.c +4 -4
- data/ext/cumo/narray/gen/tmpl/pooling_forward.c +4 -4
- data/ext/cumo/narray/gen/tmpl/pow.c +6 -6
- data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +73 -0
- data/ext/cumo/narray/gen/tmpl/set2.c +1 -1
- data/ext/cumo/narray/gen/tmpl/store_bit.c +1 -1
- data/ext/cumo/narray/gen/tmpl/store_from.c +1 -1
- data/ext/cumo/narray/gen/tmpl/unary.c +1 -1
- data/ext/cumo/narray/gen/tmpl/unary2.c +1 -1
- data/ext/cumo/narray/gen/tmpl/unary_ret2.c +1 -1
- data/ext/cumo/narray/gen/tmpl/unary_s.c +1 -1
- data/ext/cumo/narray/gen/tmpl_bit/binary.c +1 -1
- data/ext/cumo/narray/gen/tmpl_bit/bit_reduce.c +4 -1
- data/ext/cumo/narray/gen/tmpl_bit/bit_stat.c +36 -0
- data/ext/cumo/narray/gen/tmpl_bit/bit_stat_kernel.cu +4 -0
- data/ext/cumo/narray/gen/tmpl_bit/extract.c +13 -27
- data/ext/cumo/narray/gen/tmpl_bit/fill.c +1 -1
- data/ext/cumo/narray/gen/tmpl_bit/store_bit.c +1 -1
- data/ext/cumo/narray/gen/tmpl_bit/store_from.c +1 -1
- data/ext/cumo/narray/gen/tmpl_bit/unary.c +1 -1
- data/ext/cumo/narray/index.c +20 -8
- data/ext/cumo/narray/narray.c +155 -18
- data/ext/cumo/narray/ndloop.c +67 -0
- data/lib/cumo/narray/extra.rb +119 -24
- data/test/bit_test.rb +143 -0
- data/test/cudnn_test.rb +88 -0
- data/test/cumo_test.rb +1 -0
- data/test/extra_test.rb +74 -6
- data/test/narray_test.rb +851 -3
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d6b3e0decf69d07199a7930dc6a23d1a05a90ed044cf007203c78c28a8e9ea1
|
|
4
|
+
data.tar.gz: dec0ca1f195b302919d30085fcc139911299ba075193b67cab9965907210092f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4f8d6204fca59288efad1ab5dec228d474de02ee3465f89afe7fc7440b456b0d46b78c92fb1e48e92063a2b3c385dedb8955aed3ddfa4411c13beb2f32a5417
|
|
7
|
+
data.tar.gz: a397b427534b829b13f8bd4a4d6f8616d918bde834dc5c69a5611671ec942796d8615bd74d2564a2c3cd6cdaf9e572f4599e0704071f21a3c40394d0236b148d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
# 0.6.0 (2026/09/06)
|
|
2
|
+
|
|
3
|
+
Breaking changes:
|
|
4
|
+
|
|
5
|
+
* `divmod` and `%` floor the quotient and give the remainder the divisor's sign, as Ruby does, on every signed type: `Cumo::Int32[-7].divmod(3)` answers `[-3, 2]` where it answered `[-2, -1]`, and `-7 % 3` answers `2` rather than `-1`. Dividing a float by zero answers a quotient of `NaN` where it answered `Infinity`. Unsigned types are unchanged (PR #364)
|
|
6
|
+
* A view whose base has since been given a smaller shape raises `RuntimeError` instead of reading past what the base holds (PR #363)
|
|
7
|
+
* `marshal_load` and `initialize` leave an array unallocated when they take a new shape, so it reads as unallocated until something is stored in it, and both refuse a view. `initialize` also refuses a frozen array, as `marshal_load` already did (PR #361, PR #360, PR #355)
|
|
8
|
+
* A range subscript longer than 2**31 answers the length it names instead of an empty view, and one longer than 2**32 no longer answers with the low 32 bits of it (PR #354)
|
|
9
|
+
* `cov` rejects a 3-dimensional receiver or `y`, 2-dimensional or non-integer `fweights`, and any `ddof` but 0 or 1, where it answered from them; it warns and answers `nan` when the degrees of freedom come out at zero or below (PR #343)
|
|
10
|
+
* `Cumo::Bit#extract` answers with a zero-dimensional `Cumo::Bit`, as every other dtype does, rather than a Ruby Integer read back through a synchronize (PR #341)
|
|
11
|
+
* `batch_norm` and its backward reject an axis that names nothing in `x`, repeats a dimension, or is out of order, where they answered as though a different axis had been given (PR #336)
|
|
12
|
+
|
|
13
|
+
Fixes:
|
|
14
|
+
|
|
15
|
+
* Fix `RObject#divmod` reading the pair an element's `divmod` answered without looking at it: a bare Integer or a String took the process down, and a shorter Array handed back the words past its end as the quotient and the remainder (PR #359)
|
|
16
|
+
* Fix an out-of-bounds read when `initialize` is given a new shape through `send`, which left the buffer sized for the old one: 4096 elements were read out of 8, and the same on an `Cumo::RObject` crashed at the next collection (PR #361)
|
|
17
|
+
* Fix a memory leak when an Array subscript is rejected, the host buffer it is staged in being freed only after the loop that fills it, and the memory a subscript piles up before the collector takes it back (PR #357, PR #358)
|
|
18
|
+
* Fix `marshal_load` writing past a buffer sized for the shape it replaces, and the double free that came of releasing a buffer noted down before the shape's own `to_int` had run (PR #355, PR #356)
|
|
19
|
+
* Fix reading past a Ruby array or string that a conversion shrank underneath the walk: a subscript, a shape, an axis list, a marshal array and the string behind `from_binary` were each measured once and walked afterwards, and `to_int` is free to empty any of them (PR #353)
|
|
20
|
+
* Fix `Cumo::Bit#swap_byte` walking one byte per bit, eight times past the end of the packed buffer; `hton`, `to_network` and `to_swapped` reach the same loop (PR #352)
|
|
21
|
+
|
|
22
|
+
Changes:
|
|
23
|
+
|
|
24
|
+
* Add `argsort`, which answers positions along an axis rather than into the flattened array as `sort_index` does (PR #342)
|
|
25
|
+
* Add `mean`, `var`, `stddev` and `rms` to the integer types and to `Cumo::Bit`, which had only the float ones (PR #344, PR #345)
|
|
26
|
+
* Walk an elementwise function along the memory of the operand it writes rather than the shape it was given; on a transposed 1024x1024 SFloat view `a.inplace + 1.0` goes from 1180.6us to 78.3us (PR #351)
|
|
27
|
+
* Reduce along the contiguous axis with a contiguous group of threads, keep a reduction's operands out of local memory, and give a short strided row more elements per thread; `[1024, 4, 1024].sum(axis: 1)` goes from 142.0us to 23.4us and the same `mulsum` from 675.3us to 30.9us (PR #350, PR #347)
|
|
28
|
+
* Split a flat index in 32 bits where the array fits in them, the 64-bit division being a software routine on the GPU; a 4M SFloat `a[true, 1..-2] * 2.0` goes from 60.6us to 38.7us (PR #348)
|
|
29
|
+
* Reverse the bytes of an array in a kernel instead of on the host; a 4M SFloat `swap_byte` goes from 5617.0us to 13.0us (PR #335)
|
|
30
|
+
* Add `bench/cumo_shape_probe.rb`, which reads each shape of an operation against the same bytes moved along the best path it has (PR #349)
|
|
31
|
+
* Make every object depend on the headers it is built from, a header-only change having left stale objects behind (PR #346)
|
|
32
|
+
* Answer the four cuDNN TODOs left in the tree, and say what the memory pool's `TestRemoveFromFreeList` holds rather than what it waits for (PR #340, PR #339, PR #338, PR #337)
|
|
33
|
+
|
|
1
34
|
# 0.5.11 (2026/08/29)
|
|
2
35
|
|
|
3
36
|
Breaking changes:
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# ---------------------------------------------------------------------------
|
|
5
|
+
# Cumo shape sweep
|
|
6
|
+
#
|
|
7
|
+
# cumo_probe.rb runs every kernel family at one shape and reads each member
|
|
8
|
+
# against the fastest of its group. What that cannot see is a family whose
|
|
9
|
+
# speed depends on the shape it is handed: a reduction along a short row, an
|
|
10
|
+
# operand that is broadcast or strided, a copy that transposes. This sweep
|
|
11
|
+
# holds the bytes fixed, varies the shape and the layout, and reads every case
|
|
12
|
+
# against the same bytes moved down the best path there is for them: the flat
|
|
13
|
+
# reduction, the add of two contiguous arrays, the contiguous copy.
|
|
14
|
+
#
|
|
15
|
+
# GPU=1 ruby cumo_shape_probe.rb # the default sweep
|
|
16
|
+
# GPU=1 GROUP=reduce ruby cumo_shape_probe.rb # reduce, elementwise or copy
|
|
17
|
+
# GPU=1 ALL=1 ruby cumo_shape_probe.rb # every row, not just the flagged
|
|
18
|
+
# GPU=1 FLAG=3 ruby cumo_shape_probe.rb # how far off the reference is called out
|
|
19
|
+
# GPU=1 DTYPE=DFloat ELEMENTS=67108864 ruby cumo_shape_probe.rb
|
|
20
|
+
# GPU=1 JSON=before.json ruby cumo_shape_probe.rb # keep this run's rows ...
|
|
21
|
+
# GPU=1 COMPARE=before.json ruby cumo_shape_probe.rb # ... and read a later build against them
|
|
22
|
+
# ruby cumo_shape_probe.rb # the same sweep on Numo
|
|
23
|
+
#
|
|
24
|
+
# Every case queues LAUNCHES calls and synchronizes once, and the run opens
|
|
25
|
+
# with a few seconds of load. On a laptop the memory clock climbs in steps,
|
|
26
|
+
# and a best-of-N that synchronizes between calls can settle on a step below
|
|
27
|
+
# the top and read a third low without anything being wrong.
|
|
28
|
+
#
|
|
29
|
+
# The sweep runs in one process. The cases work in place or reduce, so the
|
|
30
|
+
# only results allocated are the reductions', a thousandth of their input.
|
|
31
|
+
#
|
|
32
|
+
# The default 4M elements of SFloat is 16 MB, inside the L2 of this class of
|
|
33
|
+
# GPU, which is where the activations of a model live. ELEMENTS=67108864 moves
|
|
34
|
+
# the sweep to DRAM, where a cost paid per element hides behind the bandwidth.
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
require 'json'
|
|
38
|
+
|
|
39
|
+
GPU = !%w[0 false].include?(ENV['GPU'].to_s.downcase) && !ENV['GPU'].to_s.empty?
|
|
40
|
+
|
|
41
|
+
if GPU
|
|
42
|
+
require 'cumo/narray'
|
|
43
|
+
XM = Cumo
|
|
44
|
+
else
|
|
45
|
+
require 'numo/narray'
|
|
46
|
+
XM = Numo
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
GROUP = ENV['GROUP']
|
|
50
|
+
FLAG = (ENV['FLAG'] || 2.0).to_f
|
|
51
|
+
SHOW_ALL = !ENV['ALL'].to_s.empty?
|
|
52
|
+
LAUNCHES = (ENV['LAUNCHES'] || 10).to_i
|
|
53
|
+
ROUNDS = (ENV['ROUNDS'] || 4).to_i
|
|
54
|
+
ELEMENTS = (ENV['ELEMENTS'] || (1 << 22)).to_i
|
|
55
|
+
DTYPE = XM.const_get(ENV['DTYPE'] || 'SFloat')
|
|
56
|
+
JSON_OUT = ENV['JSON']
|
|
57
|
+
COMPARE = ENV['COMPARE']
|
|
58
|
+
|
|
59
|
+
def sync
|
|
60
|
+
XM::CUDA::Runtime.cudaDeviceSynchronize if GPU
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def now
|
|
64
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def elmsz
|
|
68
|
+
DTYPE::ELEMENT_BYTE_SIZE
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def warm(seconds)
|
|
72
|
+
a = DTYPE.new(1 << 22).fill(0)
|
|
73
|
+
t = now
|
|
74
|
+
a.inplace + 1 while now - t < seconds
|
|
75
|
+
sync
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Seconds for one call, best of ROUNDS batches of LAUNCHES calls.
|
|
79
|
+
def time_one
|
|
80
|
+
2.times { yield }
|
|
81
|
+
sync
|
|
82
|
+
best = Float::INFINITY
|
|
83
|
+
ROUNDS.times do
|
|
84
|
+
sync
|
|
85
|
+
t = now
|
|
86
|
+
LAUNCHES.times { yield }
|
|
87
|
+
sync
|
|
88
|
+
best = [best, (now - t) / LAUNCHES].min
|
|
89
|
+
end
|
|
90
|
+
best
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
Row = Struct.new(:group, :name, :shape, :us, :gbs, :ref, keyword_init: true) do
|
|
94
|
+
def key
|
|
95
|
+
"#{group}/#{name}"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
ROWS = []
|
|
100
|
+
|
|
101
|
+
# bytes is what the case moves, and the reference row is the one this case is
|
|
102
|
+
# read against.
|
|
103
|
+
def probe(group, name, shape, bytes, ref: nil, &blk)
|
|
104
|
+
return if GROUP && !group.start_with?(GROUP)
|
|
105
|
+
|
|
106
|
+
s = time_one(&blk)
|
|
107
|
+
ROWS << Row.new(group: group, name: name, shape: shape.inspect, us: s * 1e6, gbs: bytes / s / 1e9, ref: ref || name)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def rand_array(*shape)
|
|
111
|
+
a = DTYPE.new(*shape)
|
|
112
|
+
DTYPE.name.include?('Int') ? a.rand(100) : a.rand
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# --- reductions ---------------------------------------------------------------
|
|
116
|
+
#
|
|
117
|
+
# The same ELEMENTS reduced along the last axis, the first axis and a middle
|
|
118
|
+
# axis, over rows of every length, all read against the flat reduction of the
|
|
119
|
+
# same array. Each output of every case is a reduction over len elements.
|
|
120
|
+
def sweep_reduce
|
|
121
|
+
n = ELEMENTS
|
|
122
|
+
es = elmsz
|
|
123
|
+
a = rand_array(n)
|
|
124
|
+
b = rand_array(n)
|
|
125
|
+
methods = %i[sum max mean argmax mulsum].select { |m| a.respond_to?(m) }
|
|
126
|
+
methods.each do |m|
|
|
127
|
+
bytes = (m == :mulsum ? 2 : 1) * n * es
|
|
128
|
+
probe('reduce', "#{m} flat", [n], bytes) { m == :mulsum ? a.mulsum(b) : a.send(m) }
|
|
129
|
+
end
|
|
130
|
+
[4, 16, 64, 256, 1024, 4096].each do |len|
|
|
131
|
+
next if len > n
|
|
132
|
+
|
|
133
|
+
rows = n / len
|
|
134
|
+
r1 = 1 << (Math.log2(rows).to_i / 2)
|
|
135
|
+
r2 = rows / r1
|
|
136
|
+
last = a.reshape(rows, len)
|
|
137
|
+
last_b = b.reshape(rows, len)
|
|
138
|
+
first = a.reshape(len, rows)
|
|
139
|
+
first_b = b.reshape(len, rows)
|
|
140
|
+
mid = a.reshape(r1, len, r2)
|
|
141
|
+
mid_b = b.reshape(r1, len, r2)
|
|
142
|
+
methods.each do |m|
|
|
143
|
+
bytes = (m == :mulsum ? 2 : 1) * n * es
|
|
144
|
+
ref = "#{m} flat"
|
|
145
|
+
call = lambda do |x, y, axis|
|
|
146
|
+
m == :mulsum ? x.mulsum(y, axis: axis) : x.send(m, axis: axis)
|
|
147
|
+
end
|
|
148
|
+
probe('reduce', "#{m} len #{len} last", [rows, len], bytes, ref: ref) { call.call(last, last_b, 1) }
|
|
149
|
+
probe('reduce', "#{m} len #{len} first", [len, rows], bytes, ref: ref) { call.call(first, first_b, 0) }
|
|
150
|
+
probe('reduce', "#{m} len #{len} middle", [r1, len, r2], bytes, ref: ref) { call.call(mid, mid_b, 1) }
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# --- elementwise --------------------------------------------------------------
|
|
156
|
+
#
|
|
157
|
+
# One [R, C] array added to in place, its operand contiguous, a scalar, a
|
|
158
|
+
# broadcast row or column, a slice, a stepped view or a transpose, plus the
|
|
159
|
+
# same broadcast through three and four dimensions. The reference is the add
|
|
160
|
+
# of two contiguous arrays, which the loop walks as one run.
|
|
161
|
+
def sweep_elementwise
|
|
162
|
+
n = ELEMENTS
|
|
163
|
+
es = elmsz
|
|
164
|
+
r = 1024
|
|
165
|
+
c = n / r
|
|
166
|
+
a = rand_array(r, c)
|
|
167
|
+
b = rand_array(r, c)
|
|
168
|
+
row = rand_array(c)
|
|
169
|
+
col = rand_array(r, 1)
|
|
170
|
+
probe('elementwise', 'a + b contiguous', [r, c], 3 * n * es) { a.inplace + b }
|
|
171
|
+
probe('elementwise', 'a * scalar', [r, c], 2 * n * es, ref: 'a + b contiguous') { a.inplace * 2 }
|
|
172
|
+
probe('elementwise', 'a + row broadcast', [r, c], 2 * n * es, ref: 'a + b contiguous') { a.inplace + row }
|
|
173
|
+
probe('elementwise', 'a - column broadcast', [r, c], 2 * n * es, ref: 'a + b contiguous') { a.inplace - col }
|
|
174
|
+
a3 = a.reshape(16, r / 16, c)
|
|
175
|
+
b3 = rand_array(1, r / 16, 1)
|
|
176
|
+
probe('elementwise', 'a + broadcast 3d', [16, r / 16, c], 2 * n * es, ref: 'a + b contiguous') { a3.inplace + b3 }
|
|
177
|
+
a4 = a.reshape(4, 4, r / 16, c)
|
|
178
|
+
b4 = rand_array(1, 4, 1, c)
|
|
179
|
+
probe('elementwise', 'a + broadcast 4d', [4, 4, r / 16, c], 2 * n * es, ref: 'a + b contiguous') { a4.inplace + b4 }
|
|
180
|
+
half = 0...(c / 2)
|
|
181
|
+
probe('elementwise', 'a + b column slice', [r, c / 2], 3 * n * es / 2, ref: 'a + b contiguous') { a[true, half].inplace + b[true, half] }
|
|
182
|
+
every_other = (0..-1) % 2
|
|
183
|
+
probe('elementwise', 'a * scalar stepped rows', [r / 2, c], n * es, ref: 'a + b contiguous') { a[every_other, true].inplace * 2 }
|
|
184
|
+
probe('elementwise', 'a + b transposed views', [c, r], 3 * n * es, ref: 'a + b contiguous') { a.transpose.inplace + b.transpose }
|
|
185
|
+
probe('elementwise', 'a.gt(b) to Bit', [r, c], 2 * n * es, ref: 'a + b contiguous') { a.gt(b) }
|
|
186
|
+
probe('elementwise', 'a.gt(row) to Bit', [r, c], n * es, ref: 'a + b contiguous') { a.gt(row) }
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# --- copies -------------------------------------------------------------------
|
|
190
|
+
#
|
|
191
|
+
# A store from a view of every kind into contiguous memory, read against the
|
|
192
|
+
# contiguous copy of the same bytes.
|
|
193
|
+
def sweep_copy
|
|
194
|
+
n = ELEMENTS
|
|
195
|
+
es = elmsz
|
|
196
|
+
r = 1024
|
|
197
|
+
c = n / r
|
|
198
|
+
a = rand_array(r, c)
|
|
199
|
+
dst = DTYPE.new(r, c).fill(0)
|
|
200
|
+
dst_t = DTYPE.new(c, r).fill(0)
|
|
201
|
+
probe('copy', 'contiguous', [r, c], 2 * n * es) { dst.store(a) }
|
|
202
|
+
half = 0...(c / 2)
|
|
203
|
+
probe('copy', 'column slice', [r, c / 2], n * es, ref: 'contiguous') { dst[true, half].store(a[true, half]) }
|
|
204
|
+
every_other = (0..-1) % 2
|
|
205
|
+
probe('copy', 'stepped rows', [r / 2, c], n * es, ref: 'contiguous') { dst[every_other, true].store(a[every_other, true]) }
|
|
206
|
+
probe('copy', 'reversed rows', [r, c], 2 * n * es, ref: 'contiguous') { dst.store(a.reverse(0)) }
|
|
207
|
+
probe('copy', 'transposed', [c, r], 2 * n * es, ref: 'contiguous') { dst_t.store(a.transpose) }
|
|
208
|
+
idx = XM::Int32.new(r / 2).seq * 2
|
|
209
|
+
probe('copy', 'index-backed rows', [r / 2, c], n * es, ref: 'contiguous') { dst[0...(r / 2), true].store(a[idx, true]) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# --- run ----------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
version = GPU ? Cumo::NArray::VERSION : Numo::NArray::VERSION
|
|
215
|
+
puts "backend : #{XM} #{version}"
|
|
216
|
+
puts "dtype : #{DTYPE}, #{ELEMENTS} elements (#{(ELEMENTS * elmsz / 1e6).round(1)} MB), #{LAUNCHES} launches per sync, best of #{ROUNDS}"
|
|
217
|
+
warm(3)
|
|
218
|
+
sweep_reduce
|
|
219
|
+
sweep_elementwise
|
|
220
|
+
sweep_copy
|
|
221
|
+
|
|
222
|
+
refs = ROWS.to_h { |row| [row.key, row] }
|
|
223
|
+
flagged = []
|
|
224
|
+
puts
|
|
225
|
+
puts format(' %-11s %-26s %-18s %9s %8s %8s', 'group', 'case', 'shape', 'us', 'GB/s', 'vs ref')
|
|
226
|
+
ROWS.each do |row|
|
|
227
|
+
ref = refs["#{row.group}/#{row.ref}"]
|
|
228
|
+
ratio = ref ? ref.gbs / row.gbs : 1.0
|
|
229
|
+
slow = ratio > FLAG
|
|
230
|
+
flagged << row if slow
|
|
231
|
+
next unless slow || SHOW_ALL || row.ref == row.name
|
|
232
|
+
|
|
233
|
+
puts format(' %-11s %-26s %-18s %9.1f %8.1f %7.1fx%s', row.group, row.name, row.shape, row.us, row.gbs, ratio, slow ? ' <--' : '')
|
|
234
|
+
end
|
|
235
|
+
puts
|
|
236
|
+
if flagged.empty?
|
|
237
|
+
puts format(' nothing was more than %.1fx off its reference', FLAG)
|
|
238
|
+
else
|
|
239
|
+
puts format(' %d of %d cases more than %.1fx off their reference', flagged.size, ROWS.size, FLAG)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
if COMPARE
|
|
243
|
+
before = JSON.parse(File.read(COMPARE))
|
|
244
|
+
puts
|
|
245
|
+
puts " against #{File.basename(COMPARE)} (#{before['meta']['backend']} #{before['meta']['version']}, saved #{before['meta']['saved']})"
|
|
246
|
+
puts format(' %-11s %-26s %9s %9s %8s', 'group', 'case', 'before us', 'after us', 'speedup')
|
|
247
|
+
ROWS.each do |row|
|
|
248
|
+
was = before['rows'][row.key]
|
|
249
|
+
next unless was
|
|
250
|
+
|
|
251
|
+
speedup = was['us'] / row.us
|
|
252
|
+
next unless SHOW_ALL || speedup > 1.25 || speedup < 0.8
|
|
253
|
+
|
|
254
|
+
puts format(' %-11s %-26s %9.1f %9.1f %7.2fx', row.group, row.name, was['us'], row.us, speedup)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
if JSON_OUT
|
|
259
|
+
out = { 'meta' => { 'backend' => XM.to_s, 'version' => version, 'dtype' => DTYPE.to_s, 'elements' => ELEMENTS,
|
|
260
|
+
'saved' => Time.now.strftime('%Y-%m-%d %H:%M') },
|
|
261
|
+
'rows' => ROWS.to_h { |row| [row.key, { 'shape' => row.shape, 'us' => row.us, 'gbs' => row.gbs }] } }
|
|
262
|
+
File.write(JSON_OUT, JSON.pretty_generate(out))
|
|
263
|
+
puts " wrote #{ROWS.size} rows to #{JSON_OUT}"
|
|
264
|
+
end
|
|
@@ -552,7 +552,15 @@ cumo_cuda_cudnn_GetBatchNormMode(size_t ndim, int* axis) {
|
|
|
552
552
|
}
|
|
553
553
|
if ((ndim == 3 && axis[0] == 0 && axis[1] == 2 && axis[2] == 3) ||
|
|
554
554
|
(ndim == 4 && axis[0] == 0 && axis[1] == 2 && axis[2] == 3 && axis[3] == 4)) { // (1, channels, (1, )1, 1)
|
|
555
|
-
//
|
|
555
|
+
// CUDNN_BATCHNORM_SPATIAL_PERSISTENT is not worth taking. The overflow
|
|
556
|
+
// check it asks for is affordable -- cudnnQueryRuntimeError costs 0.1us
|
|
557
|
+
// either way -- but the mode is 1.3 to 1.8x faster only where the
|
|
558
|
+
// spatial extent is large, and 1.6x slower on 32x256x14x14 backward.
|
|
559
|
+
// Worse, on 32x64x56x56 it raises CUDNN_STATUS_RUNTIME_FP_OVERFLOW for x
|
|
560
|
+
// in [-2, 2] with gamma 1 and beta 0, answering bit for bit what SPATIAL
|
|
561
|
+
// does; honouring that means running SPATIAL again on top. The mode, and
|
|
562
|
+
// the batch normalization API it belongs to, are deprecated as of cuDNN
|
|
563
|
+
// 9.0.0 besides.
|
|
556
564
|
return CUDNN_BATCHNORM_SPATIAL;
|
|
557
565
|
}
|
|
558
566
|
rb_raise(rb_eRuntimeError, "Invalid axis for BatchNorm using cuDNN. Expected 1, 3 or 4 dimensions.");
|
|
@@ -337,7 +337,10 @@ public:
|
|
|
337
337
|
assert(pool_->GetFreeBytes() == kRoundSize * 8);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
//
|
|
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.
|
|
341
344
|
void TestRemoveFromFreeList() {
|
|
342
345
|
Arena& arena = pool_->GetArena(stream_ptr_);
|
|
343
346
|
ArenaIndexMap& arena_index_map = pool_->GetArenaIndexMap(stream_ptr_);
|
data/ext/cumo/cuda/nvrtc.c
CHANGED
data/ext/cumo/depend.erb
CHANGED
|
@@ -49,6 +49,13 @@ list_type_rb.each do |type_rb|
|
|
|
49
49
|
|
|
50
50
|
src : <%= list_type_cu.join(" ") %> <%= list_type_c.join(" ") %>
|
|
51
51
|
|
|
52
|
+
# mkmf writes no rule that makes an object depend on a header, so editing one
|
|
53
|
+
# and rebuilding was a no-op that kept the previous cumo.so. Rebuilding all of
|
|
54
|
+
# them is a minute and a half, so the list does not have to be per-object.
|
|
55
|
+
CUMO_HDRS = <%= Dir.glob("#{__dir__}/**/*.{h,hpp}").sort.join(" ") %>
|
|
56
|
+
|
|
57
|
+
$(OBJS): $(CUMO_HDRS)
|
|
58
|
+
|
|
52
59
|
build-ctest : <%= __dir__ %>/cuda/memory_pool_impl_test.exe
|
|
53
60
|
|
|
54
61
|
run-ctest : <%= __dir__ %>/cuda/memory_pool_impl_test.exe
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
#include "cumo/indexer.h"
|
|
8
8
|
#include "cumo/reduce_kernel.h"
|
|
9
9
|
|
|
10
|
+
#define CUMO_BIT_STAT_MEAN 0
|
|
11
|
+
#define CUMO_BIT_STAT_VAR 1
|
|
12
|
+
#define CUMO_BIT_STAT_STDDEV 2
|
|
13
|
+
#define CUMO_BIT_STAT_RMS 3
|
|
14
|
+
|
|
10
15
|
namespace cumo_bit_detail {
|
|
11
16
|
|
|
12
17
|
// A Bit reduction addresses its operands the way a numeric one does -- the
|
|
@@ -169,7 +174,7 @@ __device__ static inline uint64_t bit_count_axis(
|
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
__global__ static void bit_count_reduction_kernel(
|
|
172
|
-
cumo_na_bit_reduction_arg_t arg, cumo_detail::cumo_reduce_addr_t ad, cumo_bit_word_addr_t wa, int invert,
|
|
177
|
+
CUMO_GRID_CONSTANT cumo_na_bit_reduction_arg_t arg, CUMO_GRID_CONSTANT cumo_detail::cumo_reduce_addr_t ad, CUMO_GRID_CONSTANT cumo_bit_word_addr_t wa, int invert,
|
|
173
178
|
int out_block_size, int reduce_block_size, int64_t unit_total_size) {
|
|
174
179
|
extern __shared__ __align__(8) char sdata_raw[];
|
|
175
180
|
uint64_t* sdata = reinterpret_cast<uint64_t*>(sdata_raw);
|
|
@@ -179,8 +184,8 @@ __global__ static void bit_count_reduction_kernel(
|
|
|
179
184
|
int64_t out_total_size = arg.out_indexer.total_size;
|
|
180
185
|
int64_t reduce_total_size = arg.in_indexer.total_size / out_total_size;
|
|
181
186
|
|
|
182
|
-
int64_t reduce_offset
|
|
183
|
-
|
|
187
|
+
int64_t reduce_offset, out_offset;
|
|
188
|
+
cumo_detail::reduce_thread_split(ad, tid, out_block_size, reduce_block_size, &reduce_offset, &out_offset);
|
|
184
189
|
int64_t out_base = blockIdx.x * out_block_size;
|
|
185
190
|
int64_t out_stride = gridDim.x * out_block_size;
|
|
186
191
|
|
|
@@ -191,7 +196,7 @@ __global__ static void bit_count_reduction_kernel(
|
|
|
191
196
|
uint64_t accum = bit_count_axis(arg, ad, wa, invert, in_out_off, i_in, reduce_total_size,
|
|
192
197
|
0, unit_total_size, reduce_offset, reduce_block_size);
|
|
193
198
|
|
|
194
|
-
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, impl);
|
|
199
|
+
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, reduce_block_size, !ad.out_inner, impl);
|
|
195
200
|
if (reduce_offset == 0) {
|
|
196
201
|
*reinterpret_cast<uint64_t*>(arg.out.ptr + bit_out_offset(arg, ad, i_out)) = accum;
|
|
197
202
|
}
|
|
@@ -202,7 +207,7 @@ __global__ static void bit_count_reduction_kernel(
|
|
|
202
207
|
// grid a handful of blocks however long the reduce axis is.
|
|
203
208
|
template <typename TArg>
|
|
204
209
|
__global__ static void bit_count_partial_kernel(
|
|
205
|
-
TArg arg, cumo_detail::cumo_reduce_addr_t ad, cumo_bit_word_addr_t wa, int invert,
|
|
210
|
+
CUMO_GRID_CONSTANT TArg arg, CUMO_GRID_CONSTANT cumo_detail::cumo_reduce_addr_t ad, CUMO_GRID_CONSTANT cumo_bit_word_addr_t wa, int invert,
|
|
206
211
|
uint64_t* partial, int64_t n_split, int64_t chunk,
|
|
207
212
|
int out_block_size, int reduce_block_size, int64_t unit_total_size) {
|
|
208
213
|
extern __shared__ __align__(8) char sdata_raw[];
|
|
@@ -214,8 +219,8 @@ __global__ static void bit_count_partial_kernel(
|
|
|
214
219
|
int64_t reduce_total_size = arg.in_indexer.total_size / out_total_size;
|
|
215
220
|
int64_t partial_total_size = out_total_size * n_split;
|
|
216
221
|
|
|
217
|
-
int64_t reduce_offset
|
|
218
|
-
|
|
222
|
+
int64_t reduce_offset, out_offset;
|
|
223
|
+
cumo_detail::reduce_thread_split(ad, tid, out_block_size, reduce_block_size, &reduce_offset, &out_offset);
|
|
219
224
|
int64_t out_base = blockIdx.x * out_block_size;
|
|
220
225
|
int64_t out_stride = gridDim.x * out_block_size;
|
|
221
226
|
|
|
@@ -231,7 +236,7 @@ __global__ static void bit_count_partial_kernel(
|
|
|
231
236
|
uint64_t accum = bit_count_axis(arg, ad, wa, invert, in_out_off, i_in, reduce_total_size,
|
|
232
237
|
begin, end, reduce_offset, reduce_block_size);
|
|
233
238
|
|
|
234
|
-
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, impl);
|
|
239
|
+
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, reduce_block_size, !ad.out_inner, impl);
|
|
235
240
|
if (reduce_offset == 0) {
|
|
236
241
|
partial[i_out * n_split + i_split] = accum;
|
|
237
242
|
}
|
|
@@ -287,6 +292,71 @@ static inline bit_reduce_plan make_bit_reduce_plan(const TArg& arg) {
|
|
|
287
292
|
return p;
|
|
288
293
|
}
|
|
289
294
|
|
|
295
|
+
// mean, var, stddev and rms of a bit array are functions of how many bits are
|
|
296
|
+
// set and how long the axis is, because every element is 0 or 1. They reduce
|
|
297
|
+
// the way count does and differ only in what the store computes.
|
|
298
|
+
__device__ static inline double bit_stat_of_count(uint64_t count, int64_t reduce_total_size, int stat) {
|
|
299
|
+
double k = (double)count;
|
|
300
|
+
double n = (double)reduce_total_size;
|
|
301
|
+
double mean = k / n;
|
|
302
|
+
switch (stat) {
|
|
303
|
+
case CUMO_BIT_STAT_MEAN: return mean;
|
|
304
|
+
case CUMO_BIT_STAT_RMS: return sqrt(mean);
|
|
305
|
+
// x squared is x for a bit, so the squared deviations sum to k - k * mean.
|
|
306
|
+
case CUMO_BIT_STAT_VAR: return (k - k * mean) / (n - 1);
|
|
307
|
+
default: return sqrt((k - k * mean) / (n - 1));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
__global__ static void bit_stat_reduction_kernel(
|
|
312
|
+
CUMO_GRID_CONSTANT cumo_na_bit_reduction_arg_t arg, CUMO_GRID_CONSTANT cumo_detail::cumo_reduce_addr_t ad, CUMO_GRID_CONSTANT cumo_bit_word_addr_t wa, int stat,
|
|
313
|
+
int out_block_size, int reduce_block_size, int64_t unit_total_size) {
|
|
314
|
+
extern __shared__ __align__(8) char sdata_raw[];
|
|
315
|
+
uint64_t* sdata = reinterpret_cast<uint64_t*>(sdata_raw);
|
|
316
|
+
unsigned int tid = threadIdx.x;
|
|
317
|
+
BitCountImpl impl;
|
|
318
|
+
|
|
319
|
+
int64_t out_total_size = arg.out_indexer.total_size;
|
|
320
|
+
int64_t reduce_total_size = arg.in_indexer.total_size / out_total_size;
|
|
321
|
+
|
|
322
|
+
int64_t reduce_offset, out_offset;
|
|
323
|
+
cumo_detail::reduce_thread_split(ad, tid, out_block_size, reduce_block_size, &reduce_offset, &out_offset);
|
|
324
|
+
int64_t out_base = blockIdx.x * out_block_size;
|
|
325
|
+
int64_t out_stride = gridDim.x * out_block_size;
|
|
326
|
+
|
|
327
|
+
for (int64_t i_out = out_base + out_offset; i_out < out_total_size; i_out += out_stride) {
|
|
328
|
+
ssize_t in_out_off = bit_in_out_offset(arg, ad, i_out);
|
|
329
|
+
int64_t i_in = i_out * reduce_total_size + reduce_offset;
|
|
330
|
+
|
|
331
|
+
uint64_t accum = bit_count_axis(arg, ad, wa, 0, in_out_off, i_in, reduce_total_size,
|
|
332
|
+
0, unit_total_size, reduce_offset, reduce_block_size);
|
|
333
|
+
|
|
334
|
+
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, reduce_block_size, !ad.out_inner, impl);
|
|
335
|
+
if (reduce_offset == 0) {
|
|
336
|
+
*reinterpret_cast<double*>(arg.out.ptr + bit_out_offset(arg, ad, i_out)) =
|
|
337
|
+
bit_stat_of_count(accum, reduce_total_size, stat);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Second pass of a split statistic, laid out like bit_pred_combine_kernel.
|
|
343
|
+
__global__ static void bit_stat_combine_kernel(
|
|
344
|
+
CUMO_GRID_CONSTANT cumo_na_bit_reduction_arg_t arg, CUMO_GRID_CONSTANT cumo_detail::cumo_reduce_addr_t ad, int stat,
|
|
345
|
+
const uint64_t* partial, int64_t n_split) {
|
|
346
|
+
int64_t out_total_size = arg.out_indexer.total_size;
|
|
347
|
+
int64_t reduce_total_size = arg.in_indexer.total_size / out_total_size;
|
|
348
|
+
|
|
349
|
+
for (int64_t i_out = blockIdx.x * blockDim.x + threadIdx.x; i_out < out_total_size;
|
|
350
|
+
i_out += (int64_t)blockDim.x * gridDim.x) {
|
|
351
|
+
uint64_t count = 0;
|
|
352
|
+
for (int64_t i_split = 0; i_split < n_split; ++i_split) {
|
|
353
|
+
count += partial[i_out * n_split + i_split];
|
|
354
|
+
}
|
|
355
|
+
*reinterpret_cast<double*>(arg.out.ptr + bit_out_offset(arg, ad, i_out)) =
|
|
356
|
+
bit_stat_of_count(count, reduce_total_size, stat);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
290
360
|
// all? and any? are the count of set bits against the length of the axis and
|
|
291
361
|
// against zero, so they reduce the same way and differ only here.
|
|
292
362
|
__device__ static inline CUMO_BIT_DIGIT bit_pred_of_count(uint64_t count, int64_t reduce_total_size, int all) {
|
|
@@ -294,7 +364,7 @@ __device__ static inline CUMO_BIT_DIGIT bit_pred_of_count(uint64_t count, int64_
|
|
|
294
364
|
}
|
|
295
365
|
|
|
296
366
|
__global__ static void bit_pred_reduction_kernel(
|
|
297
|
-
cumo_na_bit_pred_reduction_arg_t arg, cumo_detail::cumo_reduce_addr_t ad, cumo_bit_word_addr_t wa, int all,
|
|
367
|
+
CUMO_GRID_CONSTANT cumo_na_bit_pred_reduction_arg_t arg, CUMO_GRID_CONSTANT cumo_detail::cumo_reduce_addr_t ad, CUMO_GRID_CONSTANT cumo_bit_word_addr_t wa, int all,
|
|
298
368
|
int out_block_size, int reduce_block_size, int64_t unit_total_size) {
|
|
299
369
|
extern __shared__ __align__(8) char sdata_raw[];
|
|
300
370
|
uint64_t* sdata = reinterpret_cast<uint64_t*>(sdata_raw);
|
|
@@ -304,8 +374,8 @@ __global__ static void bit_pred_reduction_kernel(
|
|
|
304
374
|
int64_t out_total_size = arg.out_indexer.total_size;
|
|
305
375
|
int64_t reduce_total_size = arg.in_indexer.total_size / out_total_size;
|
|
306
376
|
|
|
307
|
-
int64_t reduce_offset
|
|
308
|
-
|
|
377
|
+
int64_t reduce_offset, out_offset;
|
|
378
|
+
cumo_detail::reduce_thread_split(ad, tid, out_block_size, reduce_block_size, &reduce_offset, &out_offset);
|
|
309
379
|
int64_t out_base = blockIdx.x * out_block_size;
|
|
310
380
|
int64_t out_stride = gridDim.x * out_block_size;
|
|
311
381
|
|
|
@@ -316,7 +386,7 @@ __global__ static void bit_pred_reduction_kernel(
|
|
|
316
386
|
uint64_t accum = bit_count_axis(arg, ad, wa, 0, in_out_off, i_in, reduce_total_size,
|
|
317
387
|
0, unit_total_size, reduce_offset, reduce_block_size);
|
|
318
388
|
|
|
319
|
-
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, impl);
|
|
389
|
+
accum = cumo_detail::reduce_in_block(accum, sdata, tid, out_block_size, reduce_block_size, !ad.out_inner, impl);
|
|
320
390
|
if (reduce_offset == 0) {
|
|
321
391
|
size_t pos = (size_t)((ssize_t)arg.out.pos + bit_out_offset(arg, ad, i_out));
|
|
322
392
|
CUMO_STORE_BIT(arg.out.ptr, pos, bit_pred_of_count(accum, reduce_total_size, all));
|
|
@@ -327,7 +397,7 @@ __global__ static void bit_pred_reduction_kernel(
|
|
|
327
397
|
// Second pass of a split all? or any?. There are few outputs whenever the axis
|
|
328
398
|
// was worth splitting, so one thread per output walks its chunks.
|
|
329
399
|
__global__ static void bit_pred_combine_kernel(
|
|
330
|
-
cumo_na_bit_pred_reduction_arg_t arg, cumo_detail::cumo_reduce_addr_t ad, int all,
|
|
400
|
+
CUMO_GRID_CONSTANT cumo_na_bit_pred_reduction_arg_t arg, CUMO_GRID_CONSTANT cumo_detail::cumo_reduce_addr_t ad, int all,
|
|
331
401
|
const uint64_t* partial, int64_t n_split) {
|
|
332
402
|
int64_t out_total_size = arg.out_indexer.total_size;
|
|
333
403
|
int64_t reduce_total_size = arg.in_indexer.total_size / out_total_size;
|
|
@@ -387,6 +457,42 @@ static inline void cumo_bit_count_reduce(cumo_na_bit_reduction_arg_t arg, int in
|
|
|
387
457
|
cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
|
|
388
458
|
}
|
|
389
459
|
|
|
460
|
+
// The same for the four statistics, whose result is a DFloat of the same width
|
|
461
|
+
// as the count it is computed from.
|
|
462
|
+
static inline void cumo_bit_stat_reduce(cumo_na_bit_reduction_arg_t arg, int stat) {
|
|
463
|
+
if (arg.out_indexer.total_size == 0) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
cumo_bit_detail::bit_reduce_plan p = cumo_bit_detail::make_bit_reduce_plan(arg);
|
|
468
|
+
int64_t block_size = cumo_detail::max_block_size;
|
|
469
|
+
int64_t shared_mem_size = sizeof(uint64_t) * block_size;
|
|
470
|
+
|
|
471
|
+
if (p.n_split < 2) {
|
|
472
|
+
int64_t grid_size = std::min(cumo_detail::max_grid_size, p.out_block_num);
|
|
473
|
+
cumo_bit_detail::bit_stat_reduction_kernel<<<grid_size, block_size, shared_mem_size>>>(
|
|
474
|
+
arg, p.ad, p.wa, stat, (int)p.out_block_size, (int)p.reduce_block_size, p.unit_total_size);
|
|
475
|
+
cumo_cuda_runtime_check_kernel_launch();
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
int64_t partial_total_size = p.out_total_size * p.n_split;
|
|
480
|
+
uint64_t* partial = reinterpret_cast<uint64_t*>(cumo_cuda_runtime_malloc(sizeof(uint64_t) * partial_total_size));
|
|
481
|
+
|
|
482
|
+
int64_t grid_size = std::min(cumo_detail::max_grid_size, p.partial_block_num);
|
|
483
|
+
cumo_bit_detail::bit_count_partial_kernel<<<grid_size, block_size, shared_mem_size>>>(
|
|
484
|
+
arg, p.ad, p.wa, 0, partial, p.n_split, p.chunk,
|
|
485
|
+
(int)p.split_out_block_size, (int)p.split_reduce_block_size, p.unit_total_size);
|
|
486
|
+
cumo_cuda_runtime_check_kernel_launch();
|
|
487
|
+
|
|
488
|
+
int64_t combine_grid = (p.out_total_size + block_size - 1) / block_size;
|
|
489
|
+
if (combine_grid > cumo_detail::max_grid_size) combine_grid = cumo_detail::max_grid_size;
|
|
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();
|
|
492
|
+
|
|
493
|
+
cumo_cuda_runtime_free(reinterpret_cast<char*>(partial));
|
|
494
|
+
}
|
|
495
|
+
|
|
390
496
|
// The same for all? (all non-zero) and any? (any non-zero), whose result is a
|
|
391
497
|
// Bit rather than a count.
|
|
392
498
|
static inline void cumo_bit_pred_reduce(cumo_na_bit_pred_reduction_arg_t arg, int all) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#ifndef CUMO_CHECK_H
|
|
2
|
+
#define CUMO_CHECK_H
|
|
3
|
+
|
|
4
|
+
#include <ruby.h>
|
|
5
|
+
#include "cumo/narray.h"
|
|
6
|
+
|
|
7
|
+
#if defined(__cplusplus)
|
|
8
|
+
extern "C" {
|
|
9
|
+
#if 0
|
|
10
|
+
} /* satisfy cc-mode */
|
|
11
|
+
#endif
|
|
12
|
+
#endif
|
|
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)"); \
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#define CUMO_CHECK_SIZE_EQ(sz1,sz2) \
|
|
20
|
+
if ((sz1) != (sz2)) { \
|
|
21
|
+
rb_raise(cumo_na_eShapeError, \
|
|
22
|
+
"size mismatch: %d != %d", \
|
|
23
|
+
(int)(sz1), (int)(sz2)); \
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#define CUMO_CHECK_DIM_EQ(nd1,nd2) \
|
|
27
|
+
if ((nd1) != (nd2)) { \
|
|
28
|
+
rb_raise(cumo_na_eShapeError, \
|
|
29
|
+
"dimension mismatch: %d != %d", \
|
|
30
|
+
(int)(nd1), (int)(nd2)); \
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static inline VALUE
|
|
34
|
+
cumo_option_value(VALUE value, VALUE default_value)
|
|
35
|
+
{
|
|
36
|
+
switch(TYPE(value)) {
|
|
37
|
+
case T_NIL:
|
|
38
|
+
case T_UNDEF:
|
|
39
|
+
return default_value;
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#if defined(__cplusplus)
|
|
45
|
+
#if 0
|
|
46
|
+
{ /* satisfy cc-mode */
|
|
47
|
+
#endif
|
|
48
|
+
} /* extern "C" { */
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
#endif /* CUMO_CHECK_H */
|