carray-jit 0.1.3 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb7a2a48026bc5aca8e60d203a31267400d230db19f2ddbe1b697eda88e21119
4
- data.tar.gz: 3483a507da6a6d338d113c2555efe60311695f1ae29f03e2aa61c1a0133cc7e1
3
+ metadata.gz: a4d8ccd59d268ba31816c5339867d7231b7051f7879d67faecdc4eb4d924b375
4
+ data.tar.gz: d73b3600e59633ac69986019597626b42a27e5e18b0143e00d39406d3f1c562f
5
5
  SHA512:
6
- metadata.gz: 0c1f5c3189e49927806a67de7253646fa68d699e72fdaf4ffdd4421435bd9522697f2ae818cf5d551b3bcd9e754d729baa60a18a0a3300590824948f0fd89877
7
- data.tar.gz: 731031f35f01711cf877d3d0f50444499cdf39e97ddade4b4db7d2465bdefc49fe7fc7c4a0db0f36b3eb281ab7a3053413dc77183172726eea5bcab08a44070f
6
+ metadata.gz: 96e9c9585b2e93ed1a2f655ee92e3ef446a11d35f95895acf08dda59ed841c7570e5bc0361995c303eda8aa81b3461068fb93f0495814b6a973309d8f0f18bb0
7
+ data.tar.gz: 4b6fd2250447527deca38a65b19555833cd5702e2f27a253aa9bcba7359e3862e3f3b95f921004911de2ff502422c522a37d3507efb5b69449d94a3b40f4ecad
data/CHANGELOG.md CHANGED
@@ -37,6 +37,171 @@ version you have and a newer one.
37
37
  a release needs is said in the gemspec, and an entry says so only
38
38
  when the answer changes. -->
39
39
 
40
+ ## 0.1.5
41
+
42
+ - Change: needs CArray 3.0.4 or later in the 3.0 series; install it
43
+ alongside this release. 0.1.4 accepts CArray 3.0.4 but does not run
44
+ under it, so the two are upgraded together. A store in a block into a
45
+ cell that cannot hold the value still wraps as C does, where CArray
46
+ 3.0.4's own `int8[0] = 300` raises RangeError.
47
+
48
+ - Change: `jit_each` and `jit_map` called again with operands of the same
49
+ shapes, types and masking cost less per call -- about 9 us rather than 11
50
+ for a small two-array block -- which a time-stepping loop calling one
51
+ block thousands of times will notice. Nothing changes in what they
52
+ compute; a scalar's value may differ from call to call as before.
53
+
54
+ - Fix: an element-wise `jit_each` or `jit_map` run through CArray's sweep
55
+ could read its scalars and C functions from where they had been before
56
+ GC compaction moved them, and answer wrongly or crash. Only a process
57
+ that compacts (`GC.compact`, `GC.auto_compact = true`) was exposed.
58
+
59
+ ## 0.1.4
60
+
61
+ - New: a lazy `then_else` (a lazy condition or branch) is compiled with
62
+ the expression around it, masked cells included. With 0.1.3, a masked
63
+ one made this gem raise, and CArray stopped asking it for the rest of
64
+ the process.
65
+
66
+ - New: `CArray.fuse` expressions holding comparisons or `shift` -- a
67
+ stencil, or a rule like the Game of Life's -- are compiled whole.
68
+
69
+ - New: `stand_in:` on `CArray::JIT.watching` and `CFunction#watching` is the
70
+ number a failed body answers its C caller with, for a caller that reads
71
+ the return value as a status rather than as a value -- GSL's callbacks
72
+ return `int`, where 0 is `GSL_SUCCESS`, so a failed body handed the
73
+ default is read as a step that went well. It is cast to whatever the body
74
+ returns, reaches the call that failed and every call turned away after it,
75
+ and is put back when the window closes. The default is 0, which is what a
76
+ failed call has always answered.
77
+
78
+ - New: `CArray::JIT.watching(*functions, on_error: [hook, data]) { ... }`
79
+ opens one window over several compiled bodies -- an objective and its
80
+ gradient, a function and its jacobian. Every flag goes down before the
81
+ block and every one is read after it, and the failure raised is the first
82
+ in the argument list. `on_error:` sets that pair on each of them for the
83
+ window's length and puts back whatever hook was there before.
84
+
85
+ - New: `CFunction#on_error(hook, data)` names a C function,
86
+ `void (*)(void *)`, that a compiled body calls with `data` when it fails,
87
+ once, from the call that failed -- so a library holding the address can
88
+ be told to stop, as `nlopt_force_stop` tells NLopt. `#call` from Ruby
89
+ does not call it, and `on_error(nil)` takes it away. A body that cannot
90
+ fail accepts one and never calls it.
91
+
92
+ - New: a `jit_function` body may ask whether a pointer parameter was handed
93
+ an address -- `if grad`, `grad.nil?`, `grad == nil`, `grad != nil` -- and
94
+ `f.call` passes `nil` there as `NULL`. This is what a callback needs where
95
+ the caller leaves a pointer out per call, as NLopt does with the gradient
96
+ under a derivative-free method. A bare pointer name outside a condition is
97
+ still refused, and a `void *` may be asked but not reached through.
98
+
99
+ - Change: needs CArray 3.0.3 or later in the 3.0 series. Lazy `then_else`,
100
+ integer powers, and comparisons and `shift` in `CArray.fuse` are compiled
101
+ from that version; install it alongside this release.
102
+
103
+ - Change: in a block, an integer literal above `2**63 - 1` is refused where
104
+ it would be computed as an int64 (it wrapped to a negative number).
105
+ Meeting a uint64 or a Float, or stored into a uint64 or float array, it
106
+ keeps its value as before.
107
+
108
+ - Change: a negative Integer meeting a uint64 in a block is refused, as
109
+ CArray refuses the same operand: a literal when the block is compiled, a
110
+ captured value with `RangeError` when it is called. `w[i] > -1` used to
111
+ be false for every cell.
112
+
113
+ - Change: in `jit_for`, an inner loop whose range is read from a local or
114
+ a cell (`(0...bound[i]).each`) runs, where it was refused. A subscript
115
+ that loop's index walks is checked at each access, as `a[b[i]]` is, and
116
+ raises `IndexError` past the end; a range written in literals and
117
+ captured integers is still checked before the kernel runs.
118
+
119
+ - Change: in a block, `round`, `ceil`, `truncate` and `to_i` stored into
120
+ a float cell give `0.0` for a value that rounds to zero, never `-0.0`,
121
+ as Ruby's Integer has no sign of zero.
122
+
123
+ - Change: in a block, a zero float remainder keeps the dividend's sign, as
124
+ Ruby's `Float#%` does: `-4.0 % 2.0` is `-0.0`. CArray's own `%` still
125
+ answers `0.0`.
126
+
127
+ - Change: in a block, a shift means what it means to `Integer#<<` and
128
+ `#>>`: a negative count shifts the other way, and a count of 64 or more
129
+ shifts every bit out. It was C's shift, with no defined answer there.
130
+
131
+ - Change: a body lent out answers its C caller the same way whichever way it
132
+ failed. A `raise` answered 0, while a division that had no divisor ran on
133
+ to the end and answered whatever the body made of the zero the helper
134
+ handed back -- `100 + a / 0` answered 100. Both now answer the window's
135
+ `stand_in:`, which is 0 where none was given. A caller reading the flag
136
+ rather than the return value sees no change.
137
+
138
+ - Fix: a plan holding a mask rule this gem does not know is declined and
139
+ walked by CArray, rather than raising.
140
+
141
+ - Fix: a lazy expression or `CArray.fuse` over an operand that is not
142
+ laid out end to end -- a slice, a stepped slice, a transpose, a gather,
143
+ or an operand broadcast against the others (`col.lazy * y.lazy` with
144
+ `col` of shape (n, 1)) -- is compiled; it was declined and walked by
145
+ CArray. Storing one into such a view is compiled too. A destination
146
+ that writes one cell more than once is still left to CArray.
147
+
148
+ - Fix: an integer power in a lazy expression -- `x.lazy ** 2` over a
149
+ Float array, or any power of an integer array -- is compiled; it was
150
+ declined and left to CArray every time, since the C it calls was not in
151
+ the generated source.
152
+
153
+ - Fix: in a block, a negative real raised to a power that is not an
154
+ integer, stored into a complex cell or joined with a Complex
155
+ (`z[i] = x ** y`, `x ** y + 1i`), is now the Complex Ruby gives,
156
+ to the last bit, where it was `NaN`. Read as a real -- a float cell,
157
+ or a local assigned the power -- it stays `NaN`.
158
+
159
+ - Fix: a block loop whose range ends at the top of int64 runs as Ruby's
160
+ does: `(MAX-2..MAX).each` ran no passes.
161
+
162
+ - Fix: a literal past a double, `1e400`, is an infinity as in Ruby, and an
163
+ Integer literal past uint64 is refused; both were a C compiler error.
164
+
165
+ - Fix: signed overflow wraps consistently within a kernel. Compiled
166
+ without `-fwrapv`, a kernel could contradict itself: `y = -a; y > 0 ?
167
+ y : 0` stored a negative value for `a = INT64_MIN`. `INT64_MIN / -1` in
168
+ a block is `INT64_MIN` rather than a trap on x86.
169
+
170
+ - Fix: the `carray-jit` command runs when the carray gem is not installed.
171
+ `status`, `list`, `show` and `clear` used to stop with
172
+ `Gem::MissingSpecError`; they now work on the cache directory named
173
+ `carraynone`. Nothing changes where carray is installed as a gem.
174
+
175
+ - Fix: a compiled `jit_function` exports its own declared symbol and nothing
176
+ under a fixed name, so several of them can be linked into one library.
177
+ Two bodies that could fail -- either raising, or dividing an integer by
178
+ something that might be zero -- used to define `carray_jit_error` and its
179
+ three neighbours twice and the link failed with duplicate symbols, which
180
+ is what carray-jit-aot hit when a library held two such functions. Those
181
+ four are static now, and the one function that answers where they stand
182
+ carries the body's digest.
183
+
184
+ - Fix: the flag a lent body reports through, and the hook `CFunction#on_error`
185
+ sets, belong to the thread rather than to the compiled object, so two
186
+ threads lending one body no longer read each other's failures -- which
187
+ two separately written blocks with the same text do without meaning to.
188
+ `#watching`, `#on_error`, `#clear_error` and `#report_error` are unchanged;
189
+ what moves is where a hook is set, which is now the thread that will lend
190
+ the address. A library that calls the body from threads of its own is
191
+ outside the window.
192
+
193
+ - Fix: a `jit_function` body that calls a function borrowed with
194
+ `jit_extern` from a library other than libm now links on macOS, where it
195
+ failed with "Undefined symbols". Nothing changes elsewhere: Linux linked
196
+ it already.
197
+
198
+ - Fix: a prototype given to `jit_function` or `jit_extern` reads C's integer
199
+ types in any word order and without `int`, as C does -- `unsigned n`,
200
+ `long unsigned int`, `short unsigned` -- so a header's declaration can be
201
+ copied as it stands. `int unsigned`, which was taken as a signed `int`,
202
+ is now unsigned. A combination C does not have, such as `short long`, is
203
+ refused.
204
+
40
205
  ## 0.1.3
41
206
 
42
207
  - New: `CArray::JIT.call_provider` is asked at a `jit_call` site before
data/README.md CHANGED
@@ -8,7 +8,7 @@ The block is read with Prism, translated to C if it falls inside that subset, co
8
8
 
9
9
  ## Status
10
10
 
11
- 0.1.3 is the current release, and it still moves: behaviour can change between releases — see [CHANGELOG.md](CHANGELOG.md). A companion gem to CArray, it follows CArray's surface, which is not settled until CArray 3.1.
11
+ 0.1.5 is the current release, and it still moves: behaviour can change between releases.
12
12
 
13
13
  ## Features
14
14
 
@@ -35,7 +35,7 @@ gem "carray-jit"
35
35
  Requires:
36
36
 
37
37
  - Ruby >= 3.2
38
- - CArray >= 3.0.2, < 3.1
38
+ - CArray >= 3.0.4, < 3.1
39
39
  - A C compiler
40
40
  - Prism and Fiddle (both ship with Ruby; Fiddle is a bundled gem)
41
41
 
@@ -65,14 +65,15 @@ The `jit_` methods are this gem's, and exist once `require "carray/jit"` has run
65
65
 
66
66
  ## Documentation
67
67
 
68
- * [Introduction](docs/00_Introduction.md) — what carray-jit is: the gap it fills beside CArray, the subset a block is written in, and where a kernel gets its data
69
- * [Getting started](docs/01_GettingStarted.md) — the block, its extents, what the three methods return, and where a kernel stands beside `a + b * c` and `CArray.fuse`
70
- * [The shapes a kernel takes](docs/02_KernelShapes.md) — work that reaches no neighbour, extents and subscripts, stencils, reductions, and contraction over a repeated index
71
- * [Supported features](docs/03_SupportedFeatures.md) — locals and types, branches, raising, the types that are not just a number, calling C, and the recognized subset with what it refuses
72
- * [Compiling, caching and inspecting](docs/04_Compiling.md) — what the first call costs, where kernels are kept, reading the generated C, the `carray-jit` command, and what the suite checks
73
- * [Design notes](docs/05_DesignNotes.md) — decisions that were not obvious, and why
74
- * [Cheatsheet](docs/06_Cheatsheet.md) — the eight `jit_` methods and `CArray.fuse` on one page, to look up rather than to read
75
- * [31 exercises, with solutions](docs/07_StepByStep.md) — thirty-one small tasks in the order this guide introduces things, each with its solution and what it answers ([日本語](docs/07_StepByStep.ja.md))
68
+ * [Introduction](docs/00_Introduction.md) — what carray-jit is and what it is for
69
+ * [Getting started](docs/01_GettingStarted.md) — a first kernel, step by step
70
+ * [The shapes a kernel takes](docs/02_KernelShapes.md) — loops, stencils, reductions and contractions
71
+ * [Supported features](docs/03_SupportedFeatures.md) — what a block may contain
72
+ * [Compiling, caching and inspecting](docs/04_Compiling.md) — compilation, the kernel cache and the generated C
73
+ * [Design notes](docs/05_DesignNotes.md) — why things are the way they are
74
+ * [Cheatsheet](docs/06_Cheatsheet.md) — every method on one page
75
+ * [Exercises](docs/07_StepByStep.md) — 31 small tasks, each with its solution
76
+ * [Language reference](docs/08_LanguageReference.md) — the block language, construct by construct
76
77
 
77
78
  ## Contributing
78
79
 
data/carray-jit.gemspec CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.executables = ["carray-jit"]
34
34
 
35
35
  # A kernel reaches CArray's C by address, so the ceiling is the next minor.
36
- spec.add_dependency "carray", ">= 3.0.2", "< 3.1"
36
+ spec.add_dependency "carray", ">= 3.0.4", "< 3.1"
37
37
  # fiddle ships as a bundled gem; depend on it explicitly so Ruby 3.5+ resolves it.
38
38
  spec.add_dependency "fiddle"
39
39
  end
@@ -35,7 +35,8 @@ Installing this gem also puts the compiler behind `CArray.fuse`. An array expres
35
35
  * [Supported features](03_SupportedFeatures.md) — locals and types, branches, raising, the types that are not just a number, calling C, and the recognized subset with what it refuses
36
36
  * [Compiling, caching and inspecting](04_Compiling.md) — what the first call costs, where kernels are kept, reading the generated C, the `carray-jit` command, a call site answered from somewhere else, and what the suite checks
37
37
  * [Design notes](05_DesignNotes.md) — decisions that were not obvious, and why
38
- * [Cheatsheet](06_Cheatsheet.md) — the eight `jit_` methods and `CArray.fuse` on one page, to look up rather than to read
38
+ * [Cheatsheet](06_Cheatsheet.md) — the nine `jit_` methods and `CArray.fuse` on one page, to look up rather than to read
39
39
  * [31 exercises, with solutions](07_StepByStep.md) — thirty-one small tasks in the order this guide introduces things, each with its solution and what it answers ([日本語](07_StepByStep.ja.md))
40
+ * [Language reference](08_LanguageReference.md) — what a block may contain, what each construct means, and what is refused, stated in full to look up
40
41
 
41
42
  [examples/features/](../examples/features) is a tour of the same ground, one file per feature, and [examples/applications/](../examples/applications) holds small programs that use it to do something.
@@ -275,7 +275,7 @@ CArray.jit_for(rows, columns) { |i, j|
275
275
  }
276
276
  ```
277
277
 
278
- `each` rather than `for`, because `for` does not open a scope: it would assign an enclosing variable of the same name and leave the index bound afterwards, neither of which the generated loop does. The range must be a literal `Range`; `Enumerator::ArithmeticSequence` -- `(0...n).step(2)` -- is not handled yet.
278
+ `each` rather than `for`, because `for` does not open a scope: it would assign an enclosing variable of the same name and leave the index bound afterwards, neither of which the generated loop does. The range is a literal `Range`; to step by more than one, write `(0...n).step(2) { |t| ... }`.
279
279
 
280
280
  Inner loops nest, and an index binds to an axis by name rather than by position. One index may walk two axes of the same array (a trace), two indices may walk one axis of it (`c[p,a] * c[p,b]`, which is a covariance), the outer indices need not address axes in their own order (a transposing read), and a full contraction is the same thing written into a one-cell box.
281
281
 
@@ -130,7 +130,7 @@ The aliases are CArray's too, and two of them are worth reading twice: **`CArray
130
130
 
131
131
  `CArray.empty(data_type, dim)` is carray 3.0.2's spelling. A block is read rather than run, so a kernel takes it whatever version of CArray is loaded -- but calling it outside a kernel needs 3.0.2.
132
132
 
133
- The shape is **written out**: an integer, or integers joined by `+`, `-` and `*`, so `CArray.double(2 * 4 + 1)` is a nine-cell array. A length only the call knows is refused -- there would be no C array to declare and no bound to check against -- and so is a length of zero or less. One axis in this release; `CArray.double(3, 4)` says which release takes more.
133
+ The shape is **written out**: an integer, or integers joined by `+`, `-` and `*`, so `CArray.double(2 * 4 + 1)` is a nine-cell array. A length worked out from the integers the block captured is computed when the kernel starts, and that array is allocated on the heap (see below). A length from an index, a local or a cell is refused -- there would be no C array to declare and no bound to check against -- and so is a length of zero or less.
134
134
 
135
135
  **More than one axis** is written the way CArray writes it, and each axis is checked against its own extent:
136
136
 
@@ -472,7 +472,7 @@ Two things are refused rather than compiled:
472
472
 
473
473
  ```ruby
474
474
  result[i] = flags[i] + 1
475
- #=> cannot combine types boolean and double
475
+ #=> cannot combine types boolean and int64
476
476
 
477
477
  result[i] = flags[i] == 1 ? 9.0 : 0.0
478
478
  #=> a boolean cell compares with `true` and `false`, not with a number: in
@@ -844,13 +844,63 @@ That is the arrangement a kernel already keeps with its own slot: cleared once b
844
844
 
845
845
  Once the flag stands, the body does no more work. It is asked again -- the library has no idea anything went wrong, and nothing has told it to stop -- and it returns the stand-in without running. **The stand-in is not the answer. The flag says so.** A body that returns nothing has no stand-in to offer and leaves its out-parameters alone, which is the same refusal.
846
846
 
847
+ The stand-in is zero unless a window says otherwise, and one number answers for every way the body can fail -- a `raise` that leaves early, a division that had no divisor and ran on to the end. The entry point decides it from the flag rather than each of those deciding for itself, so a caller reading the return value gets one answer for one failure.
848
+
847
849
  This matters more than it sounds. Without it a body would report its failure once and then answer normally, and an adaptive routine would do what adaptive routines do: see one bad point, subdivide around it, find every subdivision well behaved, and converge. The number that comes back is not wrong in a way anybody can see. It is worse than a wrong answer, because it looks like a right one.
848
850
 
849
851
  A failure inside the window outranks whatever the library made of it. The body returned a stand-in, so the library is usually the first to complain -- that the endpoints do not straddle, that the iteration did not converge -- and those complaints are consequences rather than what happened, so `#watching` reads the flag before letting the exception through. Where nothing stands, the library's own story is the story.
850
852
 
851
853
  A window may be opened inside a window, and `#call` may be made inside one: both borrow the flag and put it back as they found it, so an inner window answers for its own block and no other, and a call answers for itself without disarming whoever is watching. A kernel run inside a window is untouched either way -- a kernel is handed its own slot, and never reaches this flag at all.
852
854
 
853
- One flag per compiled object, so two functions never share a window. Threads do share one, though: the flag is a single `int32_t` beside the body, so lending the same function to two threads at once is not a window that can be made to mean anything. That is C's bargain again, taken along with `void *params`.
855
+ One flag per compiled object and one per thread, so two functions never share a window and neither do two threads. The second half matters more than it sounds: the object is shared whether or not anybody meant to share it, because two blocks with the same text compile to one function. So a program that never mentions threads can open two windows on one body, and one flag between them is a window neither of them can read -- the second to open puts down what the first had already recorded. The flag and the hook are `_Thread_local`, which costs about half a nanosecond a call, and only on a body that has a flag at all.
856
+
857
+ The thread is the native one, which for Ruby is the thread that opened the window -- except under the M:N scheduler (a non-main Ractor's threads, or `RUBY_MN_THREADS=1`), where a Ruby thread is not tied to one native thread and the window cannot be relied on to be the same copy throughout.
858
+
859
+ What this leaves outside the window is a library that calls the body from threads of its own. The flag such a call raises belongs to the thread that raised it: the body stops answering there, as it should, and the window watching from Ruby sees nothing and raises nothing. A library that calls back on the thread it was called from -- which is what GSL and NLopt do -- is inside the window.
860
+
861
+ #### More than one address at a time
862
+
863
+ A call is often handed several: an objective and its gradient, a function and its jacobian. Each has a flag of its own, and a flag nobody reads is a failure that turns into a wrong answer and then surfaces on an unrelated later window. So all of them go down before the call and all of them are read after it:
864
+
865
+ ```ruby
866
+ CArray::JIT.watching(objective, gradient) do
867
+ NLopt.optimize(option, x, minimum)
868
+ end
869
+ ```
870
+
871
+ The failure raised is the first one in the argument list, and it outranks what the block raised, for the reason a single window's does. `nil` in the list is skipped, so an optional gradient may be passed as it stands, and a function named twice is watched once. A body that cannot fail has no flag and changes nothing, which is what lets a binding watch what it was given without first asking which kind of body it is.
872
+
873
+ `on_error:` takes the `[hook, data]` pair `#on_error` takes and sets it on every function for the length of the window, putting back whatever hook was there when the window closes. That is what the method is for beyond the several flags: the pair can be read back here and cannot be read back from Ruby, so setting `#on_error` around a block and clearing it afterwards leaves the owner's hook gone and says nothing about it.
874
+
875
+ #### When the return value is the answer
876
+
877
+ Zero is a courtesy to a caller that reads the flag and nothing else, and for a body returning `double` that is what it is. For one returning `int` it is a status. GSL's callbacks return `int`, and zero there is `GSL_SUCCESS` -- so a failed body handed back the default tells GSL that the step went well, and GSL goes on: measured on `gsl_odeiv2`, a callback failing at t = 0.1 was called 23,356 more times and the driver returned success on a solution nobody computed.
878
+
879
+ So the number is the window's to choose:
880
+
881
+ ```ruby
882
+ CArray::JIT.watching(rhs, stand_in: 9) do # 9 is GSL_EBADFUNC
883
+ Odeiv2.driver_apply(driver, t, t1, y)
884
+ end
885
+ ```
886
+
887
+ It is set for the length of the window and put back afterwards, like `on_error:`, and `CFunction#watching` takes it for a window over one function. It is cast to whatever the body returns, so a `double` body may take one too, and it reaches both the call that failed and every call turned away after it -- which is what a library stopping on a status has to be told on each of them.
888
+
889
+ It belongs to the window rather than to the body because the right number is not a property of the body. The same `int (*)(...)` may be handed to a routine that stops on a non-zero status and to one that reads it as a rejected step and tries again with a smaller radius -- measured on the same bodies, a non-zero status cost `gsl_multifit_nlinear` 6 calls against 103 -- and what a body is handed to is not something it can know.
890
+
891
+ #### Telling the holder to stop
892
+
893
+ The stand-in keeps a library from converging on fiction, but it does not stop the library: an optimizer given a large `maxeval` goes on calling to the end, each call turned away at the top. `#on_error` gives the body a C function to call when it fails, so that whoever holds the address can be told:
894
+
895
+ ```ruby
896
+ nlopt = Fiddle::Handle.new("libnlopt.dylib")
897
+ f.on_error(nlopt["nlopt_force_stop"], opt) # void nlopt_force_stop(nlopt_opt)
898
+ f.watching { nlopt_optimize.call(opt, x, minf) }
899
+ ```
900
+
901
+ The hook is any `void (*)(void *)` -- an address as a `Fiddle::Pointer` or an Integer -- and the second argument is the one pointer it is called with. It is called once, by the call whose body put the flag up; every call after that is turned away before the body runs, so it is not called again until the flag has been put down and gone up again. It runs in C and reaches no Ruby value, as the body does not. `on_error(nil)` takes it away.
902
+
903
+ `#call` does not call it. That is one call made from Ruby, and it answers for itself by raising, as it does inside a window. A body that cannot fail has no flag to go up and is compiled without the hook; `on_error` is accepted there and never called, so a library binding can set it without asking which kind of body it was given. The hook belongs to the thread that set it, like the flag, so it is set on the thread that will lend the address -- which is where the window is written anyway. A window that wants a hook only for its own length, and the owner's back afterwards, is `CArray::JIT.watching`'s `on_error:` above.
854
904
 
855
905
  A float division is not this case: `1.0 / 0.0` is an infinity in Ruby, in C and here, so a body that only divides floats declares no flag and pays nothing for one. Neither is a subscript on a pointer parameter, which is unchecked by design -- the caller's business, as it is in C. The one exception needs nothing to run: a literal subscript outside a length the declaration gave -- `v[7]` or `v[-1]` against `double v[2]` -- is refused as the body is read, since the caller is held to that many cells and no more.
856
906
 
@@ -892,6 +942,8 @@ The generated C keeps the spelling it was given, so the C compiler gives it its
892
942
 
893
943
  A parameter may be one, which is the point of reading them: a body that counts bytes or elements is declared the way C declares it rather than in a translation of it.
894
944
 
945
+ The platform's own integer words read the same way, in whatever order C allows them: `unsigned` is `unsigned int`, `long unsigned int` is `unsigned long`, and `int` may be left out wherever C lets it be. That is how headers are written -- NLopt's objective is `double (*)(unsigned n, const double *x, double *grad, void *f_data)` -- and a declaration copied out of one reads as it stands. A combination C has no type for, such as `short long` or `signed unsigned`, is refused by name.
946
+
895
947
  ```ruby
896
948
  stride = CArray.jit_function("size_t stride(size_t n, size_t width)") { |n, w|
897
949
  n * w
@@ -1020,6 +1072,25 @@ poly.block.call(2.0, coef) # in Ruby, same bits
1020
1072
 
1021
1073
  A `void *` stays a slot, because it points at nothing in particular: it takes its place in the signature -- which is what makes `gsl_function` sayable -- and the body may not reach through it.
1022
1074
 
1075
+ #### Asking whether an address came
1076
+
1077
+ Some callers decide per call whether a pointer is there at all. NLopt hands an objective a gradient to fill when the method uses one and `NULL` when it does not, so a body that writes `grad[0]` unconditionally writes through `NULL` under a derivative-free method. The body asks first, in the spellings Ruby answers the same way for `nil`:
1078
+
1079
+ ```ruby
1080
+ objective = CArray.jit_function(
1081
+ "double (*)(uint32_t n, const double *x, double *grad, void *data)"
1082
+ ) { |n, x, grad, data|
1083
+ if grad # grad.nil?, grad == nil and grad != nil read too
1084
+ grad[0] = 2.0 * x[0]
1085
+ end
1086
+ x[0] * x[0]
1087
+ }
1088
+
1089
+ objective.call(1, x, nil, nil) # nil arrives as NULL
1090
+ ```
1091
+
1092
+ A bare pointer name is read as the question only where a condition is read -- `if`, `while`, `?:`, and `!`, `&&` and `||` inside one -- because that is where Ruby's truthiness is the question. Anywhere else a pointer is still not a value, and `g = grad` is refused as before. A `void *` may be asked too: whether a slot was filled is a question about the slot, not a reach through it. `f.block.call` takes `nil` in the same place and runs the same branch.
1093
+
1023
1094
  A kernel can hand one of its own arrays over the same way:
1024
1095
 
1025
1096
  ```ruby
@@ -1083,6 +1154,8 @@ end
1083
1154
 
1084
1155
  The declaration's parameter names are the join, and they do the work twice over: they are the body's parameters, so the block declares none, and they name the locals around the call the values come from -- `out`, `values`, `n` and `window` are read out of the method at the moment the call is made. In C a parameter's name in a prototype is decoration; here it is the whole binding. A declared name with no local behind it is refused at the call, by name, rather than read as a `nil` inside the body, and a declaration that leaves a parameter unnamed is refused for the same reason: there is nothing to bind by.
1085
1156
 
1157
+ The join holds in one direction only. A parameter is passed by value, so the body cannot write the local it came from -- and since the block reads as a closure, `count = count + 1` inside looks exactly as if it could. It is refused, naming the parameter, rather than compiled into a write that goes nowhere. An array's cells do come back through its pointer, which is how a number that has to come back travels: as a one-cell array, written as `count[0]`. A `jit_function` body is not held to it. There the parameters are the block's own, written between the bars, and nothing about them looks like a local outside -- so a body may count one down or replace it, as a C body may, and the name holds what was last assigned to it, in Ruby's type rather than the declaration's: `n = n / 2.0` makes an `int64_t n` a Float from there on. A pointer parameter is the exception: it is the array the body indexes, and assigning the name is refused; its cells are what a body writes.
1158
+
1086
1159
  What this saves over `jit_function` and a `call` is the argument list, which restates the declaration in an order nothing checks. What it costs is reading those locals through the block's binding, about 0.3 microseconds against a call that costs several -- so the body it suits is one that does a loop's worth of work per call, which is what a `void` function over whole arrays already is.
1087
1160
 
1088
1161
  What is inside the block is what `jit_function` compiles, under the same rules: the same subset, the same local arrays, the same error slot, and the same refusal for a name it closes over that the declaration did not name. What differs is where the compiled function is kept. It is kept per call site rather than per block -- a block literal is a fresh Proc every time the line runs, but the instruction sequence behind it belongs to the site -- so the second pass through the method is a lookup, and `CArray::JIT.clear_registry` reaches these as it reaches the rest.
@@ -1098,29 +1171,31 @@ Anything outside it raises `CArray::JIT::Unsupported`, naming the construct and
1098
1171
  - Integer, Float, imaginary (`2i`), `true` and `false` literals
1099
1172
  - The block's parameters: the loop indices
1100
1173
  - Block-local variables, assigned before use, reassignable -- including to a different type (see [Locals](#locals-types-and-postfix-math))
1101
- - Captured CArrays, read and written; captured scalars, read only, Float, Integer or Complex. In a block with no indices an array may be named bare -- `a` -- which is `a[]`
1174
+ - Captured CArrays, read and written; captured scalars, Float, Integer or Complex, read where they are used; assigning one inside the block makes a local of the kernel, and the variable outside keeps its value. In a block with no indices an array may be named bare -- `a` -- which is `a[]`
1102
1175
  - `+ - * /`, unary `-`, `**`, comparisons `< <= > >= == !=`
1103
1176
  - `&&`, `||`, `!`
1104
1177
  - `abs`, `floor`, `ceil`, `round`, `truncate`, `to_i`, `to_f`
1105
1178
  - `real`, `imag`, `conjugate`, `arg` and their other Ruby spellings, on a Complex or on a real number; `Complex(x, y)` and `Complex(x)` to build one
1106
1179
  - `Math::PI`, `Math::E`
1107
- - `if`/`elsif`/`else` and the ternary operator, as expressions and as statements
1180
+ - `if`/`elsif`/`else` as statements; `if`/`else` and the ternary operator as expressions. `elsif` in an expression is refused -- nest the ternary instead
1108
1181
  - `a[i] == UNDEF` and `a[i] != UNDEF`, either way round; `a[i] = UNDEF`. A cell of a local array is asked and marked the same way, `w[k] == UNDEF` and `w[k] = UNDEF`, where the kernel carries masks -- which gives every local array a shadow of one byte a cell (see [Masks in a local array](#masks-in-a-local-array))
1109
- - `Math.sqrt`, `cbrt`, `exp`, `log`, `log2`, `log10`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `hypot`, `asinh`, `acosh`, `atanh`
1182
+ - `Math.sqrt`, `cbrt`, `exp`, `log`, `log2`, `log10`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `hypot`, `erf`, `erfc`, `gamma`, `asinh`, `acosh`, `atanh`
1110
1183
  - the postfix spelling of those -- `x.sqrt`, `(0.0415 * (t[i] - 218.8)).tanh` -- which `CArray::CoreExtensions` provides (see [Postfix math](#postfix-math))
1111
1184
  - `a[i - c]` and `a[i + c]`, with `c` a non-negative integer literal or an integer built from literals and captured integers, one subscript per axis of the array; a constant subscript pins an axis, and a computed one gathers or scatters
1112
1185
  - `x.nan?` and `x.finite?`, C's `isnan` and `isfinite`, answering true or false as Ruby's do. `nan?` is a Float's: an Integer and a Complex have no method by that name and raise `NoMethodError` in Ruby, so both are refused. `finite?` answers for all three, a Complex's being both parts finite as Ruby asks it. `infinite?` is **not** here -- it answers nil, 1 or -1 rather than true or false, and a kernel has no nil; ask `x.abs == Float::INFINITY`, or `x == Float::INFINITY` where the sign is the question. `negative?`, `positive?` and `zero?` are not here either, for the reason `signbit` is not: the comparison is the thing, and it is already in the subset
1113
1186
  - `x.clamp(low, high)`, which answers the value or whichever bound it ran past. All three have to be the same *class* -- Ruby hands back the receiver in one branch and a bound in the other, so `5.clamp(0.0, 3.0)` is the Float 3.0 where `1.clamp(0.0, 3.0)` is the Integer 1, and no type assigned before the loop runs is both. Two widths of one class are not that case: a float32 cell is a Ruby Float as a double is, so `f[i].clamp(0.0, 1.0)` keeps the cell's width. The two things Ruby raises `ArgumentError` for are raised here too -- bounds the wrong way round, and a NaN that cannot be ordered -- with Ruby's class; the message names the reason rather than the value, the error slot carrying a code and not a number. A cell with no value in it does not raise, which is the rule the division helpers already keep. The range form, `x.clamp(0.0..1.0)`, is not in the subset
1114
1187
  - `sum(w)`, `min(w)`, `max(w)` and `sort(w)` over a local array of one axis -- bare calls, the compiler's own names rather than methods on the array. The first three are expressions and `sort` is a statement (see [Intrinsics](#intrinsics))
1115
- - `%`, which floors as Ruby's does rather than truncating as C's does, and raises `ZeroDivisionError` for a zero divisor as Ruby's does -- a float one included, where C's `fmod` and CArray's `%` answer `NaN`. A float `/` by zero is an infinity, in Ruby as here
1188
+ - `%`, which floors as Ruby's does rather than truncating as C's does, and raises `ZeroDivisionError` for a zero divisor as Ruby's does -- a float one included, where C's `fmod` and CArray's `%` answer `NaN`. A zero float remainder keeps the dividend's sign, as Ruby's does: `-4.0 % 2.0` is `-0.0`, where CArray's `%` answers `0.0`. A float `/` by zero is an infinity, in Ruby as here
1116
1189
  - `x += e` and the rest of the operator assignments, on a local, on a cell (`work[i, k] += e`) and on a CScalar; each is the assignment it stands for, so a fold written with `+=` is still split into partial sums. `||=` and `&&=` are refused, being about nil and false rather than about arithmetic
1117
- - `& | ^ ~ << >>` on integers, and `& | ^` on booleans. A shift is C's shift in the width the kernel computes integers in, `int64_t` (`uint64_t` for an unsigned cell), and its result is narrowed on store like any other. So on a cell narrower than that, a count that reaches past the cell's own width gives the Ruby loop's answer and not CArray's, which shifts in the cell's width: `int32` `1 << 33` stores `0` here and in Ruby, where CArray's `<<` answers `2`. A count of 64 or more, or a negative one, is C's undefined shift rather than Ruby's
1190
+ - `& | ^ ~ << >>` on integers, and `& | ^` on booleans. A shift means what it means to Ruby's Integer: a negative count shifts the other way, and a count of 64 or more shifts every bit out (`0`, or `-1` for a negative value shifted right). It is made in the width the kernel computes integers in, `int64_t` (`uint64_t` for an unsigned cell), and its result is narrowed on store like any other, so a left shift past bit 63 wraps where Ruby's Integer would grow
1118
1191
  - `(from...to).each { |j| ... }` and `n.times { |j| ... }`, an inner loop whose index addresses reads and writes alike -- `work[i, j] = ...` is a row of workspace for the cell (see [A row of workspace per cell](02_KernelShapes.md#a-row-of-workspace-per-cell)); `next` and `break` inside it, and `next` in the kernel block to skip the cell
1119
1192
  - an inner loop's range written over another index -- `(p+1...3)`, `(0...4-m)`, the shape a triangular loop takes -- where the index is one of the loops around it. Such a range is read at its widest when a subscript is held to its array (see [Known limitations](#known-limitations))
1193
+ - an inner loop's range read from a local or a cell -- `(0...bound[i]).each` -- which is known only when the loop is reached. The loop runs it, and a subscript its index walks is checked at each access, as `a[b[i]]` is: past the end, a read gives cell 0, a write does nothing, and the kernel raises `IndexError` when the pass is over
1194
+ - `printf("format", args...)`, C's `printf` followed by a flush: an integer argument takes `%d %i %u %x %X %o`, a real one `%e %E %f %F %g %G %a %A`, a complex one prints as two reals and a boolean as an integer. A count that does not match the directives is refused
1120
1195
  - `from.step(to, s) { |j| ... }` and `(from...to).step(s) { |j| ... }`, the same loop counting by a literal stride: `(n-1).step(0, -1)` is a downward sweep, and `to` is included there as Ruby includes it. Two loops in one body may both be written `{ |k| ... }`; each keeps its own range
1121
1196
  - `while cond ... end`, and its modifier form, with `next` and `break` inside it; the condition is read at the top of every pass, and a local it reads must be a local before the loop. `while true` is allowed where the body holds a `break` or a `raise`, and refused where it holds neither
1122
1197
  - a call to a C function -- one from `jit_extern` or `jit_function`, the function's own name inside its body, or, inside a `jit_function` body, another function written with `jit_function` -- as an expression, and *as a statement*, where its value is dropped as Ruby drops it and what it did is wherever its pointer parameters pointed. It is the only call that may stand alone; a `void` function -- borrowed or written here -- may only be called there. Under a mask the call does not happen (see [Calling a C function](#a-call-may-stand-alone))
1123
- - assignment to a cell: `out[i] = ...`, at a cell the loop walks onto -- every axis of it either walks with an index at no offset or is pinned, so `out[i, 0]` writes a column and `out[i, i]` a diagonal. Pin every axis and nothing walks: `box[0] = ...` writes one cell for every iteration and keeps the last, as the same Ruby loop does. A pinned position is checked against the extent before the first cell, so reaching outside is a message rather than a store past the end
1198
+ - assignment to a cell: `out[i] = ...`, at a cell the loop walks onto -- every axis of it either walks with an index, at any offset, or is pinned, so `out[i, 0]` writes a column and `out[i, i]` a diagonal. Pin every axis and nothing walks: `box[0] = ...` writes one cell for every iteration and keeps the last, as the same Ruby loop does. A pinned position is checked against the extent before the first cell, so reaching outside is a message rather than a store past the end
1124
1199
 
1125
1200
  **Rejected**
1126
1201
 
@@ -1128,9 +1203,11 @@ Everything else: `for` and `until`, `begin ... end while`, strings, hashes, symb
1128
1203
 
1129
1204
  ## Known limitations
1130
1205
 
1131
- - **Integer overflow wraps**, as CArray's own operators wrap. Ruby's Integer is arbitrary precision; the generated C uses `int64_t`, so a kernel that would grow past 2^63 wraps instead. Float kernels are unaffected.
1206
+ - **Integer overflow wraps**, as CArray's own operators wrap. Ruby's Integer is arbitrary precision; the generated C uses `int64_t`, so a kernel that would grow past 2^63 wraps instead, consistently within a kernel (it is compiled with `-fwrapv`, so `y = -a; y > 0` asks about the wrapped `y`). `INT64_MIN / -1` is `INT64_MIN`. Float kernels are unaffected.
1207
+ - **A negative Integer meeting a uint64** is refused, as CArray refuses the same operand: a literal when the block is compiled, a captured value (`RangeError`) when the kernel is called.
1208
+ - **An integer literal above 2^63 - 1 is refused where it is computed as an int64**, since it would wrap to a negative number there. Meeting a uint64 or a Float, or stored into a uint64 or float array, it keeps its value.
1132
1209
  - **An Integer compared with a Float is compared as two doubles**, as CArray compares them. Ruby compares the two exactly, so past 2^53 the answers part: `(2**53 + 1) == 2.0**53` is false in Ruby and true here, and `2**63 - 1` equals `2.0**63` here. Below 2^53 every int64 is a double exactly, and nothing differs. Where both sides may be that large, compare two integers.
1133
- - **A negative Float to a fractional power is `NaN`**, as `pow` answers and CArray does. Ruby answers a Complex -- `(-8.0) ** (1.0/3)` is `1.0+1.73i` -- which no real cell could hold anyway: storing it into a float array raises in Ruby too. A whole-number exponent, or a non-negative base, gives Ruby's number. For a cube root, `Math.cbrt` keeps the sign.
1210
+ - **A negative Float to a fractional power is `NaN` where it is read as a real**, as `pow` answers and CArray does. Ruby answers a Complex -- `(-8.0) ** (1.0/3)` is `1.0+1.73i` -- which no real cell could hold anyway: storing it into a float array raises in Ruby too. **Stored in a complex cell, or joined with a Complex** (`z[i] = x ** y`, `x ** y + 1i`), the power is Ruby's Complex, to the last bit. A local takes the type of the power, so `v = x ** y; z[i] = v` is the real power and `NaN`. A whole-number exponent, or a non-negative base, gives Ruby's number. For a cube root, `Math.cbrt` keeps the sign.
1134
1211
  - **Object arrays are not handled.** `CA_OBJECT` holds Ruby values rather than numbers, and reaching into Ruby from inside a kernel would give up what compiling it was for.
1135
1212
  - **`**` on a Complex is the one place the answer is not bit-for-bit Ruby's.** It is within a few machine epsilons, growing with the exponent. What is refused on a Complex is what Ruby refuses -- ordering comparisons, `%`, the rounding methods, the bit operators. See [Complex arrays](#complex-arrays).
1136
1213
  - **A sine and a cosine of the same argument are one `sincos` call.** The C compiler merges them, and its sine differs from `sin` in the last bit for some arguments, where Ruby calls `sin`. Either flag that stops it costs more elsewhere than the bit is worth here, so it is documented rather than disabled; see [Design notes](05_DesignNotes.md#a-sine-beside-a-cosine-is-sincos-and-is-allowed-to-be).
data/docs/04_Compiling.md CHANGED
@@ -67,7 +67,7 @@ In `~/.cache/carray-jit` (honouring `XDG_CACHE_HOME`), one directory per version
67
67
 
68
68
  A kernel is only good for the version that generated it and the architecture it was built for, so those get their own directories. CArray's version is there for the same reason and one of its own: a kernel is handed CArray's memory, on layouts CArray decides, so one compiled against one version and run against the next would answer wrongly rather than fail to load.
69
69
 
70
- The two numbers say different things and move on their own clocks: the first is the version that generated the kernel, the second the version it was generated against. This gem is not versioned with CArray, and one release of it meets more than one -- the dependency is `>= 3.0.2, < 3.1`, so 3.0.2 and 3.0.4 both satisfy it -- and a layout the kernel reaches into can move between them. A new release does not spend its cache budget on entries nothing can reach any more, and a home directory shared between machines -- over NFS, or between Rosetta and native -- does not have one architecture evicting the other's kernels. A directory whose newest entry has not been touched in 30 days (`CARRAY_JIT_CACHE_MAX_AGE_DAYS`) is removed.
70
+ The two numbers say different things and move on their own clocks: the first is the version that generated the kernel, the second the version it was generated against. This gem is not versioned with CArray, and one release of it meets more than one -- the dependency is `>= 3.0.4, < 3.1`, so 3.0.4 and 3.0.5 both satisfy it -- and a layout the kernel reaches into can move between them. A new release does not spend its cache budget on entries nothing can reach any more, and a home directory shared between machines -- over NFS, or between Rosetta and native -- does not have one architecture evicting the other's kernels. A directory whose newest entry has not been touched in 30 days (`CARRAY_JIT_CACHE_MAX_AGE_DAYS`) is removed.
71
71
 
72
72
  ```ruby
73
73
  CArray::JIT.cache_root #=> "/home/you/.cache/carray-jit"
@@ -263,7 +263,7 @@ The tridiagonal solver in `test/test_thomas.rb` is the case the design had to be
263
263
  The Ruby loop is the comparison this gem is about. The LAPACK row is **not** a claim that this is faster than LAPACK, and three things stand between it and any such reading:
264
264
 
265
265
  - `?gtsv` does LU with partial pivoting and solves systems that are not diagonally dominant. This solves the ones that are. Skipping the pivot is most of the difference.
266
- - What is timed is `CArray::Linalg.solve_tridiagonal`, not `?gtsv` itself: the diagonals are passed as views, so there are contiguity copies, plus validation and output allocation, inside that number.
266
+ - What is timed is `CA::Linalg.solve_tridiagonal`, not `?gtsv` itself: the diagonals are passed as views, so there are contiguity copies, plus validation and output allocation, inside that number.
267
267
  - The four arguments `?gtsv` overwrites have to be copied first, and that is excluded from the figure, because it is not part of solving.
268
268
 
269
269
  What the comparison is good for is the other direction. `?gtsv` exists, so this kernel can be checked against it, and it agrees to within rounding. The algorithms `jit_for` is actually for, a periodic tridiagonal solve or a domain-specific recurrence, have no LAPACK entry point to be checked against at all.
@@ -4,7 +4,7 @@ Decisions that were not obvious, and why.
4
4
 
5
5
  ## The type is C's, and so is the arithmetic where the width is real
6
6
 
7
- The storage type is CArray's, mapped to C exactly -- `int64_t`, `uint8_t`, `float` -- and everything the type itself decides follows from that: the width, the wrap on store, the bit patterns. There a kernel agrees with CArray, because both are the same C. A shift is the exception that proves it: a kernel shifts an integer in the width it computes in, `int64_t`, and narrows on store, so on an `int32` cell a count past 31 gives the Ruby loop's answer where CArray, shifting in 32 bits, gives another.
7
+ The storage type is CArray's, mapped to C exactly -- `int64_t`, `uint8_t`, `float` -- and everything the type itself decides follows from that: the width, the wrap on store, the bit patterns. There a kernel agrees with CArray's conversion between arrays (`to_type`), because both are the same C; CArray's store of a single Ruby Integer refuses one the cell cannot hold, where a kernel narrows it. A shift agrees too, though the two get there differently: a kernel shifts in the width it computes in, `int64_t`, and narrows on store, where CArray shifts in the cell's own width -- and since both take a shift to mean what Ruby's Integer means by it, the low bits that survive the narrowing are the same.
8
8
 
9
9
  The arithmetic follows the same rule where the width makes a difference to the answer, and Ruby's where it does not. That splits the types in two.
10
10
 
@@ -1,6 +1,6 @@
1
1
  # Cheatsheet
2
2
 
3
- Nine entry points: the eight `jit_` methods this gem puts on `CArray`, and
3
+ Ten entry points: the nine `jit_` methods this gem puts on `CArray`, and
4
4
  `CArray.fuse`, which is CArray's own and gets the compiler from this gem being
5
5
  installed. Every example here runs as written.
6
6
 
@@ -63,8 +63,8 @@ CArray.jit_for(1...6) { |i| x[i] = x[i-1] * 2 }
63
63
 
64
64
  The parameters are the loop indices and the arguments are their extents, one
65
65
  each, an Integer `n` standing for `0...n`. Naming an index is what lets a cell
66
- reach `x[i-1]`, and reaching a cell the kernel will later write is what fixes
67
- the direction the axis runs -- derived from the dependencies, not chosen.
66
+ reach `x[i-1]`. The direction an axis runs is the extent's -- a Range runs up,
67
+ `(n-2).step(0, -1)` runs down -- and is not checked against what the body reads.
68
68
 
69
69
  An inner loop counts up with `(a...b).each` or `n.times`, and by a stride with
70
70
  `a.step(b, s)` -- `(n-1).step(0, -1)` for a sweep back down a row. Its index
@@ -444,34 +444,33 @@ CArray.jit_for(4) { |i|
444
444
  found.to_a #=> [0, 2, 3, 3]
445
445
  ```
446
446
 
447
- #### 28. 配列から読んだ `bound[i]` まで数える。何に止められ、何で通るか (★★★)
447
+ #### 28. 配列から読んだ `bound[i]` 個ぶん、行 `i` の先頭から足す。何がいつ確かめられるか (★★★)
448
448
 
449
- `ヒント: 内側の range はループの前に分かっている`
449
+ `ヒント: データが決める範囲は、ループに着くまで分からない`
450
450
 
451
451
  ```ruby
452
452
  bound = CA_INT32([2, 3])
453
+ m = CA_INT32([[1, 2, 3], [4, 5, 6]])
453
454
  out = CArray.int32(2)
454
455
 
455
456
  CArray.jit_for(2) { |i|
456
- count = 0
457
- (0...bound[i]).each { |j| count = count + 1 }
458
- out[i] = count
457
+ s = 0
458
+ (0...bound[i]).each { |j| s = s + m[i, j] }
459
+ out[i] = s
459
460
  }
460
- #=> CArray::JIT::Unsupported: an inner loop's range is an integer expression
461
- # over literals, captured scalars and the indices around it
461
+ out.to_a #=> [3, 15]
462
462
  ```
463
463
 
464
+ リテラルと捕捉した整数で書いた範囲はカーネルが走る前に分かるので、`m[i, j]` が `m` の内側にあることは最初のセルの前に確かめられる。この範囲はループに着くまで分からないので、`m[i, j]` は `m[i, b[i]]` と同じく、アクセスのたびに確かめられる。行の端を越えると、カーネルはその回を終えてから raise する:
465
+
464
466
  ```ruby
467
+ bound = CA_INT32([2, 4])
465
468
  CArray.jit_for(2) { |i|
466
- count = 0
467
- j = 0
468
- while j < bound[i] # データが決める上限を運ぶのは while
469
- count = count + 1
470
- j = j + 1
471
- end
472
- out[i] = count
469
+ s = 0
470
+ (0...bound[i]).each { |j| s = s + m[i, j] }
471
+ out[i] = s
473
472
  }
474
- out.to_a #=> [2, 3]
473
+ #=> IndexError: index out of range
475
474
  ```
476
475
 
477
476
  #### 29. 複素配列の二乗を `squared` へ、元の絶対値を `size` へ (★★☆)
@@ -444,34 +444,33 @@ CArray.jit_for(4) { |i|
444
444
  found.to_a #=> [0, 2, 3, 3]
445
445
  ```
446
446
 
447
- #### 28. Count up to `bound[i]`, read from an array. What stops it, and what gets past it? (★★★)
447
+ #### 28. Sum the first `bound[i]` cells of row `i`, where `bound` is read from an array. What is checked, and when? (★★★)
448
448
 
449
- `hint: an inner range is known before the loop runs`
449
+ `hint: a range the data decides is known only when the loop is reached`
450
450
 
451
451
  ```ruby
452
452
  bound = CA_INT32([2, 3])
453
+ m = CA_INT32([[1, 2, 3], [4, 5, 6]])
453
454
  out = CArray.int32(2)
454
455
 
455
456
  CArray.jit_for(2) { |i|
456
- count = 0
457
- (0...bound[i]).each { |j| count = count + 1 }
458
- out[i] = count
457
+ s = 0
458
+ (0...bound[i]).each { |j| s = s + m[i, j] }
459
+ out[i] = s
459
460
  }
460
- #=> CArray::JIT::Unsupported: an inner loop's range is an integer expression
461
- # over literals, captured scalars and the indices around it
461
+ out.to_a #=> [3, 15]
462
462
  ```
463
463
 
464
+ A range written in literals and captured integers is known before the kernel runs, so `m[i, j]` is proved inside `m` before the first cell. This one is not known until the loop is reached, so `m[i, j]` is checked at each access instead, as `m[i, b[i]]` would be. Past the end of a row, the kernel finishes its pass and raises:
465
+
464
466
  ```ruby
467
+ bound = CA_INT32([2, 4])
465
468
  CArray.jit_for(2) { |i|
466
- count = 0
467
- j = 0
468
- while j < bound[i] # `while` carries a bound the data decides
469
- count = count + 1
470
- j = j + 1
471
- end
472
- out[i] = count
469
+ s = 0
470
+ (0...bound[i]).each { |j| s = s + m[i, j] }
471
+ out[i] = s
473
472
  }
474
- out.to_a #=> [2, 3]
473
+ #=> IndexError: index out of range
475
474
  ```
476
475
 
477
476
  #### 29. Write the square of a complex array into `squared`, and the magnitude of the original into `size` (★★☆)