cumo 0.9.0 → 0.11.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 +93 -0
- data/README.md +382 -150
- data/bench/cumo_probe.rb +6 -2
- data/bench/cumo_sync_probe.rb +1 -1
- data/cumo.gemspec +1 -1
- data/ext/cumo/cuda/cublas.c +6 -1
- data/ext/cumo/cuda/cudnn.c +5 -1
- data/ext/cumo/cuda/cudnn_impl.cpp +31 -6
- data/ext/cumo/cuda/driver.c +214 -1
- data/ext/cumo/cuda/memory_pool.cpp +15 -9
- data/ext/cumo/cuda/nvrtc.c +42 -0
- data/ext/cumo/cuda/runtime.c +627 -1
- data/ext/cumo/cumo.c +43 -12
- data/ext/cumo/depend.erb +1 -1
- data/ext/cumo/include/cumo/bit_reduce_kernel.h +10 -10
- data/ext/cumo/include/cumo/cuda/cudnn.h +14 -5
- data/ext/cumo/include/cumo/cuda/cumo_thrust.hpp +7 -0
- data/ext/cumo/include/cumo/cuda/runtime.h +77 -3
- data/ext/cumo/include/cumo/indexer.h +126 -9
- data/ext/cumo/include/cumo/intern.h +13 -2
- data/ext/cumo/include/cumo/ndloop.h +21 -0
- data/ext/cumo/include/cumo/reduce_kernel.h +43 -41
- data/ext/cumo/include/cumo/row_kernel.h +79 -17
- data/ext/cumo/include/cumo/template.h +1 -5
- data/ext/cumo/include/cumo/template_kernel.h +18 -1
- data/ext/cumo/include/cumo/types/f16_macro.h +23 -1
- data/ext/cumo/include/cumo/types/f16_macro_kernel.h +23 -1
- data/ext/cumo/include/cumo/types/float_macro.h +23 -1
- data/ext/cumo/include/cumo/types/float_macro_kernel.h +23 -1
- data/ext/cumo/include/cumo.h +9 -2
- data/ext/cumo/narray/data.c +109 -2
- data/ext/cumo/narray/data_kernel.cu +237 -93
- data/ext/cumo/narray/gen/cogen_kernel.rb +24 -1
- data/ext/cumo/narray/gen/def/bfloat.rb +0 -2
- data/ext/cumo/narray/gen/def/bit.rb +0 -3
- data/ext/cumo/narray/gen/def/dcomplex.rb +1 -3
- data/ext/cumo/narray/gen/def/dfloat.rb +1 -3
- data/ext/cumo/narray/gen/def/hfloat.rb +0 -2
- data/ext/cumo/narray/gen/def/int16.rb +0 -3
- data/ext/cumo/narray/gen/def/int32.rb +0 -3
- data/ext/cumo/narray/gen/def/int64.rb +0 -3
- data/ext/cumo/narray/gen/def/int8.rb +0 -3
- data/ext/cumo/narray/gen/def/robject.rb +0 -3
- data/ext/cumo/narray/gen/def/scomplex.rb +1 -3
- data/ext/cumo/narray/gen/def/sfloat.rb +1 -3
- data/ext/cumo/narray/gen/def/uint16.rb +0 -3
- data/ext/cumo/narray/gen/def/uint32.rb +0 -3
- data/ext/cumo/narray/gen/def/uint64.rb +0 -3
- data/ext/cumo/narray/gen/def/uint8.rb +0 -3
- data/ext/cumo/narray/gen/narray_def.rb +7 -0
- data/ext/cumo/narray/gen/spec.rb +2 -0
- data/ext/cumo/narray/gen/tmpl/aref_cpu.c +8 -0
- data/ext/cumo/narray/gen/tmpl/binary2_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/binary_kernel.cu +72 -22
- data/ext/cumo/narray/gen/tmpl/binary_s_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/bincount_kernel.cu +4 -4
- data/ext/cumo/narray/gen/tmpl/clip_kernel.cu +4 -31
- data/ext/cumo/narray/gen/tmpl/cond_binary_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/cond_unary_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/conv.c +3 -2
- data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +3 -2
- data/ext/cumo/narray/gen/tmpl/conv_transpose.c +3 -2
- data/ext/cumo/narray/gen/tmpl/cum.c +66 -37
- data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +210 -19
- data/ext/cumo/narray/gen/tmpl/each.c +4 -3
- data/ext/cumo/narray/gen/tmpl/each_with_index.c +4 -3
- data/ext/cumo/narray/gen/tmpl/ewcomp_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/extract_cpu.c +8 -0
- data/ext/cumo/narray/gen/tmpl/extract_data.c +8 -0
- data/ext/cumo/narray/gen/tmpl/eye_kernel.cu +1 -1
- data/ext/cumo/narray/gen/tmpl/fill.c +11 -3
- data/ext/cumo/narray/gen/tmpl/fill_kernel.cu +20 -11
- data/ext/cumo/narray/gen/tmpl/format.c +2 -2
- data/ext/cumo/narray/gen/tmpl/format_to_a.c +2 -2
- data/ext/cumo/narray/gen/tmpl/frexp_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/gemm.c +21 -28
- data/ext/cumo/narray/gen/tmpl/inspect.c +0 -1
- data/ext/cumo/narray/gen/tmpl/layer_norm_kernel.cu +98 -0
- data/ext/cumo/narray/gen/tmpl/logseq.c +28 -14
- data/ext/cumo/narray/gen/tmpl/logseq_kernel.cu +19 -23
- data/ext/cumo/narray/gen/tmpl/map_with_index.c +5 -1
- data/ext/cumo/narray/gen/tmpl/median.c +9 -4
- data/ext/cumo/narray/gen/tmpl/new_dim0_kernel.cu +1 -1
- data/ext/cumo/narray/gen/tmpl/poly_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/pow_kernel.cu +3 -21
- data/ext/cumo/narray/gen/tmpl/rand.c +20 -13
- data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +15 -24
- data/ext/cumo/narray/gen/tmpl/rand_norm.c +12 -24
- data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +15 -24
- data/ext/cumo/narray/gen/tmpl/rms_norm_kernel.cu +67 -0
- data/ext/cumo/narray/gen/tmpl/seq.c +9 -3
- data/ext/cumo/narray/gen/tmpl/seq_kernel.cu +11 -15
- data/ext/cumo/narray/gen/tmpl/set2.c +13 -4
- data/ext/cumo/narray/gen/tmpl/set2_kernel.cu +9 -15
- data/ext/cumo/narray/gen/tmpl/softmax_kernel.cu +73 -7
- data/ext/cumo/narray/gen/tmpl/sort.c +7 -6
- data/ext/cumo/narray/gen/tmpl/sort_index.c +1 -1
- data/ext/cumo/narray/gen/tmpl/store.c +14 -0
- data/ext/cumo/narray/gen/tmpl/store_array.c +3 -2
- data/ext/cumo/narray/gen/tmpl/store_array_kernel.cu +4 -4
- data/ext/cumo/narray/gen/tmpl/store_bit_kernel.cu +1 -10
- data/ext/cumo/narray/gen/tmpl/store_from_kernel.cu +6 -21
- data/ext/cumo/narray/gen/tmpl/store_numeric.c +9 -5
- data/ext/cumo/narray/gen/tmpl/to_a.c +1 -2
- data/ext/cumo/narray/gen/tmpl/unary2_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/unary_kernel.cu +40 -12
- data/ext/cumo/narray/gen/tmpl/unary_ret2_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl/unary_s_kernel.cu +40 -12
- data/ext/cumo/narray/gen/tmpl_bit/aref_cpu.c +3 -2
- data/ext/cumo/narray/gen/tmpl_bit/binary_kernel.cu +3 -12
- data/ext/cumo/narray/gen/tmpl_bit/bit_count.c +6 -5
- data/ext/cumo/narray/gen/tmpl_bit/bit_reduce.c +23 -21
- data/ext/cumo/narray/gen/tmpl_bit/each.c +4 -3
- data/ext/cumo/narray/gen/tmpl_bit/each_with_index.c +4 -3
- data/ext/cumo/narray/gen/tmpl_bit/extract_cpu.c +1 -2
- data/ext/cumo/narray/gen/tmpl_bit/fill_kernel.cu +2 -11
- data/ext/cumo/narray/gen/tmpl_bit/format.c +2 -3
- data/ext/cumo/narray/gen/tmpl_bit/format_to_a.c +2 -3
- data/ext/cumo/narray/gen/tmpl_bit/inspect.c +1 -2
- data/ext/cumo/narray/gen/tmpl_bit/mask_kernel.cu +3 -3
- data/ext/cumo/narray/gen/tmpl_bit/store_array.c +3 -2
- data/ext/cumo/narray/gen/tmpl_bit/store_array_kernel.cu +5 -5
- data/ext/cumo/narray/gen/tmpl_bit/store_bit_kernel.cu +3 -12
- data/ext/cumo/narray/gen/tmpl_bit/store_from_kernel.cu +1 -10
- data/ext/cumo/narray/gen/tmpl_bit/to_a.c +2 -3
- data/ext/cumo/narray/gen/tmpl_bit/unary.c +6 -0
- data/ext/cumo/narray/gen/tmpl_bit/unary_kernel.cu +3 -12
- data/ext/cumo/narray/gen/tmpl_bit/where.c +2 -2
- data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +4 -4
- data/ext/cumo/narray/index.c +5 -4
- data/ext/cumo/narray/index_kernel.cu +13 -13
- data/ext/cumo/narray/narray.c +357 -56
- data/ext/cumo/narray/ndloop.c +331 -26
- data/ext/cumo/narray/ndloop_kernel.cu +87 -89
- data/ext/cumo/narray/sort_kernel.cu +88 -40
- data/lib/cumo/cuda/compiler.rb +95 -30
- data/lib/cumo/cuda/device.rb +8 -1
- data/lib/cumo/cuda/elementwise_kernel.rb +144 -0
- data/lib/cumo/cuda/event.rb +68 -0
- data/lib/cumo/cuda/function.rb +45 -0
- data/lib/cumo/cuda/module.rb +30 -3
- data/lib/cumo/cuda/nvrtc_program.rb +16 -1
- data/lib/cumo/cuda/pinned_memory.rb +93 -0
- data/lib/cumo/cuda/reduction_kernel.rb +310 -0
- data/lib/cumo/cuda/stream.rb +114 -0
- data/lib/cumo/cuda/user_kernel.rb +253 -0
- data/lib/cumo/cuda.rb +6 -0
- data/lib/cumo/narray/extra.rb +35 -3
- data/test/bit_test.rb +233 -7
- data/test/cuda/compiler_test.rb +84 -0
- data/test/cuda/device_test.rb +42 -0
- data/test/cuda/elementwise_kernel_test.rb +305 -0
- data/test/cuda/function_test.rb +162 -0
- data/test/cuda/module_test.rb +15 -0
- data/test/cuda/name_expression_test.rb +172 -0
- data/test/cuda/pinned_memory_test.rb +138 -0
- data/test/cuda/reduction_kernel_test.rb +276 -0
- data/test/cuda/runtime_test.rb +21 -0
- data/test/cuda/stream_test.rb +247 -0
- data/test/cudnn_test.rb +54 -0
- data/test/cumo_test.rb +47 -0
- data/test/extra_test.rb +46 -0
- data/test/fused_test.rb +24 -3
- data/test/math_test.rb +163 -1
- data/test/narray_alt_coverage_test.rb +117 -1
- data/test/narray_test.rb +1205 -2
- metadata +22 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d50aabaf6d47d63e97fc6c68726cac94af2ecb8b737a3441de618860d2f5ab6a
|
|
4
|
+
data.tar.gz: 9d7bfc02015586d0ef533802be1d48558abf93fddf362ce445c55b0b4713ab7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99f47260f8dbbb9320d2f74984262e328fd42c2dac0321300b6087712dd129d03f80d65a2089f12f624ea2814e59eb8400748c9bf08f3937b09c0bceb452f417
|
|
7
|
+
data.tar.gz: a3ccc77b320c0bfdaa64abed339cc22ce5099638f8a52e9abc5a44a1e144295f56581b43ebf99f5a431d54bbe7bed2fe590975abd59cb6b7e1a9008878c91f23
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
|
+
# 0.11.0 (2026/09/27)
|
|
2
|
+
|
|
3
|
+
Breaking changes:
|
|
4
|
+
|
|
5
|
+
* `sort`, `sort_index`, `cumsum` and `cumprod` raise `ArgumentError` on `keepdims:`, which they accepted and ignored, since they keep the shape of their input. They take `axis:` and `nan:`, and `nan:` stays on every type as it does for `sum` and `median`. Numo still accepts `keepdims:` here without a word (PR #552)
|
|
6
|
+
* `reverse` raises `ArgumentError` on `keepdims:` and `nan:`, which it accepted and ignored, and takes `axis:` only. Numo still accepts both without a word (PR #551)
|
|
7
|
+
|
|
8
|
+
Fixes:
|
|
9
|
+
|
|
10
|
+
* Fix a store from an overlapping part of the same array, such as `a[0...-k] = a[k..]`, writing wrong values, since threads read elements other threads had already overwritten. With 10 million SFloat elements and a shift of 1024 every run was wrong, on every path and every dtype. The source is now copied first when it shares an element with the destination, which gives numpy's answer (PR #553, PR #542)
|
|
11
|
+
* Fix `ElementwiseKernel` and `ReductionKernel` racing when an input shares memory with an output but is not read element for element, such as a contiguous input shifted against its output. Such an input is copied first (PR #535)
|
|
12
|
+
* Fix a leak of the new shape when `expand_dims` cannot allocate its strides (PR #563)
|
|
13
|
+
|
|
14
|
+
Changes:
|
|
15
|
+
|
|
16
|
+
* `cumsum` and `cumprod` down axis 0, or along a transposed view, gather and scatter their buffer through a shared-memory tile: on a 2048 x 2048 SFloat, `a.cumsum(axis: 0)` spends 133 us on the GPU where it spent 315, and the gather for `a.transpose.cumsum(axis: 1)` takes 51 us where it took 154 (PR #565)
|
|
17
|
+
* An elementwise op on a view built on an index array gathers it into a buffer a typed element at a time rather than with a memcpy of a run-time size: `a[idx, true] + 1` on a 512 x 512 SFloat spends 2.75 us in the gather where it spent 5.2 (PR #564)
|
|
18
|
+
* Say in the API docs of `MemoryPool.used_bytes` and `total_bytes` what each counts and when, and in the README that `used_bytes` waits for the garbage collector where CuPy's `used_bytes()` and PyTorch's `memory_allocated()` do not (PR #562)
|
|
19
|
+
* `ElementwiseKernel` and `ReductionKernel` accept `Cumo::HFloat` and `Cumo::BFloat` as `float16` and `bfloat16`, CUDA's `__half` and `__nv_bfloat16`. A Ruby number handed to one is rounded on the host the way `Cumo::HFloat[x]` and `Cumo::BFloat[x]` round it (PR #561)
|
|
20
|
+
* CI builds against CUDA 13.4.1 and 12.9.2 where it built against 13.2.1, and keeps 12.8.1 (PR #560)
|
|
21
|
+
* `concatenate`, and `hstack`, `vstack` and `dstack` through it, write every part in one kernel launch when every part is a contiguous array of the result's class, and no longer fill the result with zeros first: `hstack` of six SFloat parts of 1500 x 64 takes 5.2 us where it took 18.3 (PR #559)
|
|
22
|
+
* Elementwise ops, bias adds and math functions such as `gelu` move 16 bytes of each operand a thread when the last axis is contiguous in every operand: at 1500 x 384 and 1500 x 1536 SFloat, an add takes 4.18 us where it took 5.67, a bias add 15.1 where it took 27.4, and `gelu` 16.7 where it took 23.7. An HFloat add is 2.3 times faster and an Int8 add 3.6 times. Integer `div`, `mod` and `reciprocal`, and DComplex, keep the old loop (PR #558)
|
|
23
|
+
* Say in the README and in comments why one spelling is faster than another rather than the timings of one GPU, which read as wrong on another card (PR #557, PR #556)
|
|
24
|
+
* `layer_norm` and `rms_norm` hold a row in registers when it divides into sixteen-byte vectors and fits in eight of them a thread, and read it once: `layer_norm` over 1500 x 384 takes 6.7 us where it took 18.5. `layer_norm` takes the variance about the mean in a second pass, which is also more accurate, and a row whose sum overflows near the top of the range stays finite (PR #555)
|
|
25
|
+
* `softmax` holds a row of up to 16 elements a thread in registers and reads it once: 1500 x 1500 takes 18.8 us where it took 34.4. SFloat and DFloat answers move by a few ulp, and HFloat and BFloat come closer to the reference, since `exp` is no longer rounded to half precision before the scale (PR #554)
|
|
26
|
+
* `sort`, `sort_index`, `cumsum`, `cumprod` and `reverse` accept an empty array and answer an empty array of the same shape, where they raised. A missing axis still raises `DimensionError` (PR #550, PR #549)
|
|
27
|
+
* A copy that the wide byte copy cannot take, as reductions, `argmax` and `sort` make of a non-contiguous input, goes a typed element at a time: `argmax` over a column slice runs 1.9 times faster and a copy of a column slice 1.3 times (PR #548)
|
|
28
|
+
* A kernel over a view of 2 to 4 dimensions whose offsets fit an int32 does its address arithmetic in 32 bits: a store with a cast, `abs` or `clip` on such a view runs 1.17 to 1.32 times faster (PR #547)
|
|
29
|
+
* A DFloat `logseq` of base 10 or 2 calls `exp10` or `exp2` rather than `pow`: 200,000 elements take 20 us where they took 84 (PR #546)
|
|
30
|
+
* The rank switch of every generated kernel launcher comes from one helper in the kernel generator, and the generated sources are token for token what they were (PR #545)
|
|
31
|
+
* `logseq` writes a strided, index or transposed view in one launch rather than one per row: a 6250 x 32 DFloat view takes 87 us where it took 14 ms (PR #544)
|
|
32
|
+
* A reduction along a last axis of 16 elements or fewer gives a row that spans more than one 32-byte sector two threads: `Cumo::SFloat.ones(1536, 64)[true, 0...16].sum(axis: 1)` takes 2.18 us where it took 4.20, and `max`, `mean`, `mulsum` and `argmax` over short rows run 1.2 to 1.8 times faster (PR #543)
|
|
33
|
+
* A store between views of the same type moves 16, 8, 4 or 2 bytes a thread over a run contiguous and aligned on both sides, through one set of kernels the copy behind `dup` shares: the store of an im2col unfold runs about 2 times faster (PR #541)
|
|
34
|
+
* `cumsum` and `cumprod` no longer wait for the GPU. A 1-D scan shorter than 8192 elements ran on the host after a device synchronize, and every other shape waited for the stream to drain: `x = a * 1; x.cumsum + 1` takes about 9 us where it took 0.3 to 0.8 ms (PR #540)
|
|
35
|
+
* `rand`, `rand_norm` and `seq` write a view built on an index array directly rather than through a buffer copied out and back, and so do `real=` and `imag=`: `rand` into a `[6250, 1024][true, idx64]` SFloat view takes 12.7 us where it took 80.7. An axis of length one merges with its neighbour, so a `[N, 1]` column runs what a flat array runs. A seed draws the same values as before (PR #539)
|
|
36
|
+
* `ElementwiseKernel` and `ReductionKernel` read a reversed or stepped view, or a frozen array, where it is rather than copying it on every call, and `ReductionKernel` reads a transposed view the same way: a reduction along the first axis of a transposed 4000 by 4000 array no longer pays a copy that took longer than the reduction. An `ElementwiseKernel` input that is transposed is still copied, in tiles, which beats reading it across rows (PR #538, PR #535)
|
|
37
|
+
* `ReductionKernel` over a leading axis gives a block up to 32 outputs that lie end to end, so a warp reads neighbouring outputs: a sum over axis 0 of a [4000, 4000] SFloat takes 0.169 ms where it took 0.413, what Cumo's own `sum` takes (PR #537)
|
|
38
|
+
* `ElementwiseKernel` and `ReductionKernel` walk neighbouring dimensions that every array lays out end to end as one: a `ReductionKernel` sum over everything of a [160, 100, 1000] SFloat takes 0.162 ms where it took 0.193 (PR #536)
|
|
39
|
+
* Fix a stream test that failed about once in a thousand runs, since its fill on a non-blocking stream was not ordered before its launch on the null stream (PR #534)
|
|
40
|
+
* Say in the README where `CUMO_ALLOW_TF32` pays and where it does not: it speeds up matrix products and convolutions, buys nothing where reading the weights takes the time, and its rounding can push a training step past a tolerance single precision meets (PR #533)
|
|
41
|
+
* A number stored with `a[...] = x`, `a[] = x` or `store(x)` goes through `fill`, one kernel launch with nothing reserved, where it filled a 0-dimensional array first. `fill` writes a view built on an index array or a mask directly rather than through a copy of the whole view (PR #532)
|
|
42
|
+
|
|
43
|
+
# 0.10.0 (2026/09/22)
|
|
44
|
+
|
|
45
|
+
Breaking changes:
|
|
46
|
+
|
|
47
|
+
* `CUMO_COMPATIBLE_MODE`, `CUMO_SHOW_WARNING`, `CUMO_SHOW_WARNING_ONCE` and `CUMO_MEMORY_POOL` read `off`, `false` and `no` as a no, in any case, where anything but `OFF`, `0` and `NO` used to turn the flag on without a word. A value they do not recognise keeps the flag's default and warns, and an empty value reads as unset (PR #514)
|
|
48
|
+
* An empty array keeps its shape through everything that goes through ndloop: `Cumo::Int32.new(0, 3).copy` and `(a + 1)` answer `[0, 3]` where they answered `[0, 0]`. `to_a` nests down to the first zero and no further, the way numpy's `tolist` does, so `(3, 0)` is `[[], [], []]` and `(0, 3, 4)` is `[]` (PR #513)
|
|
49
|
+
* `Marshal.dump` of a subclass of `Cumo::RObject` writes its objects, where it wrote the raw addresses the buffer holds. Data the old dump wrote for such a subclass no longer loads, and what it held was addresses from a process that has since exited (PR #509)
|
|
50
|
+
|
|
51
|
+
Fixes:
|
|
52
|
+
|
|
53
|
+
* Fix `all?(axis: [])` answering a zero-dimensional `Cumo::Bit`, which is truthy whatever it holds, for an array it is false for. Whether an axis was named is now read off the reduction rather than off the list (PR #512)
|
|
54
|
+
* Fix `all?`, `any?` and `none?` answering a zero-dimensional `Cumo::Bit` when a default keyword such as `axis: nil` or `keepdims: false` is written out, so `Cumo::Bit[1, 1, 0].all?(axis: nil)` read as true (PR #510)
|
|
55
|
+
* Fix an axis reduction of an empty `Cumo::Bit` answering an empty array: `Cumo::Bit.new(0, 3).all?(axis: 0)` answers `[1, 1, 1]` and `count_true(axis: 0)` answers `[0, 0, 0]` (PR #508)
|
|
56
|
+
* Fix the five `Cumo::Bit` reductions answering false, or an Integer, for an empty array before reading their arguments. `Cumo::Bit[].all?` is true and `any?` false, as Ruby's are, `count_true` answers a `UInt64`, and an axis that is not there raises `DimensionError` (PR #507)
|
|
57
|
+
* Fix `CastError` for a subclass of a dtype, which broke `store`, arithmetic and the reductions, and put an element of one into an RObject array as one object repeated (PR #506)
|
|
58
|
+
* Fix `Device#with` running its block on the device it was already on, whichever device it was given (PR #499)
|
|
59
|
+
* Fix `reshape` writing the new shape over whatever the receiver's `dup` answered, which could leave an array claiming a size its buffer cannot hold. The answer `dup` gives has to be the same class, own its data and not be frozen (PR #494)
|
|
60
|
+
* Fix `gemm` walking a copy of a non-contiguous operand with the original's dimensions when `dup` answered something else (PR #493)
|
|
61
|
+
* Fix `marshal_dump` and `to_binary` handing out memory past the end of the array when a view's `dup` answered another shape, class or layout (PR #492)
|
|
62
|
+
* Fix `median` and `sort` ignoring the axes a view marked with `:sum`, so `f[:sum, true].median` answered the whole array. `median` keeps its receiver rather than standing its copy in its place, and no longer copies an operand it only reads, so the pool holds less afterwards (PR #489, PR #488, PR #487)
|
|
63
|
+
* Fix `Cumo::Bit#copy` answering its own receiver when the receiver is inplace, which let a write to the copy reach the original and gave every reduction that copies an index-backed view first the wrong rows (PR #486)
|
|
64
|
+
* Fix the interpreter abort when `all?` or `any?` reduces a `[:sum, true]` view (PR #485)
|
|
65
|
+
|
|
66
|
+
Changes:
|
|
67
|
+
|
|
68
|
+
* `compile_with_cache` answers the same module for the same source, options and device for the rest of the process, so it can be called wherever the kernel is launched: a repeated call costs 0.0009 ms where it read the cubin back and loaded a new module for 0.033 ms, nine times the launch itself. A module nobody holds any more is unloaded when Ruby collects it, and `Cumo::CUDA::Compiler.clear_modules` makes the next call read the disk cache or compile again (PR #531)
|
|
69
|
+
* The ports of the CuPy examples live in `examples/`, one Ruby file per original, with `backend.rb` running the CPU side on Numo and the GPU side on Cumo under `GPU=1`. The gem leaves them out (PR #530)
|
|
70
|
+
* Drop a dead first definition of `Compiler#save_cache` that `ruby -W` warned about (PR #529)
|
|
71
|
+
* Reading a result back from the device copies the range it needs into pinned host memory rather than reading managed memory in place, which faulted the page over and back once per read where a small block shares its page with others: `Float(r.sum)` takes 0.012 ms where it took 0.885, `to_a` of eight elements 0.016 ms where it took 0.97, and `to_binary` of 8 MB 2.3 ms where it took 7.6. `to_a`, `each`, `each_with_index`, `map_with_index`, `inspect`, `format`, `extract_cpu`, `aref_cpu`, `to_binary` and the RObject marshal all take the copy, and a block that writes the array it is walking still sees what it wrote (PR #528)
|
|
72
|
+
* `rand` and `rand_norm` fill an array whose last axis has length one in one kernel launch rather than one per row: a `[1048576, 1]` column takes 0.018 ms where it took 1799. A seed produces the same values as before whatever the layout (PR #527)
|
|
73
|
+
* A module answers a template kernel such as `kernel<float>` by its name expression: `NVRTCProgram.new` and `compile_with_cache` take `name_expressions:`, the mangled names are cached beside the cubin, and `Module#get_function` looks an expression up before asking the driver. Arrays of structs are arrays whose trailing axes are the fields, and a struct by value is a packed String. The cubin cache had been writing empty files where `/tmp` and the cache directory are different file systems, so every `compile_with_cache` compiled again; it now writes next to its place (PR #526)
|
|
74
|
+
* Add `Cumo::CUDA::PinnedMemory`, a page-locked host buffer read and written as bytes, and `NArray#set` and `NArray#get`, which copy through it asynchronously on the current stream or on `stream:` the way `cupy.ndarray.set` and `get` do. A copy in flight keeps both ends until it is done (PR #525)
|
|
75
|
+
* Add `Cumo::CUDA::Stream` and `Cumo::CUDA::Event`, after `cupy.cuda`: `Stream.new`, `with`, `use`, `synchronize`, `record`, `wait_event`, `Stream.null`, `Stream.current`, `Event.new`, `record`, `synchronize`, `done?` and `Cumo::CUDA.get_elapsed_time`, and `Function#launch` takes `stream:`. `Stream#with` orders the block after what the previous stream had queued and waits for the stream on the way out (PR #524)
|
|
76
|
+
* Every kernel, copy, cuBLAS and cuDNN call, thrust and CUB call goes to the current stream of the thread, which is the null stream until a `Stream` is used, so a stream a caller picks reaches all of it. `Runtime.cudaStreamCreateWithFlags`, `cudaStreamDestroy`, `cudaStreamSynchronize` and `current_stream=` are bound (PR #523)
|
|
77
|
+
* Add `Runtime.cudaDeviceCanAccessPeer` and `Device#can_access_peer?`. A CUDA error raised for a device that is not there no longer stays as the runtime's last error for the next kernel launch to read as its own (PR #521)
|
|
78
|
+
* The CI ccache ceiling is 2 GB, since one build no longer fit in 1 GB and the CUDA 12 jobs compiled again what had been evicted (PR #522)
|
|
79
|
+
* Add `Cumo::CUDA::ReductionKernel`, a reduction written as a map, a reduce and a post expression with an identity, called with `axis:` and `keepdims:`, after CuPy's. This is the last half of #106 (PR #520)
|
|
80
|
+
* Add `Cumo::CUDA::ElementwiseKernel`, a kernel written as one expression over a parameter list, with one-letter types resolved from the arguments and broadcasting, compiled once per set of types, after CuPy's. Bit, half precision and complex arguments are refused for now (PR #519)
|
|
81
|
+
* A kernel compiled with NVRTC can be launched: `Module#get_function` answers a `Cumo::CUDA::Function` whose `launch` takes the arguments, the grid, the block and the dynamic shared memory. An NArray hands over its device pointer, an Integer goes as a `long long`, a Float as a `double`, and a String as its bytes. On CUDA 12.4 or later the count and the sizes of the arguments are checked against the kernel before the launch (PR #518, PR #500)
|
|
82
|
+
* `CUMO_ALLOW_TF32` puts single precision `gemm` on the tensor cores as well as convolutions: a `[4096, 4096]` SFloat gemm goes from 17.8 to 28.4 TFLOP/s, and its answer moves from 4e-07 to 3e-04 of a double precision reference. Off, which is the default, answers bit for bit what it did. The flag is read in `cumo.c` as `Cumo.allow_tf32?` rather than in `cudnn.c`, so a build without cuDNN has it too (PR #517, PR #516)
|
|
83
|
+
* Every dtype's `gemm` goes through `cublasGemmStridedBatchedEx`, the entry point that can name a compute type. The answers are the same bytes, and no change in speed could be told apart from the noise (PR #515)
|
|
84
|
+
* `all?`, `any?` and `none?` on an empty `Cumo::Bit` with no axis named answer the identity without reserving anything or waiting on the device: 0.045 us a call where it took 12.3 and 512 bytes (PR #511)
|
|
85
|
+
* `sort_index`, and `sort` and `median` over a view, address by rank rather than through the run-time accessor: `sort_index` over a 4096 by 1024 SFloat takes 2.2 ms where it took 5.6, and an inplace `sort` over a column slice 2.0 ms where it took 6.5 (PR #505)
|
|
86
|
+
* `cumsum` and `cumprod` along an axis send every row to one scan keyed on its row rather than starting a scan per row, and a view no single stride reaches is gathered by a kernel that addresses by rank: over a `[512, 2048]` SFloat a column slice takes 25.7 us where 0.9.0 took 50.7, and a transposed view 45.2 us where it took 308.3. A reversed view is read through its stride and costs what a flat one does (PR #504, PR #503, PR #498, PR #496)
|
|
87
|
+
* The default cuDNN workspace ceiling is 128 MB rather than 8 MB, which reaches the faster convolution algorithms: the twenty convolutions of a ResNet-18 forward pass at batch 16 take 5.23 ms where they took 9.80, and a half precision convolution reaches its tensor core algorithms. The search reserves the whole ceiling while it runs, so the peak grows with it and goes back to the pool afterwards (PR #502)
|
|
88
|
+
* A single precision convolution stays off the tensor cores unless `CUMO_ALLOW_TF32` says yes, where cuDNN's default let the algorithm search move it there and round the operands to a 10 bit significand (PR #501)
|
|
89
|
+
* Say in the README that `contiguous?` answers true for a view whose length-one axis Numo counts as breaking contiguity, that copying a transposed operand beats handing it to cuBLAS as it is for most shapes, what the memory numbers of the pool and of `nvidia-smi` mean, where a bfloat16 integer index goes past 256, and that `quantize_symmetric` breaks ties away from zero where numpy and torch round to even (PR #497, PR #495, PR #491, PR #484)
|
|
90
|
+
* Say what routing `silu` through `sigmoid` would change, rather than a fraction that no dtype matches (PR #483)
|
|
91
|
+
* Add `Cumo::NMath.sigmoid`, which keeps a value where `1 / (1 + exp(-x))` written out gives out: single reaches zero at -104 rather than -89, double at -745 rather than -710 (PR #482)
|
|
92
|
+
* Add `Cumo::NMath.softplus`, which does not overflow where `log(1 + exp(x))` written out does, and answers `x` to the last bit past 11.09 in half and 88.7 in single (PR #481)
|
|
93
|
+
|
|
1
94
|
# 0.9.0 (2026/09/19)
|
|
2
95
|
|
|
3
96
|
Breaking changes:
|