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 +4 -4
- data/CHANGELOG.md +165 -0
- data/README.md +11 -10
- data/carray-jit.gemspec +1 -1
- data/docs/00_Introduction.md +2 -1
- data/docs/02_KernelShapes.md +1 -1
- data/docs/03_SupportedFeatures.md +88 -11
- data/docs/04_Compiling.md +2 -2
- data/docs/05_DesignNotes.md +1 -1
- data/docs/06_Cheatsheet.md +3 -3
- data/docs/07_StepByStep.ja.md +14 -15
- data/docs/07_StepByStep.md +14 -15
- data/docs/08_LanguageReference.md +428 -0
- data/lib/carray/jit/analyzer.rb +144 -7
- data/lib/carray/jit/c_function.rb +359 -21
- data/lib/carray/jit/c_generator.rb +398 -58
- data/lib/carray/jit/compiler.rb +27 -3
- data/lib/carray/jit/expression.rb +271 -33
- data/lib/carray/jit/kernel.rb +48 -9
- data/lib/carray/jit/node.rb +15 -0
- data/lib/carray/jit/type_assignment.rb +53 -1
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +223 -87
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4d8ccd59d268ba31816c5339867d7231b7051f7879d67faecdc4eb4d924b375
|
|
4
|
+
data.tar.gz: d73b3600e59633ac69986019597626b42a27e5e18b0143e00d39406d3f1c562f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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
|
|
69
|
-
* [Getting started](docs/01_GettingStarted.md) —
|
|
70
|
-
* [The shapes a kernel takes](docs/02_KernelShapes.md) —
|
|
71
|
-
* [Supported features](docs/03_SupportedFeatures.md) —
|
|
72
|
-
* [Compiling, caching and inspecting](docs/04_Compiling.md) —
|
|
73
|
-
* [Design notes](docs/05_DesignNotes.md) —
|
|
74
|
-
* [Cheatsheet](docs/06_Cheatsheet.md) —
|
|
75
|
-
* [
|
|
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.
|
|
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
|
data/docs/00_Introduction.md
CHANGED
|
@@ -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
|
|
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.
|
data/docs/02_KernelShapes.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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 `
|
|
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.
|
data/docs/05_DesignNotes.md
CHANGED
|
@@ -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
|
|
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
|
|
data/docs/06_Cheatsheet.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cheatsheet
|
|
2
2
|
|
|
3
|
-
|
|
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]
|
|
67
|
-
|
|
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
|
data/docs/07_StepByStep.ja.md
CHANGED
|
@@ -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
|
-
`ヒント:
|
|
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
|
-
|
|
457
|
-
(0...bound[i]).each { |j|
|
|
458
|
-
out[i] =
|
|
457
|
+
s = 0
|
|
458
|
+
(0...bound[i]).each { |j| s = s + m[i, j] }
|
|
459
|
+
out[i] = s
|
|
459
460
|
}
|
|
460
|
-
#=>
|
|
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
|
-
|
|
467
|
-
j =
|
|
468
|
-
|
|
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
|
-
|
|
473
|
+
#=> IndexError: index out of range
|
|
475
474
|
```
|
|
476
475
|
|
|
477
476
|
#### 29. 複素配列の二乗を `squared` へ、元の絶対値を `size` へ (★★☆)
|
data/docs/07_StepByStep.md
CHANGED
|
@@ -444,34 +444,33 @@ CArray.jit_for(4) { |i|
|
|
|
444
444
|
found.to_a #=> [0, 2, 3, 3]
|
|
445
445
|
```
|
|
446
446
|
|
|
447
|
-
#### 28.
|
|
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:
|
|
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
|
-
|
|
457
|
-
(0...bound[i]).each { |j|
|
|
458
|
-
out[i] =
|
|
457
|
+
s = 0
|
|
458
|
+
(0...bound[i]).each { |j| s = s + m[i, j] }
|
|
459
|
+
out[i] = s
|
|
459
460
|
}
|
|
460
|
-
#=>
|
|
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
|
-
|
|
467
|
-
j =
|
|
468
|
-
|
|
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
|
-
|
|
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` (★★☆)
|