cumo 0.5.10 → 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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +27 -0
  4. data/README.md +45 -1
  5. data/bench/cumo_probe.rb +713 -270
  6. data/ext/cumo/cuda/cudnn_impl.cpp +15 -4
  7. data/ext/cumo/include/cumo/bit_reduce_kernel.h +426 -0
  8. data/ext/cumo/include/cumo/indexer.h +96 -2
  9. data/ext/cumo/include/cumo/intern.h +2 -0
  10. data/ext/cumo/include/cumo/narray.h +2 -0
  11. data/ext/cumo/include/cumo/narray_kernel.h +41 -0
  12. data/ext/cumo/include/cumo/reduce_kernel.h +227 -23
  13. data/ext/cumo/include/cumo/template.h +2 -6
  14. data/ext/cumo/include/cumo/types/bit_kernel.h +89 -23
  15. data/ext/cumo/include/cumo.h +2 -2
  16. data/ext/cumo/narray/data.c +23 -41
  17. data/ext/cumo/narray/data_kernel.cu +75 -0
  18. data/ext/cumo/narray/gen/tmpl/accum_binary.c +33 -17
  19. data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +30 -81
  20. data/ext/cumo/narray/gen/tmpl/cond_binary_kernel.cu +28 -15
  21. data/ext/cumo/narray/gen/tmpl/cond_unary_kernel.cu +23 -10
  22. data/ext/cumo/narray/gen/tmpl/store_from.c +8 -13
  23. data/ext/cumo/narray/gen/tmpl/store_from_kernel.cu +16 -11
  24. data/ext/cumo/narray/gen/tmpl_bit/binary_kernel.cu +52 -12
  25. data/ext/cumo/narray/gen/tmpl_bit/bit_count.c +14 -31
  26. data/ext/cumo/narray/gen/tmpl_bit/bit_count_kernel.cu +3 -78
  27. data/ext/cumo/narray/gen/tmpl_bit/bit_reduce.c +14 -107
  28. data/ext/cumo/narray/gen/tmpl_bit/bit_reduce_kernel.cu +4 -75
  29. data/ext/cumo/narray/gen/tmpl_bit/mask.c +1 -0
  30. data/ext/cumo/narray/gen/tmpl_bit/mask_kernel.cu +14 -16
  31. data/ext/cumo/narray/gen/tmpl_bit/store_bit_kernel.cu +47 -10
  32. data/ext/cumo/narray/gen/tmpl_bit/store_from_kernel.cu +23 -10
  33. data/ext/cumo/narray/gen/tmpl_bit/unary_kernel.cu +49 -11
  34. data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +18 -16
  35. data/ext/cumo/narray/index.c +64 -26
  36. data/ext/cumo/narray/narray.c +1 -0
  37. data/ext/cumo/narray/ndloop.c +10 -5
  38. data/ext/cumo/narray/ndloop_kernel.cu +32 -0
  39. data/test/bit_test.rb +457 -0
  40. data/test/cudnn_test.rb +64 -0
  41. data/test/narray_test.rb +197 -0
  42. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcb51638cb488e5abcb0bf885744ea1478f4daefd4945f6af8becf252bc85f29
4
- data.tar.gz: 75aed5ea382f37a823992e29ef2f3fe9ee31a7b7c11350d2541e5f4fc0c0fb59
3
+ metadata.gz: b345c4e036212d334600041165fe79e533ef0e1dd450e22b1288f5e6d6214ffa
4
+ data.tar.gz: af60f78cdd2f39a96e8a183eff02a26c26d8977560ba148643c55df5961c2bf2
5
5
  SHA512:
6
- metadata.gz: ee03167a0966008c99598d90623757772060c09bad859af35d313dda831975461cba49c8da251a4793e6a9b933cd5ec75588038381d55e492398d0b4e2686559
7
- data.tar.gz: 2e2b3e1a6f0f304be8b2375be48061ba73926973d4ec966e49d0b50f666d88d7078cec3c59ea797401bd72b91ec96a4f115bf2ed49485dc1d7ca3b6a29fb1c48
6
+ metadata.gz: 361042e17c2f498b8e4f5ddc76bbd119688a6a3a3ac176307973fd51c75b332d4d831a82c0fc0e8a3b28be98bfbe50d507ec67d517c609c2342caf57d94d9c41
7
+ data.tar.gz: 53da42c447fe31812654e1ab0566f5e553f781daaf75b97bb1c36630b8dc3ed195704002990bc4a2a9f4fdd6e26e92c2d5905ff64da0e0f058b01dae20ae3dec
data/.gitignore CHANGED
@@ -29,3 +29,4 @@ t.rb
29
29
  .DS_Store
30
30
  /vendor/
31
31
  .kernel_cache/
32
+ bench/cumo_probe_baseline.json
data/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
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
+
1
28
  # 0.5.10 (2026/08/23)
2
29
 
3
30
  Breaking changes:
data/README.md CHANGED
@@ -100,7 +100,7 @@ a = xm::DFloat.new(3,5).seq
100
100
 
101
101
  Numo returns a Ruby numeric object wherever a result is 0-dimensional, while Cumo returns the 0-dimensional NArray itself.
102
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) for what it buys.
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.
104
104
 
105
105
  The methods affected are:
106
106
 
@@ -178,6 +178,50 @@ x += p_dir * alpha # and consumed there, without crossing the bus
178
178
 
179
179
  Read the value back once the loop is done, or every k iterations if it has to test something.
180
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
+
181
225
  ### Select a GPU device ID
182
226
 
183
227
  Set the `CUDA_VISIBLE_DEVICES=id` environment variable, or