cumo 0.5.9 → 0.5.11
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/.gitignore +1 -0
- data/3rd_party/mkmf-cu/lib/mkmf-cu/cli.rb +3 -1
- data/CHANGELOG.md +67 -0
- data/Gemfile +1 -0
- data/README.md +110 -7
- data/bench/cumo_probe.rb +713 -270
- data/ext/cumo/cuda/cudnn_impl.cpp +16 -5
- data/ext/cumo/cuda/memory_pool_impl.cpp +6 -1
- data/ext/cumo/cuda/memory_pool_impl_test.cpp +47 -0
- data/ext/cumo/extconf.rb +30 -1
- data/ext/cumo/include/cumo/bit_reduce_kernel.h +426 -0
- data/ext/cumo/include/cumo/cuda/cudnn.h +53 -0
- data/ext/cumo/include/cumo/indexer.h +117 -3
- data/ext/cumo/include/cumo/intern.h +2 -0
- data/ext/cumo/include/cumo/narray.h +6 -1
- data/ext/cumo/include/cumo/narray_kernel.h +41 -0
- data/ext/cumo/include/cumo/reduce_kernel.h +261 -28
- data/ext/cumo/include/cumo/template.h +2 -6
- data/ext/cumo/include/cumo/types/bit_kernel.h +89 -23
- data/ext/cumo/include/cumo/types/complex_kernel.h +5 -3
- data/ext/cumo/include/cumo/types/float_macro_kernel.h +6 -4
- data/ext/cumo/include/cumo/types/int_macro_kernel.h +0 -4
- data/ext/cumo/include/cumo/types/robject_kernel.h +0 -3
- data/ext/cumo/include/cumo/types/uint_macro_kernel.h +1 -0
- data/ext/cumo/include/cumo.h +2 -2
- data/ext/cumo/narray/data.c +24 -42
- data/ext/cumo/narray/data_kernel.cu +75 -0
- data/ext/cumo/narray/gen/spec.rb +1 -0
- data/ext/cumo/narray/gen/tmpl/accum.c +2 -11
- data/ext/cumo/narray/gen/tmpl/accum_arg.c +8 -14
- data/ext/cumo/narray/gen/tmpl/accum_arg_kernel.cu +104 -22
- data/ext/cumo/narray/gen/tmpl/accum_binary.c +33 -17
- data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +30 -81
- data/ext/cumo/narray/gen/tmpl/accum_index.c +7 -26
- data/ext/cumo/narray/gen/tmpl/accum_index_kernel.cu +104 -22
- data/ext/cumo/narray/gen/tmpl/allocate.c +19 -0
- data/ext/cumo/narray/gen/tmpl/aset.c +8 -0
- data/ext/cumo/narray/gen/tmpl/batch_norm.c +8 -6
- data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +18 -8
- data/ext/cumo/narray/gen/tmpl/binary.c +76 -0
- data/ext/cumo/narray/gen/tmpl/binary_kernel.cu +57 -0
- data/ext/cumo/narray/gen/tmpl/binary_s.c +68 -9
- data/ext/cumo/narray/gen/tmpl/binary_s_kernel.cu +44 -7
- data/ext/cumo/narray/gen/tmpl/cast.c +6 -0
- data/ext/cumo/narray/gen/tmpl/clip.c +67 -0
- data/ext/cumo/narray/gen/tmpl/clip_kernel.cu +63 -17
- data/ext/cumo/narray/gen/tmpl/complex_accum_kernel.cu +145 -0
- data/ext/cumo/narray/gen/tmpl/cond_binary.c +53 -0
- data/ext/cumo/narray/gen/tmpl/cond_binary_kernel.cu +48 -12
- data/ext/cumo/narray/gen/tmpl/cond_unary_kernel.cu +23 -10
- data/ext/cumo/narray/gen/tmpl/conv.c +7 -5
- data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +13 -10
- data/ext/cumo/narray/gen/tmpl/conv_transpose.c +8 -7
- data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +8 -6
- data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +120 -0
- data/ext/cumo/narray/gen/tmpl/gemm.c +82 -8
- data/ext/cumo/narray/gen/tmpl/minmax.c +6 -7
- data/ext/cumo/narray/gen/tmpl/new_dim0.c +17 -0
- data/ext/cumo/narray/gen/tmpl/pooling_backward.c +18 -3
- data/ext/cumo/narray/gen/tmpl/pooling_forward.c +7 -5
- data/ext/cumo/narray/gen/tmpl/pow.c +91 -0
- data/ext/cumo/narray/gen/tmpl/pow_kernel.cu +60 -12
- data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +87 -0
- data/ext/cumo/narray/gen/tmpl/seq_kernel.cu +13 -1
- data/ext/cumo/narray/gen/tmpl/sort_index.c +5 -2
- data/ext/cumo/narray/gen/tmpl/store_from.c +8 -13
- data/ext/cumo/narray/gen/tmpl/store_from_kernel.cu +16 -11
- data/ext/cumo/narray/gen/tmpl_bit/aset.c +8 -0
- data/ext/cumo/narray/gen/tmpl_bit/binary_kernel.cu +52 -12
- data/ext/cumo/narray/gen/tmpl_bit/bit_count.c +14 -31
- data/ext/cumo/narray/gen/tmpl_bit/bit_count_kernel.cu +3 -78
- data/ext/cumo/narray/gen/tmpl_bit/bit_reduce.c +14 -107
- data/ext/cumo/narray/gen/tmpl_bit/bit_reduce_kernel.cu +4 -75
- data/ext/cumo/narray/gen/tmpl_bit/mask.c +21 -5
- data/ext/cumo/narray/gen/tmpl_bit/mask_kernel.cu +52 -0
- data/ext/cumo/narray/gen/tmpl_bit/store_bit_kernel.cu +47 -10
- data/ext/cumo/narray/gen/tmpl_bit/store_from_kernel.cu +23 -10
- data/ext/cumo/narray/gen/tmpl_bit/unary_kernel.cu +49 -11
- data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +18 -16
- data/ext/cumo/narray/index.c +83 -29
- data/ext/cumo/narray/index_kernel.cu +4 -4
- data/ext/cumo/narray/narray.c +17 -4
- data/ext/cumo/narray/ndloop.c +10 -6
- data/ext/cumo/narray/ndloop_kernel.cu +32 -0
- data/test/bit_test.rb +477 -4
- data/test/cudnn_test.rb +198 -0
- data/test/cumo_test.rb +59 -0
- data/test/narray_test.rb +1263 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b345c4e036212d334600041165fe79e533ef0e1dd450e22b1288f5e6d6214ffa
|
|
4
|
+
data.tar.gz: af60f78cdd2f39a96e8a183eff02a26c26d8977560ba148643c55df5961c2bf2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 361042e17c2f498b8e4f5ddc76bbd119688a6a3a3ac176307973fd51c75b332d4d831a82c0fc0e8a3b28be98bfbe50d507ec67d517c609c2342caf57d94d9c41
|
|
7
|
+
data.tar.gz: 53da42c447fe31812654e1ab0566f5e553f781daaf75b97bb1c36630b8dc3ed195704002990bc4a2a9f4fdd6e26e92c2d5905ff64da0e0f058b01dae20ae3dec
|
data/.gitignore
CHANGED
|
@@ -40,7 +40,9 @@ module MakeMakefileCuda
|
|
|
40
40
|
s = MakeMakefileCuda::Nvcc.generate(argv)
|
|
41
41
|
cmd = "nvcc #{s}"
|
|
42
42
|
if ENV['CUMO_NVCC_GENERATE_CODE']
|
|
43
|
-
|
|
43
|
+
ENV['CUMO_NVCC_GENERATE_CODE'].split.each do |code|
|
|
44
|
+
cmd << " --generate-code=#{code}"
|
|
45
|
+
end
|
|
44
46
|
else
|
|
45
47
|
capability = nil
|
|
46
48
|
if find_executable('nvidia-smi')
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
+
# 0.5.11 (2026/08/29)
|
|
2
|
+
|
|
3
|
+
Breaking changes:
|
|
4
|
+
|
|
5
|
+
* `conv` and `pooling` raise `ArgumentError` on a stride of zero, where the assert meant to catch it never ran under `NDEBUG` and the division by it raised SIGFPE (PR #307)
|
|
6
|
+
|
|
7
|
+
Fixes:
|
|
8
|
+
|
|
9
|
+
* Fix a numeric subscript into a view built from an index array reading the parent's index before the kernel that fills it, which answered with whatever the memory held (PR #333)
|
|
10
|
+
* Fix `mulsum` answering from the wrong bits when an operand is a `Cumo::Bit` view carrying an index array, `cumo_na_copy` moving whole bytes where a Bit element is one bit (PR #326)
|
|
11
|
+
|
|
12
|
+
Changes:
|
|
13
|
+
|
|
14
|
+
* Read a view's index array through one synchronize instead of one per row of an md-loop, one per element read, or one per one-element subscript; with twenty kernels queued, `rand` over a 2000-row gather goes from 11.2ms to 3.4ms, a second `a[idx, true][0, 0]` from 54.2us to 0.2us, and `a[[0], true] * 2.0` from 104.3us to 3.5us (PR #334, PR #332, PR #331)
|
|
15
|
+
* Gather a word of a `Cumo::Bit` operand from its rows rather than a bit at a time, and fold a Bit reduction by words within a row rather than only across a flat group; on a 512x2048 column slice `dst.store(slice)` goes from 18.0us to 8.9us and `count_true` from 22.2us to 8.8us (PR #328, PR #327)
|
|
16
|
+
* Reduce `Bit#count_true`, `count_false`, `all?` and `any?` in one kernel instead of one launch per output element; 512x2048 `count_true(axis: 1)` goes from 904.0us to 10.0us and `all?(axis: 1)` from 498.7us to 12.7us (PR #324, PR #323)
|
|
17
|
+
* Address a store's index array from its kernel instead of staging it through a contiguous buffer; a 512x2048 SFloat store into an indexed destination goes from 30.1us to 12.4us (PR #322)
|
|
18
|
+
* Copy an ndloop buffer one element per thread instead of one row; `a[idx, true] = b` on a 1024x1024 SFloat goes from 1567.3us to 67.5us (PR #319)
|
|
19
|
+
* Build `flatten`'s index array with a kernel instead of a host loop; a 512x2048 SFloat column slice goes from 2448.5us to 529.1us (PR #316)
|
|
20
|
+
* Reduce `mulsum` in one kernel instead of one per output element; 512x2048 SFloat `mulsum(axis: 1)` goes from 1758.8us to 13.6us (PR #315)
|
|
21
|
+
* Scatter one element per lane in the compaction behind `where`, `where2` and `mask`; at 4M elements `mask` goes from 193.8us to 106.2us and `where` from 131.4us to 74.1us (PR #313)
|
|
22
|
+
* Build a contiguous Bit output a word at a time with `__ballot_sync`, whatever the operand's layout; at 4M elements `a > 0.5` goes from 459.0us to 31.5us and `view & view` from 481.5us to 42.6us (PR #312, PR #311)
|
|
23
|
+
* Count bits a word at a time instead of one atomicAdd per element; 16M bits all set go from 227.0us to 17.9us (PR #309)
|
|
24
|
+
* Document that a Ruby Float in `Cumo::NMath` promotes the result to double, and that passing a 0-dimensional array keeps it single (PR #325)
|
|
25
|
+
* Drop the contiguity assert the cuDNN descriptors do not need (PR #308)
|
|
26
|
+
* Sweep every kernel family in `bench/cumo_probe.rb`, measure the kernel rather than the allocation it sits above, and read a run against a saved baseline (PR #330, PR #329, PR #321, PR #320, PR #318, PR #317, PR #314, PR #310)
|
|
27
|
+
|
|
28
|
+
# 0.5.10 (2026/08/23)
|
|
29
|
+
|
|
30
|
+
Breaking changes:
|
|
31
|
+
|
|
32
|
+
* An indexed assignment to a frozen view raises instead of writing through it; `store` and `fill` already raised (PR #306)
|
|
33
|
+
* The cuDNN and cuBLAS entry points turn away arguments they used to read or write out of bounds: `pooling_backward` requires `y` and `gy` to carry its own dtype and the pooling output shape of `x`, `gemm` requires a contiguous `c`, `batch_norm` requires the reduced size to cover x's channels, and `conv_grad_w` raises on a mismatched `gy` rather than asserting (PR #303, PR #302, PR #300, PR #278)
|
|
34
|
+
* `seq` on the unsigned types wraps a negative start and a start past 2**63 the way `fill` and `cast` do, where they collapsed to zero and clamped to INT64_MAX (PR #295, PR #294)
|
|
35
|
+
* A negative power of an unsigned array answers zero instead of spinning the device until SIGKILL (PR #293)
|
|
36
|
+
* A subscript that is a Cumo::RObject subclass, and an `expand_dims` past `CUMO_NA_MAX_DIMENSION`, raise instead of being taken (PR #272, PR #270)
|
|
37
|
+
|
|
38
|
+
Fixes:
|
|
39
|
+
|
|
40
|
+
* Fix `[]=` writing through a frozen view, the store going to a derived view whose data object is the root rather than the receiver (PR #306)
|
|
41
|
+
* Fix `at()` reading the accumulator in place of the subscript on a reversed view, which addressed far in front of the buffer (PR #305)
|
|
42
|
+
* Fix `cumo_cuda_cudnn_CreateBNTensorDescriptor` returning before it derived the descriptor, an inverted status check that no caller reached (PR #304)
|
|
43
|
+
* Fix `batch_norm`, `fixed_batch_norm` and `batch_norm_backward` reading and writing past their parameters, the sizes being checked against the reduced shape while cuDNN reaches x's channel count (PR #303)
|
|
44
|
+
* Fix `gemm` writing past its allocation when `c` is a non-contiguous inplace view, and over whatever the pool put next to it (PR #302)
|
|
45
|
+
* Fix `pooling_backward` reading past `y` and `gy`, and returning what it read in `gx` (PR #300)
|
|
46
|
+
* Fix `pow_int` negating INT32_MIN, which overflows and leaves the loop unbounded (PR #299)
|
|
47
|
+
* Fix the device free running for a subscript that allocated no index array (PR #297)
|
|
48
|
+
* Fix a NULL dereference from `at()` given a scalar subscript (PR #271)
|
|
49
|
+
* Fix an out-of-bounds write through an unchecked cuDNN output array (PR #269)
|
|
50
|
+
* Fix `gemm` reading past an operand that carries fewer batch dimensions (PR #268)
|
|
51
|
+
|
|
52
|
+
Changes:
|
|
53
|
+
|
|
54
|
+
* Pass a numeric operand to the kernel instead of casting it to a 0-dimensional array first, in the operators, `clip`, `pow`, the comparisons, the NMath functions and the coerced left-hand side; GPT-2 124M decode drops from 787 to 677 kernel launches a token (PR #296, PR #291, PR #290, PR #289, PR #288, PR #287)
|
|
55
|
+
* Run the NMath binary functions through the indexer loop instead of one kernel launch per row; 11x on a transposed operand and up to 60x on the cheap kernels (PR #292)
|
|
56
|
+
* Copy a reduction operand only when it carries an index array, where any non-contiguous view was copied whole (PR #286)
|
|
57
|
+
* Run the nan-aware reductions, the nan-aware index reductions, `kahan_sum` and `Bit#mask` on the GPU (PR #277, PR #276, PR #275, PR #273)
|
|
58
|
+
* Take a transposed batch of matrices with the cuBLAS transpose flag rather than duplicating it, which saves a temporary the size of the operand (PR #283)
|
|
59
|
+
* Emit indexer accessors up to eight dimensions (PR #284)
|
|
60
|
+
* Drop a free-list bin from the pool arena when `Malloc` empties it (PR #279)
|
|
61
|
+
* Check the cuDNN header and version, not just the library (PR #280)
|
|
62
|
+
* Document every method that returns a 0-dimensional NArray, and what keeping scalars on the device buys (PR #298, PR #282)
|
|
63
|
+
* Build two GPU architectures in CI instead of every one (PR #285)
|
|
64
|
+
* Add `fiddle` to the Gemfile (PR #281)
|
|
65
|
+
* Remove TODO comments whose questions have been answered (PR #274)
|
|
66
|
+
* Cover the Int32 array exponent in the INT_MIN power test (PR #301)
|
|
67
|
+
|
|
1
68
|
# 0.5.9 (2026/08/20)
|
|
2
69
|
|
|
3
70
|
Breaking changes:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Cumo (pronounced "koomo") is a CUDA-aware, GPU-optimized numerical library that
|
|
|
9
9
|
* Ruby 3.0 or later
|
|
10
10
|
* NVIDIA GPU Compute Capability 3.5 (Kepler) or later
|
|
11
11
|
* CUDA 11.0 or later
|
|
12
|
+
* cuDNN 8.0 or later (optional, for the cuDNN features)
|
|
12
13
|
|
|
13
14
|
## Preparation
|
|
14
15
|
|
|
@@ -97,17 +98,31 @@ a = xm::DFloat.new(3,5).seq
|
|
|
97
98
|
|
|
98
99
|
### Incompatibility With Numo
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
Numo returns a Ruby numeric object wherever a result is 0-dimensional, while Cumo returns the 0-dimensional NArray itself.
|
|
102
|
+
Cumo differs in this way to avoid synchronization and minimize CPU ⇄ GPU data transfer.
|
|
103
|
+
That is not only a cost of the port; see [Keeping Scalars On The Device](#keeping-scalars-on-the-device) and [Ruby Floats In NMath Promote To Double](#ruby-floats-in-nmath-promote-to-double) for what it buys.
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
* `[]`
|
|
104
|
-
* `count_true`
|
|
105
|
-
* `count_false`
|
|
105
|
+
The methods affected are:
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
* `[]` and `extract`
|
|
108
|
+
* `count_true` and `count_false`
|
|
109
|
+
* reductions down to a single value: `sum`, `prod`, `mean`, `stddev`, `var`, `rms`, `min`, `max`, `ptp`, `minmax`, `median`, `mulsum`, `dot`, `inner`
|
|
110
|
+
* index reductions: `max_index`, `min_index`, `argmax`, `argmin`
|
|
111
|
+
|
|
112
|
+
A 0-dimensional `Cumo::Bit` is truthy even when it holds 0, because Ruby treats every object but `nil` and `false` as true.
|
|
113
|
+
Comparing two scalars therefore takes the wrong branch without raising anything:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
a = Cumo::SFloat[5.0]
|
|
117
|
+
a[0] < 1.0 #=> Cumo::Bit#shape=[] holding 0
|
|
118
|
+
(a[0] < 1.0) ? :yes : :no #=> :yes, where Numo gives :no
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`assert_operator(a[0], :<, 1.0)` passes for the same reason, so a test suite written for Numo can stay green against Cumo while asserting nothing.
|
|
122
|
+
Read the value back to the host before branching on it, or run under `compatible_mode`.
|
|
109
123
|
|
|
110
124
|
Set the `CUMO_COMPATIBLE_MODE` environment variable to `ON` to force Numo NArray compatibility (for worse performance).
|
|
125
|
+
Running a Numo test suite that way keeps its assertions meaningful.
|
|
111
126
|
|
|
112
127
|
You may enable or disable `compatible_mode` as:
|
|
113
128
|
|
|
@@ -126,6 +141,87 @@ You can also use the following methods which behave like Numo's NArray methods.
|
|
|
126
141
|
* `count_true_cpu`
|
|
127
142
|
* `count_false_cpu`
|
|
128
143
|
|
|
144
|
+
```ruby
|
|
145
|
+
a.aref_cpu(0) < 1.0 #=> false in either mode
|
|
146
|
+
Float(a.sum) #=> 7.0 in either mode
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
They are methods on an NArray, so chaining one onto a result that `compatible_mode` has already turned into a Ruby object, as in `a.sum.extract_cpu`, raises `NoMethodError` while the mode is on.
|
|
150
|
+
`Kernel#Float` and `Kernel#Integer` read either representation, and read Numo's too, so they are what code that runs against both libraries wants.
|
|
151
|
+
|
|
152
|
+
### Keeping Scalars On The Device
|
|
153
|
+
|
|
154
|
+
The 0-dimensional return is what lets an iterative loop stay on the GPU.
|
|
155
|
+
Reading a scalar back to the host waits for everything queued behind it, so every read caps how far ahead the GPU is allowed to run.
|
|
156
|
+
What a read costs is not a fixed price either: it is however much work happens to be queued when it is taken.
|
|
157
|
+
|
|
158
|
+
`bench/cg_bench.rb` prices this with a conjugate gradient solve, 200 iterations over a 512x512 grid on an RTX 5070 Ti Laptop:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
scalars convergence test us/iter readbacks/iter
|
|
162
|
+
Ruby Floats every iteration 136.7 2.02
|
|
163
|
+
Ruby Floats never 134.4 2.02
|
|
164
|
+
0-dim NArray every iteration 142.6 1.02
|
|
165
|
+
0-dim NArray every 20th 65.6 0.06
|
|
166
|
+
0-dim NArray never 60.3 0.02
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Written with Ruby Floats the loop reads back twice an iteration whatever the convergence test does, since `alpha` needs `pap` and `beta` needs `rs_new` as Floats.
|
|
170
|
+
Thinning the test cannot get under that floor, and keeping the scalars as 0-dimensional NArrays buys nothing on its own.
|
|
171
|
+
The two only pay together, and together they are worth 2.1x.
|
|
172
|
+
The relative residual is identical in every row.
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
alpha = rs_old / pap # a 0-dimensional NArray, divided on the device
|
|
176
|
+
x += p_dir * alpha # and consumed there, without crossing the bus
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Read the value back once the loop is done, or every k iterations if it has to test something.
|
|
180
|
+
|
|
181
|
+
### Ruby Floats In NMath Promote To Double
|
|
182
|
+
|
|
183
|
+
`Cumo::NMath` picks the module it dispatches to from every argument it is given, and a Ruby `Float` counts as a `DFloat` there.
|
|
184
|
+
A single-precision array therefore comes back doubled whenever a plain Float rides along, even though the arithmetic operators leave it alone:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
Cumo::NMath.atan2(a, 2.0) #=> Cumo::DFloat
|
|
188
|
+
Cumo::NMath.atan2(a, b) #=> Cumo::SFloat
|
|
189
|
+
a + 2.0 #=> Cumo::SFloat
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Numo promotes the same way, and on a CPU it costs nothing: Numo's single-precision math computes in double and narrows the result anyway.
|
|
193
|
+
On a GeForce card, whose double-precision rate is a sixty-fourth of its single-precision one, it costs a great deal.
|
|
194
|
+
512x2048 elements in place on an RTX 5070 Ti Laptop:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
SFloat DFloat
|
|
198
|
+
a * 2.0 11.8 us 12.8 us
|
|
199
|
+
sqrt 11.0 us 43.0 us
|
|
200
|
+
sin 11.5 us 97.5 us
|
|
201
|
+
atan 11.1 us 122.9 us
|
|
202
|
+
atan2 12.5 us 192.4 us
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Only the transcendentals pay for the promotion; a double multiply runs at the speed of a single one.
|
|
206
|
+
The methods a Float can reach as a second argument are `atan2`, `hypot` and `ldexp`.
|
|
207
|
+
`ldexp` pays a different way, since scaling by a power of two is cheap in either precision: `Cumo::NMath.ldexp(a, 2.0)` takes 268.0 us against 12.6 us for `Cumo::NMath.ldexp(a, 2)`, and the difference there is the doubled arrays it has to allocate rather than the arithmetic.
|
|
208
|
+
|
|
209
|
+
Pass a 0-dimensional array instead of a Float and the call stays single precision.
|
|
210
|
+
That is what `[]` hands back, so a scalar taken out of an array is already in the right form:
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
two = Cumo::SFloat[2.0][0] # a 0-dimensional Cumo::SFloat
|
|
214
|
+
Cumo::NMath.atan2(a, two) #=> Cumo::SFloat, 14.9 us against 219.3 us
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Naming the module directly works too, under both libraries:
|
|
218
|
+
|
|
219
|
+
```ruby
|
|
220
|
+
Cumo::SFloat::Math.atan2(a, 2.0) #=> Cumo::SFloat
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The 0-dimensional form has no effect under Numo, where `[]` returns a Ruby Float.
|
|
224
|
+
|
|
129
225
|
### Select a GPU device ID
|
|
130
226
|
|
|
131
227
|
Set the `CUDA_VISIBLE_DEVICES=id` environment variable, or
|
|
@@ -201,7 +297,14 @@ ln -sf "$HOME/opt/ccache/bin/ccache" "$HOME/opt/ccache/bin/nvcc"
|
|
|
201
297
|
bundle exec env CUMO_NVCC_GENERATE_CODE=arch=compute_60,code=sm_60 rake compile
|
|
202
298
|
```
|
|
203
299
|
|
|
300
|
+
Separate the entries with a space to build for more than one architecture:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
bundle exec env CUMO_NVCC_GENERATE_CODE="arch=compute_75,code=sm_75 arch=compute_121,code=sm_121" rake compile
|
|
304
|
+
```
|
|
305
|
+
|
|
204
306
|
This is useful even on development because it makes it possible to skip JIT compilation of PTX to cubin during runtime.
|
|
307
|
+
Without it, and without an `nvidia-smi` to read the local compute capability from, the build covers every architecture the CUDA version supports.
|
|
205
308
|
|
|
206
309
|
### Run tests with gdb
|
|
207
310
|
|