carray-jit 0.1.2 → 0.1.4
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 +917 -3
- data/README.md +14 -12
- data/carray-jit.gemspec +1 -3
- data/docs/00_Introduction.md +5 -3
- data/docs/01_GettingStarted.md +1 -1
- data/docs/02_KernelShapes.md +94 -15
- data/docs/03_SupportedFeatures.md +665 -32
- data/docs/04_Compiling.md +34 -7
- data/docs/05_DesignNotes.md +3 -3
- data/docs/06_Cheatsheet.md +200 -7
- data/docs/07_StepByStep.ja.md +533 -0
- data/docs/07_StepByStep.md +534 -0
- data/docs/08_LanguageReference.md +427 -0
- data/examples/README.md +12 -0
- data/examples/applications/alarm.rb +121 -0
- data/examples/applications/collatz.rb +105 -0
- data/examples/applications/cubic_spline.rb +331 -0
- data/examples/applications/dithering.rb +144 -0
- data/examples/applications/group_stats.rb +115 -0
- data/examples/applications/lookup.rb +126 -0
- data/examples/applications/median_filter.rb +153 -0
- data/examples/applications/parcel_ascent.rb +220 -0
- data/examples/applications/point_in_polygon.rb +111 -0
- data/examples/applications/random_walk.rb +98 -0
- data/examples/applications/van_der_pol.rb +186 -0
- data/examples/applications/wet_bulb.rb +140 -0
- data/examples/features/10_complex.rb +14 -4
- data/examples/features/15_loops.rb +7 -1
- data/lib/carray/jit/access.rb +14 -0
- data/lib/carray/jit/analyzer.rb +2218 -140
- data/lib/carray/jit/block_reader.rb +37 -6
- data/lib/carray/jit/c_function.rb +961 -86
- data/lib/carray/jit/c_generator.rb +1970 -191
- data/lib/carray/jit/call.rb +68 -0
- data/lib/carray/jit/compiler.rb +102 -14
- data/lib/carray/jit/expression.rb +271 -33
- data/lib/carray/jit/kernel.rb +382 -32
- data/lib/carray/jit/node.rb +374 -9
- data/lib/carray/jit/sorting_networks.rb +182 -0
- data/lib/carray/jit/type_assignment.rb +424 -35
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +547 -77
- metadata +23 -8
- data/ext/carray_jit_access/carray_jit_access.c +0 -460
- data/ext/carray_jit_access/extconf.rb +0 -8
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
# 31 carray-jit exercises, with solutions
|
|
2
|
+
|
|
3
|
+
Thirty-one small tasks, in the order the documentation introduces things, with a
|
|
4
|
+
solution under each. Roughly in order of difficulty -- the stars say how much
|
|
5
|
+
of the subset a task leans on, not how much code it takes.
|
|
6
|
+
|
|
7
|
+
They run as written. Start with
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
require "carray"
|
|
11
|
+
require "carray/jit"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
and every `#=>` is what that line actually answers.
|
|
15
|
+
|
|
16
|
+
Read the task, try it, then look. Where the answer is that the compiler
|
|
17
|
+
refuses something, the refusal is the answer and is printed under the code.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
#### 1. Compute `a + b * c` into `out` in a single pass, with no array built in between (★☆☆)
|
|
22
|
+
|
|
23
|
+
`hint: the method for work that reaches no neighbour`
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
a = CA_DOUBLE([1, 2, 3])
|
|
27
|
+
b = CA_DOUBLE([2, 3, 4])
|
|
28
|
+
c = CA_DOUBLE([3, 4, 5])
|
|
29
|
+
out = CArray.double(3)
|
|
30
|
+
|
|
31
|
+
CArray.jit_each { out = a + b * c }
|
|
32
|
+
out.to_a #=> [7.0, 14.0, 23.0]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### 2. Compute `(a + b * c) / 2` over three int32 arrays, taking the result back as a new array. What is its data type? (★☆☆)
|
|
36
|
+
|
|
37
|
+
`hint: the other element-wise method`
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
a = CA_INT32([1, 2, 3])
|
|
41
|
+
b = CA_INT32([2, 3, 4])
|
|
42
|
+
c = CA_INT32([3, 4, 5])
|
|
43
|
+
|
|
44
|
+
half = CArray.jit_map { (a + b * c) / 2.0 }
|
|
45
|
+
half.to_a #=> [3.5, 7.0, 11.5]
|
|
46
|
+
half.data_type_name #=> "float64"
|
|
47
|
+
# typed from the block's last value, not from the arrays: `2.0` has no width
|
|
48
|
+
# of its own, so it takes the one it meets. Without it, int64.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### 3. Add a row of three to every row of a 2x3 table into `out`, without copying the row (★☆☆)
|
|
52
|
+
|
|
53
|
+
`hint: CArray will broadcast an axis, but not invent a rank`
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
table = CArray.double(2, 3).seq!(1.0)
|
|
57
|
+
row = CA_DOUBLE([10, 20, 30]).reshape(1, 3) # 1x3, not 3: else `ndim mismatch`
|
|
58
|
+
out = CArray.double(2, 3)
|
|
59
|
+
|
|
60
|
+
CArray.jit_each { out = table + row }
|
|
61
|
+
out.to_a #=> [[11.0, 22.0, 33.0], [14.0, 25.0, 36.0]]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### 4. Scale an array by a Float the block closes over, twice with different values and compiling only once. Collect both results (★☆☆)
|
|
65
|
+
|
|
66
|
+
`hint: nothing special is needed`
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
a = CA_DOUBLE([1, 2])
|
|
70
|
+
out = CArray.double(2)
|
|
71
|
+
|
|
72
|
+
[2.0, 5.0].map { |gain|
|
|
73
|
+
CArray.jit_each { out = a * gain } # a captured scalar is an argument,
|
|
74
|
+
out.to_a # not a constant compiled in
|
|
75
|
+
} #=> [[2.0, 4.0], [5.0, 10.0]]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### 5. Scale an array into `out` by a factor that carries a data type of its own (★☆☆)
|
|
79
|
+
|
|
80
|
+
`hint: CScalar`
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
a = CA_DOUBLE([1, 2])
|
|
84
|
+
gain = CScalar.double() { 2.5 }
|
|
85
|
+
out = CArray.double(2)
|
|
86
|
+
|
|
87
|
+
CArray.jit_each { out = a * gain }
|
|
88
|
+
out.to_a #=> [2.5, 5.0]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### 6. Write each value into `out`, with the negatives clipped to zero (★☆☆)
|
|
92
|
+
|
|
93
|
+
`hint: the branch is an expression`
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
a = CA_DOUBLE([-2, 0, 1.5, 3])
|
|
97
|
+
out = CArray.double(4)
|
|
98
|
+
|
|
99
|
+
CArray.jit_each { out = a < 0.0 ? 0.0 : a }
|
|
100
|
+
out.to_a #=> [0.0, 0.0, 1.5, 3.0]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 7. Write a vector into `out` reversed (★☆☆)
|
|
104
|
+
|
|
105
|
+
`hint: the cell has to say which cell it reads`
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
a = CArray.double(5).seq!(1.0)
|
|
109
|
+
out = CArray.double(5)
|
|
110
|
+
|
|
111
|
+
CArray.jit_for(5) { |i| out[i] = a[4 - i] }
|
|
112
|
+
out.to_a #=> [5.0, 4.0, 3.0, 2.0, 1.0]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### 8. First differences: `out[i] = a[i] - a[i-1]`, leaving `out[0]` at zero (★☆☆)
|
|
116
|
+
|
|
117
|
+
`hint: where can the range start?`
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
a = CA_DOUBLE([1, 3, 6, 10])
|
|
121
|
+
out = CArray.double(4)
|
|
122
|
+
|
|
123
|
+
CArray.jit_for(1...4) { |i| out[i] = a[i] - a[i - 1] }
|
|
124
|
+
out.to_a #=> [0.0, 2.0, 3.0, 4.0]
|
|
125
|
+
# 1... and not 0...: bounds are checked against the subscript as written,
|
|
126
|
+
# before the loop runs, so a guard inside the body would not help.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### 9. `out[i] = 0.5 * a[i] + 0.5 * out[i-1]`, with `out[0] = a[0]` (★★☆)
|
|
130
|
+
|
|
131
|
+
`hint: read what this loop just wrote`
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
a = CA_DOUBLE([4, 8, 8, 8])
|
|
135
|
+
out = CArray.double(4)
|
|
136
|
+
out[0] = a[0]
|
|
137
|
+
|
|
138
|
+
CArray.jit_for(1...4) { |i| out[i] = 0.5 * a[i] + 0.5 * out[i - 1] }
|
|
139
|
+
out.to_a #=> [4.0, 6.0, 7.0, 7.5]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### 10. Write a 2x3 into `out` transposed, as a 3x2 (★☆☆)
|
|
143
|
+
|
|
144
|
+
`hint: two parameters, two extents`
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
a = CArray.double(2, 3).seq!(1.0)
|
|
148
|
+
out = CArray.double(3, 2)
|
|
149
|
+
|
|
150
|
+
CArray.jit_for(2, 3) { |i, j| out[j, i] = a[i, j] }
|
|
151
|
+
out.to_a #=> [[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### 11. Write the largest value of each row into `out` (★☆☆)
|
|
155
|
+
|
|
156
|
+
`hint: a loop inside the cell`
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
a = CA_DOUBLE([[1, 3, 2], [9, 4, 5]])
|
|
160
|
+
out = CArray.double(2)
|
|
161
|
+
|
|
162
|
+
CArray.jit_for(2) { |i|
|
|
163
|
+
best = a[i, 0]
|
|
164
|
+
(1...3).each { |j| best = a[i, j] if a[i, j] > best }
|
|
165
|
+
out[i] = best
|
|
166
|
+
}
|
|
167
|
+
out.to_a #=> [3.0, 9.0]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### 12. Total an int32 array into a single value the kernel writes (★☆☆)
|
|
171
|
+
|
|
172
|
+
`hint: a local does not survive the iteration`
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
a = CArray.int32(10).seq!(1)
|
|
176
|
+
total = CScalar.int32() { 0 }
|
|
177
|
+
|
|
178
|
+
CArray.jit_for(10) { |i| total[] += a[i] }
|
|
179
|
+
total[0] #=> 55
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### 13. Given the bin number of each reading, count the readings of every bin into `counts` (★★☆)
|
|
183
|
+
|
|
184
|
+
`hint: the subscript is a value, not the index`
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
bin = CA_INT32([0, 2, 1, 1, 0, 1])
|
|
188
|
+
counts = CArray.int32(3)
|
|
189
|
+
|
|
190
|
+
CArray.jit_for(6) { |i| counts[bin[i]] += 1 }
|
|
191
|
+
counts.to_a #=> [2, 3, 1]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### 14. Write the distance from the origin of each (x, y) into `out` (★☆☆)
|
|
195
|
+
|
|
196
|
+
`hint: Math`
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
x = CA_DOUBLE([3, 5])
|
|
200
|
+
y = CA_DOUBLE([4, 12])
|
|
201
|
+
out = CArray.double(2)
|
|
202
|
+
|
|
203
|
+
CArray.jit_each { out = Math.sqrt(x * x + y * y) }
|
|
204
|
+
out.to_a #=> [5.0, 13.0]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### 15. Give `jit_each` a block that names an index, and `jit_for` one that names none. What comes back? (★★☆)
|
|
208
|
+
|
|
209
|
+
`hint: neither guesses`
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
a = CA_DOUBLE([1, 2, 3])
|
|
213
|
+
out = CArray.double(3)
|
|
214
|
+
|
|
215
|
+
CArray.jit_each { |i| out[i] = a[i] + 1.0 }
|
|
216
|
+
#=> CArray::JIT::Unsupported: this block names the arrays it reaches, so it
|
|
217
|
+
# takes no parameters; for a loop that names its indices, see `jit_for`
|
|
218
|
+
|
|
219
|
+
CArray.jit_for(3) { out = a + 1.0 }
|
|
220
|
+
#=> CArray::JIT::Unsupported: jit_for's block names the cells it is on, so it
|
|
221
|
+
# takes the loop indices as its parameters; a block that names none is
|
|
222
|
+
# element-wise and belongs to jit_each
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### 16. Write how many decimal digits each number has into `digits` (★★☆)
|
|
226
|
+
|
|
227
|
+
`hint: divide by ten until there is nothing left -- but how many times is that?`
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
value = CA_INT32([7, 42, 1000, 65536])
|
|
231
|
+
digits = CArray.int32(4)
|
|
232
|
+
|
|
233
|
+
CArray.jit_for(4) { |i|
|
|
234
|
+
n = value[i]
|
|
235
|
+
count = 0
|
|
236
|
+
while n > 0 # one cell goes round once, another five
|
|
237
|
+
n = n / 10 # integer division, and Ruby's: it floors
|
|
238
|
+
count = count + 1
|
|
239
|
+
end
|
|
240
|
+
digits[i] = count
|
|
241
|
+
}
|
|
242
|
+
digits.to_a #=> [1, 2, 4, 5]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### 17. Write the first column past 4.0 in each row into `first`, or -1 where there is none (★★☆)
|
|
246
|
+
|
|
247
|
+
`hint: stop looking`
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
rows = CA_DOUBLE([[1, 2, 9, 3], [5, 1, 1, 8]])
|
|
251
|
+
first = CArray.int32(2)
|
|
252
|
+
|
|
253
|
+
CArray.jit_for(2) { |i|
|
|
254
|
+
found = -1
|
|
255
|
+
(0...4).each { |j|
|
|
256
|
+
if rows[i, j] > 4.0
|
|
257
|
+
found = j
|
|
258
|
+
break
|
|
259
|
+
end
|
|
260
|
+
}
|
|
261
|
+
first[i] = found
|
|
262
|
+
}
|
|
263
|
+
first.to_a #=> [2, 0]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### 18. Multiply a 2x3 by a 3x2, taking the result back (★★☆)
|
|
267
|
+
|
|
268
|
+
`hint: the index that repeats`
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
a = CArray.double(2, 3).seq!(1.0)
|
|
272
|
+
b = CArray.double(3, 2).seq!(1.0)
|
|
273
|
+
|
|
274
|
+
CArray.jit_contract { |i, j, k| a[i, k] * b[k, j] }.to_a
|
|
275
|
+
#=> [[22.0, 28.0], [49.0, 64.0]]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### 19. For each row `i` of two 2x3 arrays, take back the sum over `k` of `x[i, k] * y[i, k]` (★★★)
|
|
279
|
+
|
|
280
|
+
`hint: the row index repeats too, and must not be summed`
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
x = CA_DOUBLE([[1, 2, 3], [4, 5, 6]])
|
|
284
|
+
y = CA_DOUBLE([[1, 1, 1], [2, 2, 2]])
|
|
285
|
+
|
|
286
|
+
CArray.jit_contract(:b) { |k| x[b, k] * y[b, k] }.to_a
|
|
287
|
+
#=> [6.0, 30.0]
|
|
288
|
+
# `b` is named in the call, not taken as a parameter; both at once is refused
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### 20. Take back each cell plus its two neighbours, with a cell off the end reading as zero (★★☆)
|
|
292
|
+
|
|
293
|
+
`hint: no index, no extent, no edge`
|
|
294
|
+
|
|
295
|
+
```ruby
|
|
296
|
+
a = CA_DOUBLE([1, 2, 3, 4])
|
|
297
|
+
|
|
298
|
+
CArray.jit_stencil(a, border: :zero) { |w| w[-1] + w[0] + w[1] }.to_a
|
|
299
|
+
#=> [3.0, 6.0, 9.0, 7.0]
|
|
300
|
+
# border: :zero, :clamp, :wrap compute the frame; :mask and :skip answer
|
|
301
|
+
# about the frame cells themselves
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### 21. Write every cell into `out`, with `UNDEF` replaced by zero (★★☆)
|
|
305
|
+
|
|
306
|
+
`hint: ask about one cell`
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
a = CA_DOUBLE([1, 2, 3])
|
|
310
|
+
a[1] = UNDEF
|
|
311
|
+
out = CArray.double(3)
|
|
312
|
+
|
|
313
|
+
CArray.jit_for(3) { |i| out[i] = a[i] == UNDEF ? 0.0 : a[i] }
|
|
314
|
+
out.to_a #=> [1.0, 0.0, 3.0]
|
|
315
|
+
# where "propagate" is the answer wanted, write nothing: masks are ORed
|
|
316
|
+
# across the inputs and carried to the outputs on their own
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### 22. Write 1, 2, 3 down the middle column of a 3x3, leaving the rest (★★☆)
|
|
320
|
+
|
|
321
|
+
`hint: hand the kernel the column`
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
table = CArray.double(3, 3)
|
|
325
|
+
column = table[nil, 1]
|
|
326
|
+
|
|
327
|
+
CArray.jit_for(3) { |i| column[i] = i + 1.0 }
|
|
328
|
+
table.to_a #=> [[0.0, 1.0, 0.0], [0.0, 2.0, 0.0], [0.0, 3.0, 0.0]]
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### 23. Total a uint64 array through a local accumulator. What stops it, and what gets past it? (★★★)
|
|
332
|
+
|
|
333
|
+
`hint: a literal has no width of its own`
|
|
334
|
+
|
|
335
|
+
```ruby
|
|
336
|
+
source = CArray.uint64(2).seq!(1)
|
|
337
|
+
out = CArray.uint64(1)
|
|
338
|
+
|
|
339
|
+
CArray.jit_for(1) { |i|
|
|
340
|
+
total = 0
|
|
341
|
+
(0...2).each { |j| total = total + source[j] }
|
|
342
|
+
out[i] = total
|
|
343
|
+
}
|
|
344
|
+
#=> CArray::JIT::Unsupported: `total` enters this loop as an Integer and comes
|
|
345
|
+
# back round as a uint64; the value carried to the next pass would change
|
|
346
|
+
# type, and one C variable is one type -- give it one type before the loop
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
```ruby
|
|
350
|
+
seed = CScalar.uint64() { 0 } # a value with a data type
|
|
351
|
+
|
|
352
|
+
CArray.jit_for(1) { |i|
|
|
353
|
+
total = seed
|
|
354
|
+
(0...2).each { |j| total = total + source[j] }
|
|
355
|
+
out[i] = total
|
|
356
|
+
}
|
|
357
|
+
out.to_a #=> [3]
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### 24. Suffix sums: `out[i] = a[i] + out[i+1]`, with `out[4]` already set (★★★)
|
|
361
|
+
|
|
362
|
+
`hint: which way does the loop have to run, and where do you say so?`
|
|
363
|
+
|
|
364
|
+
```ruby
|
|
365
|
+
a = CA_DOUBLE([1, 2, 3, 4, 5])
|
|
366
|
+
out = CArray.double(5)
|
|
367
|
+
out[4] = a[4]
|
|
368
|
+
|
|
369
|
+
CArray.jit_for(3.step(0, -1)) { |i| out[i] = a[i] + out[i + 1] }
|
|
370
|
+
out.to_a #=> [15.0, 14.0, 12.0, 9.0, 5.0]
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```ruby
|
|
374
|
+
out = CArray.double(5)
|
|
375
|
+
out[4] = a[4]
|
|
376
|
+
|
|
377
|
+
CArray.jit_for(0...4) { |i| out[i] = a[i] + out[i + 1] }
|
|
378
|
+
out.to_a #=> [1.0, 2.0, 3.0, 9.0, 5.0]
|
|
379
|
+
# the extent is the only place the direction is stated, and stating it
|
|
380
|
+
# wrongly is not refused: every cell read the zero that was still there
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### 25. Write the `i`-th Fibonacci number into `out[i]`, advancing two locals rather than reading the array back (★★★)
|
|
384
|
+
|
|
385
|
+
`hint: both names change at once`
|
|
386
|
+
|
|
387
|
+
```ruby
|
|
388
|
+
out = CArray.int64(12)
|
|
389
|
+
|
|
390
|
+
CArray.jit_for(12) { |i|
|
|
391
|
+
a = 0
|
|
392
|
+
b = 1
|
|
393
|
+
i.times { |k| a, b = b, a + b }
|
|
394
|
+
out[i] = a
|
|
395
|
+
}
|
|
396
|
+
out.to_a #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
|
|
397
|
+
# every value on the right is settled before anything on the left is
|
|
398
|
+
# written, so `a + b` reads the `a` this pass came in with. Written as two
|
|
399
|
+
# lines -- `a = b` and then `b = a + b` -- the second reads the `a` the
|
|
400
|
+
# first just wrote, and `a` merely doubles: [0, 1, 2, 4, 8, ...]. Not a
|
|
401
|
+
# refusal, and not this compiler's doing: those two lines mean the same in
|
|
402
|
+
# Ruby
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### 26. The longest run of 1s in a series of 0s and 1s (★★★)
|
|
406
|
+
|
|
407
|
+
`hint: two things have to survive the iteration, and neither is a sum`
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
flag = CA_INT32([0, 1, 1, 1, 0, 1, 1, 0])
|
|
411
|
+
run = CScalar.int32() { 0 }
|
|
412
|
+
best = CScalar.int32() { 0 }
|
|
413
|
+
|
|
414
|
+
CArray.jit_for(8) { |i|
|
|
415
|
+
run[] = flag[i] == 1 ? run[] + 1 : 0
|
|
416
|
+
best[] = run[] if run[] > best[]
|
|
417
|
+
}
|
|
418
|
+
best[0] #=> 3
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
#### 27. For each query, write the index of the last grid point not above it into `found` (★★★)
|
|
422
|
+
|
|
423
|
+
`hint: the grid is sorted`
|
|
424
|
+
|
|
425
|
+
```ruby
|
|
426
|
+
grid = CA_DOUBLE([0, 1, 4, 9, 16])
|
|
427
|
+
query = CA_DOUBLE([0.5, 5.0, 9.0, 12.0])
|
|
428
|
+
found = CArray.int32(4)
|
|
429
|
+
|
|
430
|
+
CArray.jit_for(4) { |i|
|
|
431
|
+
t = query[i]
|
|
432
|
+
low = 0
|
|
433
|
+
high = 4
|
|
434
|
+
while high - low > 1
|
|
435
|
+
middle = (low + high) / 2
|
|
436
|
+
if grid[middle] > t
|
|
437
|
+
high = middle
|
|
438
|
+
else
|
|
439
|
+
low = middle
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
found[i] = low
|
|
443
|
+
}
|
|
444
|
+
found.to_a #=> [0, 2, 3, 3]
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### 28. Sum the first `bound[i]` cells of row `i`, where `bound` is read from an array. What is checked, and when? (★★★)
|
|
448
|
+
|
|
449
|
+
`hint: a range the data decides is known only when the loop is reached`
|
|
450
|
+
|
|
451
|
+
```ruby
|
|
452
|
+
bound = CA_INT32([2, 3])
|
|
453
|
+
m = CA_INT32([[1, 2, 3], [4, 5, 6]])
|
|
454
|
+
out = CArray.int32(2)
|
|
455
|
+
|
|
456
|
+
CArray.jit_for(2) { |i|
|
|
457
|
+
s = 0
|
|
458
|
+
(0...bound[i]).each { |j| s = s + m[i, j] }
|
|
459
|
+
out[i] = s
|
|
460
|
+
}
|
|
461
|
+
out.to_a #=> [3, 15]
|
|
462
|
+
```
|
|
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
|
+
|
|
466
|
+
```ruby
|
|
467
|
+
bound = CA_INT32([2, 4])
|
|
468
|
+
CArray.jit_for(2) { |i|
|
|
469
|
+
s = 0
|
|
470
|
+
(0...bound[i]).each { |j| s = s + m[i, j] }
|
|
471
|
+
out[i] = s
|
|
472
|
+
}
|
|
473
|
+
#=> IndexError: index out of range
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### 29. Write the square of a complex array into `squared`, and the magnitude of the original into `size` (★★☆)
|
|
477
|
+
|
|
478
|
+
`hint: a data type like the others`
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
z = CA_CMPLX128([Complex(0, 1), Complex(1, 1)])
|
|
482
|
+
squared = CArray.cmplx128(2)
|
|
483
|
+
size = CArray.double(2)
|
|
484
|
+
|
|
485
|
+
CArray.jit_each { squared = z * z }
|
|
486
|
+
CArray.jit_each { size = z.abs }
|
|
487
|
+
|
|
488
|
+
squared.to_a #=> [(-1.0+0.0i), (0.0+2.0i)]
|
|
489
|
+
size.to_a #=> [1.0, 1.4142135623730951]
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
#### 30. Fill an array with a hundred thousand random numbers in [0, 1), drawn inside the kernel (★★☆)
|
|
493
|
+
|
|
494
|
+
`hint: not Ruby's rand -- the loop runs without the GVL`
|
|
495
|
+
|
|
496
|
+
```ruby
|
|
497
|
+
random = CArray::Rng.new(seed: 3)
|
|
498
|
+
draws = CArray.double(100_000)
|
|
499
|
+
|
|
500
|
+
CArray.jit_for(100_000) { |i| draws[i] = random.random }
|
|
501
|
+
draws.mean.round(4) #=> 0.5006
|
|
502
|
+
# which draw lands in which cell is the loop's order; where that matters,
|
|
503
|
+
# fill an array with CArray#random! first and read a cell of it
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
#### 31. A local holds an int32 cell and is squared into an int64 array. What C type did it get? (★★★)
|
|
507
|
+
|
|
508
|
+
`hint: read what was compiled`
|
|
509
|
+
|
|
510
|
+
```ruby
|
|
511
|
+
source = CArray.int32(4).seq!(1)
|
|
512
|
+
out = CArray.int64(4)
|
|
513
|
+
|
|
514
|
+
kernel = CArray.jit_for(4) { |i|
|
|
515
|
+
v = source[i]
|
|
516
|
+
out[i] = v * v
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
kernel.c_source.lines.grep(/int64_t v;/).first.strip
|
|
520
|
+
#=> "int64_t v;"
|
|
521
|
+
# and the assignment below it:
|
|
522
|
+
# v = (int64_t)*(int32_t *)(p_source + (i) * source_s0);
|
|
523
|
+
# nothing in `v = source[i]` asked for 64 bits: a local's type is settled
|
|
524
|
+
# from the whole body, and `v * v` lands in an int64 array
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
Where to go next: [06_Cheatsheet.md](06_Cheatsheet.md) to look things up,
|
|
530
|
+
[examples/features/](../examples/features) for a tour of one feature per file,
|
|
531
|
+
and [examples/applications/](../examples/applications) for programs that use
|
|
532
|
+
all of this to do something -- `mandelbrot.rb` is a loop like 16 whose
|
|
533
|
+
length no cell shares, `alarm.rb` is 25 with hysteresis, `lookup.rb` is 26
|
|
534
|
+
against half a million queries, `sieve.rb` is 27 from the other side.
|