cumo 0.5.10 → 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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +60 -0
  4. data/README.md +45 -1
  5. data/bench/cumo_probe.rb +713 -270
  6. data/bench/cumo_shape_probe.rb +264 -0
  7. data/ext/cumo/cuda/cudnn_impl.cpp +24 -5
  8. data/ext/cumo/cuda/memory_pool_impl_test.cpp +4 -1
  9. data/ext/cumo/cuda/nvrtc.c +1 -1
  10. data/ext/cumo/depend.erb +7 -0
  11. data/ext/cumo/include/cumo/bit_reduce_kernel.h +532 -0
  12. data/ext/cumo/include/cumo/check.h +51 -0
  13. data/ext/cumo/include/cumo/cuda/cudnn.h +27 -42
  14. data/ext/cumo/include/cumo/indexer.h +125 -10
  15. data/ext/cumo/include/cumo/intern.h +2 -0
  16. data/ext/cumo/include/cumo/narray.h +12 -0
  17. data/ext/cumo/include/cumo/narray_kernel.h +41 -0
  18. data/ext/cumo/include/cumo/ndloop.h +1 -0
  19. data/ext/cumo/include/cumo/reduce_kernel.h +343 -56
  20. data/ext/cumo/include/cumo/template.h +2 -6
  21. data/ext/cumo/include/cumo/types/bit_kernel.h +89 -23
  22. data/ext/cumo/include/cumo/types/float_macro.h +15 -2
  23. data/ext/cumo/include/cumo/types/float_macro_kernel.h +15 -2
  24. data/ext/cumo/include/cumo/types/int_macro.h +20 -0
  25. data/ext/cumo/include/cumo/types/int_macro_kernel.h +20 -0
  26. data/ext/cumo/include/cumo/types/robj_macro.h +8 -3
  27. data/ext/cumo/include/cumo.h +2 -2
  28. data/ext/cumo/narray/data.c +44 -66
  29. data/ext/cumo/narray/data_kernel.cu +188 -0
  30. data/ext/cumo/narray/gen/narray_def.rb +5 -0
  31. data/ext/cumo/narray/gen/spec.rb +10 -0
  32. data/ext/cumo/narray/gen/tmpl/accum_binary.c +33 -17
  33. data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +30 -81
  34. data/ext/cumo/narray/gen/tmpl/batch_norm.c +25 -22
  35. data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +20 -17
  36. data/ext/cumo/narray/gen/tmpl/binary.c +3 -3
  37. data/ext/cumo/narray/gen/tmpl/binary2.c +1 -1
  38. data/ext/cumo/narray/gen/tmpl/binary_s.c +3 -3
  39. data/ext/cumo/narray/gen/tmpl/clip.c +6 -6
  40. data/ext/cumo/narray/gen/tmpl/cond_binary.c +3 -3
  41. data/ext/cumo/narray/gen/tmpl/cond_binary_kernel.cu +28 -15
  42. data/ext/cumo/narray/gen/tmpl/cond_unary.c +1 -1
  43. data/ext/cumo/narray/gen/tmpl/cond_unary_kernel.cu +23 -10
  44. data/ext/cumo/narray/gen/tmpl/conv.c +8 -8
  45. data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +10 -10
  46. data/ext/cumo/narray/gen/tmpl/conv_transpose.c +10 -10
  47. data/ext/cumo/narray/gen/tmpl/ewcomp.c +1 -1
  48. data/ext/cumo/narray/gen/tmpl/fill.c +1 -1
  49. data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +16 -13
  50. data/ext/cumo/narray/gen/tmpl/frexp.c +1 -1
  51. data/ext/cumo/narray/gen/tmpl/poly.c +1 -1
  52. data/ext/cumo/narray/gen/tmpl/pooling_backward.c +4 -4
  53. data/ext/cumo/narray/gen/tmpl/pooling_forward.c +4 -4
  54. data/ext/cumo/narray/gen/tmpl/pow.c +6 -6
  55. data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +73 -0
  56. data/ext/cumo/narray/gen/tmpl/set2.c +1 -1
  57. data/ext/cumo/narray/gen/tmpl/store_bit.c +1 -1
  58. data/ext/cumo/narray/gen/tmpl/store_from.c +8 -13
  59. data/ext/cumo/narray/gen/tmpl/store_from_kernel.cu +16 -11
  60. data/ext/cumo/narray/gen/tmpl/unary.c +1 -1
  61. data/ext/cumo/narray/gen/tmpl/unary2.c +1 -1
  62. data/ext/cumo/narray/gen/tmpl/unary_ret2.c +1 -1
  63. data/ext/cumo/narray/gen/tmpl/unary_s.c +1 -1
  64. data/ext/cumo/narray/gen/tmpl_bit/binary.c +1 -1
  65. data/ext/cumo/narray/gen/tmpl_bit/binary_kernel.cu +52 -12
  66. data/ext/cumo/narray/gen/tmpl_bit/bit_count.c +14 -31
  67. data/ext/cumo/narray/gen/tmpl_bit/bit_count_kernel.cu +3 -78
  68. data/ext/cumo/narray/gen/tmpl_bit/bit_reduce.c +18 -108
  69. data/ext/cumo/narray/gen/tmpl_bit/bit_reduce_kernel.cu +4 -75
  70. data/ext/cumo/narray/gen/tmpl_bit/bit_stat.c +36 -0
  71. data/ext/cumo/narray/gen/tmpl_bit/bit_stat_kernel.cu +4 -0
  72. data/ext/cumo/narray/gen/tmpl_bit/extract.c +13 -27
  73. data/ext/cumo/narray/gen/tmpl_bit/fill.c +1 -1
  74. data/ext/cumo/narray/gen/tmpl_bit/mask.c +1 -0
  75. data/ext/cumo/narray/gen/tmpl_bit/mask_kernel.cu +14 -16
  76. data/ext/cumo/narray/gen/tmpl_bit/store_bit.c +1 -1
  77. data/ext/cumo/narray/gen/tmpl_bit/store_bit_kernel.cu +47 -10
  78. data/ext/cumo/narray/gen/tmpl_bit/store_from.c +1 -1
  79. data/ext/cumo/narray/gen/tmpl_bit/store_from_kernel.cu +23 -10
  80. data/ext/cumo/narray/gen/tmpl_bit/unary.c +1 -1
  81. data/ext/cumo/narray/gen/tmpl_bit/unary_kernel.cu +49 -11
  82. data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +18 -16
  83. data/ext/cumo/narray/index.c +84 -34
  84. data/ext/cumo/narray/narray.c +156 -18
  85. data/ext/cumo/narray/ndloop.c +77 -5
  86. data/ext/cumo/narray/ndloop_kernel.cu +32 -0
  87. data/lib/cumo/narray/extra.rb +119 -24
  88. data/test/bit_test.rb +600 -0
  89. data/test/cudnn_test.rb +152 -0
  90. data/test/cumo_test.rb +1 -0
  91. data/test/extra_test.rb +74 -6
  92. data/test/narray_test.rb +1048 -3
  93. metadata +6 -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: 0d6b3e0decf69d07199a7930dc6a23d1a05a90ed044cf007203c78c28a8e9ea1
4
+ data.tar.gz: dec0ca1f195b302919d30085fcc139911299ba075193b67cab9965907210092f
5
5
  SHA512:
6
- metadata.gz: ee03167a0966008c99598d90623757772060c09bad859af35d313dda831975461cba49c8da251a4793e6a9b933cd5ec75588038381d55e492398d0b4e2686559
7
- data.tar.gz: 2e2b3e1a6f0f304be8b2375be48061ba73926973d4ec966e49d0b50f666d88d7078cec3c59ea797401bd72b91ec96a4f115bf2ed49485dc1d7ca3b6a29fb1c48
6
+ metadata.gz: e4f8d6204fca59288efad1ab5dec228d474de02ee3465f89afe7fc7440b456b0d46b78c92fb1e48e92063a2b3c385dedb8955aed3ddfa4411c13beb2f32a5417
7
+ data.tar.gz: a397b427534b829b13f8bd4a4d6f8616d918bde834dc5c69a5611671ec942796d8615bd74d2564a2c3cd6cdaf9e572f4599e0704071f21a3c40394d0236b148d
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,63 @@
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
+
34
+ # 0.5.11 (2026/08/29)
35
+
36
+ Breaking changes:
37
+
38
+ * `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)
39
+
40
+ Fixes:
41
+
42
+ * 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)
43
+ * 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)
44
+
45
+ Changes:
46
+
47
+ * 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)
48
+ * 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)
49
+ * 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)
50
+ * 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)
51
+ * 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)
52
+ * 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)
53
+ * 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)
54
+ * 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)
55
+ * 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)
56
+ * 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)
57
+ * 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)
58
+ * Drop the contiguity assert the cuDNN descriptors do not need (PR #308)
59
+ * 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)
60
+
1
61
  # 0.5.10 (2026/08/23)
2
62
 
3
63
  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