carray-jit 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +771 -3
  3. data/README.md +7 -6
  4. data/carray-jit.gemspec +1 -3
  5. data/docs/00_Introduction.md +4 -3
  6. data/docs/01_GettingStarted.md +1 -1
  7. data/docs/02_KernelShapes.md +93 -14
  8. data/docs/03_SupportedFeatures.md +582 -26
  9. data/docs/04_Compiling.md +33 -6
  10. data/docs/05_DesignNotes.md +3 -3
  11. data/docs/06_Cheatsheet.md +198 -5
  12. data/docs/07_StepByStep.ja.md +534 -0
  13. data/docs/07_StepByStep.md +535 -0
  14. data/examples/README.md +12 -0
  15. data/examples/applications/alarm.rb +121 -0
  16. data/examples/applications/collatz.rb +105 -0
  17. data/examples/applications/cubic_spline.rb +331 -0
  18. data/examples/applications/dithering.rb +144 -0
  19. data/examples/applications/group_stats.rb +115 -0
  20. data/examples/applications/lookup.rb +126 -0
  21. data/examples/applications/median_filter.rb +153 -0
  22. data/examples/applications/parcel_ascent.rb +220 -0
  23. data/examples/applications/point_in_polygon.rb +111 -0
  24. data/examples/applications/random_walk.rb +98 -0
  25. data/examples/applications/van_der_pol.rb +186 -0
  26. data/examples/applications/wet_bulb.rb +140 -0
  27. data/examples/features/10_complex.rb +14 -4
  28. data/examples/features/15_loops.rb +7 -1
  29. data/lib/carray/jit/access.rb +14 -0
  30. data/lib/carray/jit/analyzer.rb +2077 -136
  31. data/lib/carray/jit/block_reader.rb +37 -6
  32. data/lib/carray/jit/c_function.rb +613 -76
  33. data/lib/carray/jit/c_generator.rb +1595 -156
  34. data/lib/carray/jit/call.rb +68 -0
  35. data/lib/carray/jit/compiler.rb +75 -11
  36. data/lib/carray/jit/kernel.rb +369 -32
  37. data/lib/carray/jit/node.rb +359 -9
  38. data/lib/carray/jit/sorting_networks.rb +182 -0
  39. data/lib/carray/jit/type_assignment.rb +371 -34
  40. data/lib/carray/jit/version.rb +1 -1
  41. data/lib/carray/jit.rb +560 -64
  42. metadata +22 -8
  43. data/ext/carray_jit_access/carray_jit_access.c +0 -460
  44. data/ext/carray_jit_access/extconf.rb +0 -8
data/lib/carray/jit.rb CHANGED
@@ -7,10 +7,14 @@ require "carray/jit/analyzer"
7
7
  require "carray/jit/block_reader"
8
8
  require "carray/jit/c_function"
9
9
  require "carray/jit/type_assignment"
10
+ require "carray/jit/sorting_networks"
10
11
  require "carray/jit/c_generator"
11
12
  require "carray/jit/compiler"
12
13
  require "carray/jit/access"
13
14
  require "carray/jit/kernel"
15
+ # `jit_call` and the provider that may answer for it, which need no
16
+ # compiler to be defined -- see the file for why that matters.
17
+ require "carray/jit/call"
14
18
 
15
19
  class CArray
16
20
 
@@ -42,12 +46,10 @@ class CArray
42
46
  # to make a per-cell computation fast, so quietly doing the slow thing would
43
47
  # answer a question that was not asked.
44
48
  #
45
- # The name is CArray's own: carray/lazy.rb defines jit_for to raise
46
- # NotImplementedError, saying that the block is compiled and that the
47
- # compiler is this gem. Requiring carray/jit replaces it with this one.
48
- # So the method is named where the subset is documented, and a program that
49
- # calls it either compiles or is told why it cannot -- an expression over
50
- # whole arrays that needs no compiler is CArray.fuse's.
49
+ # The name is this gem's: CArray defines no jit_for of its own, so the
50
+ # method exists once carray/jit is required, and is documented where the
51
+ # subset is. An expression over whole arrays that needs no compiler is
52
+ # CArray.fuse's.
51
53
  #
52
54
  # `reassociate:` says whether a reduction's accumulator may be split into
53
55
  # partial sums. It defaults to CArray::JIT.reassociate, which is true:
@@ -106,8 +108,8 @@ class CArray
106
108
  # matter, while an element-wise block has neither. And it is jit_map's
107
109
  # sibling in the other direction: the name says whether a value comes back.
108
110
  #
109
- # Like jit_for, the name is CArray's own and raises there until this gem
110
- # replaces it. Written without a compiler, the same computation is
111
+ # Like jit_for, the name is this gem's, and exists once carray/jit is
112
+ # required. Written without a compiler, the same computation is
111
113
  # CArray.fuse's -- the expression itself, one pass per operation, with the
112
114
  # intermediates this one does without.
113
115
  #
@@ -253,14 +255,20 @@ class CArray
253
255
  # is the convention's, with a third clause: an index that repeats is summed,
254
256
  # one that appears once is free, and a named one is free however often it
255
257
  # appears -- which is what puts the diagonal and the per-point quantity
256
- # inside the notation instead of outside it. A free index needs somewhere
257
- # to go, so a parameter at a single position is refused once the axes are
258
- # named: it is free, and the axes are already stated. The list is all of
259
- # the result's axes rather than some of them -- name one and you have named
260
- # them all -- which is what keeps the order it states complete. Naming is allowed
258
+ # inside the notation instead of outside it. The list is all of the
259
+ # result's axes rather than some of them -- name one and you have named them
260
+ # all -- so what is left out of it is summed, at however few positions it
261
+ # sits: `jit_contract(:i) { |k| a[i,k] }` is the row sums, which the
262
+ # convention alone cannot say. (`sum(axis:)` is the faster way to write
263
+ # that one, being a reduction rather than a contraction.) Naming is allowed
261
264
  # even where the convention would have reached the same answer, which is how
262
265
  # the result's axes are put in another order.
263
266
  #
267
+ # Where the block assigns into an array of yours, the left-hand side has the
268
+ # result's axes on it, and an axis there that the list left out is the list
269
+ # falling short rather than an index to sum -- which is refused, and is the
270
+ # one place a short list is caught.
271
+ #
264
272
  # The sum is split into partial ones, as `jit_for`'s reduction is: a
265
273
  # contraction says which indices are summed and nothing about the order, so
266
274
  # there is no order here to override. `CArray::JIT.reassociate = false`
@@ -337,6 +345,77 @@ class CArray
337
345
  JIT.function(prototype, &block)
338
346
  end
339
347
 
348
+ # Gives every cell of `self` the block's value at its indices, and returns
349
+ # `self`.
350
+ #
351
+ # The block takes one parameter per axis, as a constructor block does, and
352
+ # its value is what the cell at those indices gets:
353
+ #
354
+ # CArray.int32(1000, 1000).jit_init { |i, j| (i + j) % 2 }
355
+ #
356
+ # That is what `CArray.int32(n, n) { |i, j| ... }` says, without the Ruby
357
+ # call per cell: the constructor form is `map_index!`, which leaves C for
358
+ # every element and comes back, and this one compiles the body and runs the
359
+ # whole loop as C.
360
+ #
361
+ # It is the only member of the family that is an instance method, because
362
+ # the array being written is the receiver. {CArray.jit_for} runs the same
363
+ # loop and has to be given both the index space and the array to write into
364
+ # -- `CArray.jit_for(n, n) { |i, j| z[i, j] = ... }` -- where here the
365
+ # extents are the receiver's shape and the target is the receiver, so
366
+ # neither is said twice.
367
+ #
368
+ # Reach for it when the values are a formula over the indices that will not
369
+ # go through whole-array arithmetic. Where it will, that needs no compiler
370
+ # and is faster still: a checkerboard is `(i[nil, :_] + i[:_, nil]) % 2`
371
+ # over an index vector, and a sequence or a random fill has `seq!` and
372
+ # `random!` already. What is left for this method is the formula that
373
+ # cannot be said that way -- a branch per cell, a value looked up, a
374
+ # recurrence along an axis.
375
+ #
376
+ # A block outside the compilable subset raises CArray::JIT::Unsupported, as
377
+ # every other entry point here does. Nobody writes `jit_init` except to
378
+ # have the compiled loop, so quietly running the slow one would answer a
379
+ # question that was not asked -- write the constructor block when the slow
380
+ # one is what is wanted.
381
+ #
382
+ # The block is read and compiled, never called, so it is not yielded to.
383
+ #
384
+ # @param reassociate [Boolean, nil] as {CArray.jit_for}; `nil` defers to
385
+ # {CArray::JIT.reassociate}.
386
+ # @return [CArray] `self`
387
+ # @raise [CArray::JIT::Unsupported] when the block falls outside the
388
+ # recognized subset, or names a number of indices other than `ndim`.
389
+ def jit_init (reassociate: nil, &block)
390
+ unless block
391
+ raise JIT::Unsupported, "jit_init needs a block"
392
+ end
393
+ if block.arity.zero?
394
+ raise JIT::Unsupported,
395
+ "jit_init's block takes the indices of the cell it computes, one " \
396
+ "per axis, as a constructor block does; a block that names none " \
397
+ "computes from other arrays and belongs to jit_each"
398
+ end
399
+ # A splat or an optional parameter makes the arity negative, and the
400
+ # count in the message below a number nobody wrote. The block reader
401
+ # refuses both a few steps later; here there is an arity to compare, and
402
+ # -1 is not one.
403
+ if block.arity.negative?
404
+ raise JIT::Unsupported,
405
+ "jit_init's block names one index per axis, and names them as " \
406
+ "required parameters; this one takes a splat or an optional " \
407
+ "parameter, which says nothing about how many axes it is for"
408
+ end
409
+ unless block.arity == ndim
410
+ raise JIT::Unsupported,
411
+ "this array has #{ndim} #{ndim == 1 ? 'axis' : 'axes'} and the " \
412
+ "block names #{block.arity} " \
413
+ "#{block.arity == 1 ? 'index' : 'indices'}; jit_init fills every " \
414
+ "cell, so there is one index per axis"
415
+ end
416
+ JIT.run(dim, block, reassociate, into: self)
417
+ end
418
+
340
419
  # @!endgroup
341
420
 
342
421
  # The compiler behind `CArray.jit_*`: it reads a block, generates C for it,
@@ -379,7 +458,11 @@ class CArray
379
458
  # the block reaches for it the way it reaches for a captured value.
380
459
  # It is neither: it is an index, and it is answered here.
381
460
  names = capture_names(source, node) - (free_indices || [])
382
- arrays, scalars, c_functions = split_captures(names, binding_of(block))
461
+ arrays, scalars, c_functions, randoms =
462
+ split_captures(names, binding_of(block))
463
+ refuse_a_generator(randoms, "a contraction", "the block is a summand " \
464
+ "and runs once per term, so a draw in it would be one per term " \
465
+ "rather than one per cell of the result")
383
466
  contract(source, arrays, free_indices, node: node, origin: origin,
384
467
  scalars: scalars, c_functions: c_functions)
385
468
  end
@@ -423,7 +506,11 @@ class CArray
423
506
  # is not the same as naming an empty list of axes.
424
507
  free_indices = nil if free_indices.empty?
425
508
  names = capture_names(source, node) - (free_indices || [])
426
- arrays, scalars, c_functions = split_captures(names, binding_of(block))
509
+ arrays, scalars, c_functions, randoms =
510
+ split_captures(names, binding_of(block))
511
+ refuse_a_generator(randoms, "a contraction", "the block is a summand " \
512
+ "and runs once per term, so a draw in it would be one per term " \
513
+ "rather than one per cell of the result")
427
514
  # A compiled function in the summand is not something the structure
428
515
  # carries; a captured number is, as the scale.
429
516
  return nil unless c_functions.empty?
@@ -620,7 +707,8 @@ class CArray
620
707
  # `contract_terms` reach it with a source it wrote itself.
621
708
  def contract (source, arrays, free_indices,
622
709
  node: nil, origin: nil, scalars: {}, c_functions: {})
623
- result = allocate_result(source, node, arrays, scalars, free_indices)
710
+ result = allocate_result(source, node, arrays, scalars, free_indices,
711
+ c_functions)
624
712
  arrays = arrays.merge(RESULT => result) if result
625
713
 
626
714
  kernel = compile(source,
@@ -670,10 +758,13 @@ class CArray
670
758
  arrays.each do |name, array|
671
759
  next if name == written
672
760
  next unless root_of(array).equal?(target)
761
+ same = arrays.fetch(written).equal?(array)
673
762
  raise Unsupported,
674
- "`#{written}` and `#{name}` are the same array, which this " \
675
- "both writes and reads; that is a recurrence rather than a " \
676
- "contraction, and is written with jit_for"
763
+ "`#{written}` and `#{name}` are #{same ? 'the same array' : 'views of one array'}, " \
764
+ "which this both writes and reads; that is a recurrence " \
765
+ "rather than a contraction, and is written with jit_for. " \
766
+ "Two views are refused by the storage they share rather " \
767
+ "than by the cells, so two that share none are refused too"
677
768
  end
678
769
  end
679
770
  end
@@ -686,8 +777,10 @@ class CArray
686
777
  # typed before there is a kernel to ask, so the block is analyzed once
687
778
  # without being compiled. Returns nil when the block assigns into an
688
779
  # array of its own.
689
- def allocate_result (source, node, arrays, scalars, free_indices = nil)
690
- probe = probe_contraction(source, node, arrays, scalars, free_indices)
780
+ def allocate_result (source, node, arrays, scalars, free_indices = nil,
781
+ c_functions = {})
782
+ probe = probe_contraction(source, node, arrays, scalars, free_indices,
783
+ c_functions)
691
784
  return nil unless probe
692
785
  free, index_axes, type = probe
693
786
  shape = free.map do |index|
@@ -708,17 +801,23 @@ class CArray
708
801
  end
709
802
 
710
803
  # @private
711
- def probe_contraction (source, node, arrays, scalars, free_indices = nil)
804
+ def probe_contraction (source, node, arrays, scalars, free_indices = nil,
805
+ c_functions = {})
712
806
  key = [source, arrays.transform_values(&:data_type_name),
713
807
  scalars.transform_values { |value| TypeAssignment.scalar_type(value) },
714
808
  # The result's axes are named at the call site rather than in
715
809
  # the block, so the same source under another naming is another
716
810
  # kernel -- and another probe.
717
811
  free_indices,
812
+ # A compiled function the summand calls decides the type of
813
+ # what it hands back, which is the type the result is
814
+ # collected into.
815
+ c_functions.transform_values(&:signature),
718
816
  cell_names(arrays)]
719
817
  cached = probe_cache[key]
720
818
  return cached unless cached.nil?
721
- probe_cache[key] = build_probe(source, node, arrays, scalars, free_indices)
819
+ probe_cache[key] = build_probe(source, node, arrays, scalars,
820
+ free_indices, c_functions)
722
821
  end
723
822
 
724
823
  # @private
@@ -727,14 +826,20 @@ class CArray
727
826
  end
728
827
 
729
828
  # @private
730
- def build_probe (source, node, arrays, scalars, free_indices = nil)
829
+ def build_probe (source, node, arrays, scalars, free_indices = nil,
830
+ c_functions = {})
731
831
  storage_types = arrays.transform_values(&:data_type_name)
832
+ # The functions the summand calls are given to the probe as they are
833
+ # to the compile below it: a body that calls one is a body the probe
834
+ # has to be able to read, and what the call hands back is what the
835
+ # result is sized and typed from.
732
836
  analyzer = Analyzer.new(source, node: node, array_names: arrays.keys,
733
837
  contract: :probe, free_indices: free_indices,
838
+ c_functions: c_functions,
734
839
  cell_names: cell_names(arrays))
735
840
  return false if analyzer.body.statements.last.is_a?(ElementWrite)
736
841
 
737
- TypeAssignment.new(analyzer.body, storage_types, scalars)
842
+ TypeAssignment.new(analyzer.body, storage_types, scalars, c_functions)
738
843
  summand = analyzer.body.statements.last
739
844
  axes = Hash.new { |hash, key| hash[key] = [] }
740
845
  analyzer.subscripts.each do |array, uses|
@@ -776,6 +881,14 @@ class CArray
776
881
  # @private
777
882
  MAP_RESULT = :__map_result
778
883
 
884
+ # The array `jit_init` writes into is the receiver, which the block does
885
+ # not close over -- it is `self` there, and `self` is not a capture. So
886
+ # it joins the operands under a name this compiler made up, the way a
887
+ # generator's state does, and the block's value is written to it at the
888
+ # cell the loop is on.
889
+ # @private
890
+ INIT_TARGET = :__init_target
891
+
779
892
  # Whether CArray's sweep can run this pass, decided before there is a
780
893
  # kernel -- because the answer changes what is compiled.
781
894
  #
@@ -808,7 +921,7 @@ class CArray
808
921
  # but only where the tiers here would have to materialise it. So the
809
922
  # tier each operand would be opened at is the rest of the decision:
810
923
  #
811
- # TIER_ATTACH neither can walk it, and the sweep holds 32KB where
924
+ # TIER_XFER neither can walk it, and the sweep holds 32KB where
812
925
  # the tiers hold the whole box: the sweep runs
813
926
  # TIER_STRIDE a column, a transpose, every other cell -- the tiers
814
927
  # address it in place, and the sweep re-gathers it for
@@ -821,7 +934,7 @@ class CArray
821
934
  # scratch either way -- there was nothing the re-gather was buying.
822
935
  def walkable_in_place? (arrays)
823
936
  tiers = arrays.map { |array| Access.classify(array)[:tier] }
824
- return true if tiers.include?(Access::TIER_ATTACH)
937
+ return true if tiers.include?(Access::TIER_XFER)
825
938
  tiers.none? { |tier| tier == Access::TIER_STRIDE }
826
939
  end
827
940
 
@@ -906,8 +1019,11 @@ class CArray
906
1019
 
907
1020
  windowed = windows.zip(arrays).to_h
908
1021
  free, assigned = free_and_assigned_names(source, node)
909
- captured, scalars, c_functions =
1022
+ captured, scalars, c_functions, randoms =
910
1023
  split_captures(free - windows, binding_of(block))
1024
+ refuse_a_generator(randoms, "a stencil", "its border is a second loop " \
1025
+ "over the frame, so the draws would not run in one pass over the " \
1026
+ "cells")
911
1027
  unless assigned.empty? || (assigned & captured.keys).empty?
912
1028
  raise Unsupported,
913
1029
  "a stencil's value is its block's, and the cells it is over " \
@@ -967,9 +1083,13 @@ class CArray
967
1083
  }
968
1084
  if aliased
969
1085
  raise Unsupported,
970
- "`into:` is the array `#{aliased.first}` reaches its window " \
971
- "into, and a cell written there is one a later cell reads; " \
972
- "a stencil writes into an array of its own"
1086
+ "`into:` and `#{aliased.first}` are views of one array, and " \
1087
+ "a cell written through the one could be a cell the other " \
1088
+ "reads through its window. Which cells two views have in " \
1089
+ "common is not a question asked here -- the storage is -- " \
1090
+ "so two slabs of one array are refused along with the " \
1091
+ "overlapping case. Give the stencil an array of its own, " \
1092
+ "or `#{aliased.first}.copy` to read from"
973
1093
  end
974
1094
  end
975
1095
  result.mask = 0 if (kernel.masked || border == :mask) && !result.has_mask?
@@ -1067,50 +1187,118 @@ class CArray
1067
1187
  # The result has to be sized and typed before there is a kernel to ask,
1068
1188
  # so the block is analyzed once without being compiled -- the same thing
1069
1189
  # a returned contraction does, for the same reason.
1070
- def allocate_map_result (source, node, arrays, scalars, shape, c_functions)
1071
- type = probe_map(source, node, arrays, scalars, c_functions)
1190
+ def allocate_map_result (source, node, arrays, scalars, shape, c_functions,
1191
+ randoms = {})
1192
+ type = probe_map(source, node, arrays, scalars, c_functions, randoms)
1072
1193
  CArray.send(type, *(shape.empty? ? [1] : shape))
1073
1194
  end
1074
1195
 
1075
1196
  # @private
1076
- def probe_map (source, node, arrays, scalars, c_functions)
1197
+ def probe_map (source, node, arrays, scalars, c_functions, randoms = {})
1077
1198
  key = [:map, source, arrays.transform_values(&:data_type_name),
1078
1199
  scalars.transform_values { |value| TypeAssignment.scalar_type(value) },
1079
- c_functions.transform_values(&:signature)]
1200
+ c_functions.transform_values(&:signature),
1201
+ randoms.transform_values(&:generator)]
1080
1202
  cached = probe_cache[key]
1081
1203
  return cached if cached
1082
1204
  analyzer = Analyzer.new(source, node: node, array_names: arrays.keys,
1083
- c_functions: c_functions, rank: 1, map: :probe)
1205
+ c_functions: c_functions, rank: 1, map: :probe,
1206
+ randoms: random_state_names(randoms))
1084
1207
  TypeAssignment.new(analyzer.body, arrays.transform_values(&:data_type_name),
1085
1208
  scalars, c_functions)
1086
1209
  value = analyzer.body.statements.last
1087
1210
  probe_cache[key] = TypeAssignment.result_storage_type(value.type)
1088
1211
  end
1089
1212
 
1213
+ # Handed where there is nothing to take out of the line-up, so that the
1214
+ # common case allocates nothing.
1215
+ EMPTY_ARRAYS = {}.freeze
1216
+
1090
1217
  # @private
1091
1218
  def run_over_whole_arrays (block, map: false)
1092
1219
  node, source, origin = read_block(block)
1093
1220
  free, assigned = free_and_assigned_names(source, node)
1094
- arrays, scalars, c_functions = split_captures(free, binding_of(block))
1221
+ # One Binding for the three questions asked of it. `Proc#binding`
1222
+ # allocates one every time it is asked, and this method is re-entered
1223
+ # on every call of an element-wise pass, so asking three times was
1224
+ # three allocations a call for one answer.
1225
+ outside = binding_of(block)
1226
+ arrays, scalars, c_functions, randoms = split_captures(free, outside)
1095
1227
  # `out = a + b` writes the array named `out` where the block was
1096
1228
  # written. A name the block assigns is not free in it, so it is
1097
1229
  # looked up here rather than by split_captures -- and a name that is
1098
1230
  # not an array outside stays what it looks like, a local.
1099
- arrays = arrays.merge(assigned_arrays(assigned, binding_of(block)))
1231
+ #
1232
+ # Except a name the block makes an array of its own under: there the
1233
+ # assignment is a declaration and not a write to anybody's cell, so it
1234
+ # is taken out before the lookup. Left in, and with an array of that
1235
+ # name outside, the broadcast either fails on a shape nobody wrote or
1236
+ # -- where the shapes happen to agree -- quietly makes the local array
1237
+ # an operand and part of the cache key. So it is refused here, which
1238
+ # is before the broadcast.
1239
+ made = local_array_names(source, node)
1240
+ refuse_a_shadowed_array(made, outside)
1241
+ arrays = arrays.merge(assigned_arrays(assigned - made, outside))
1100
1242
  if arrays.empty?
1101
1243
  raise Unsupported, "the block reaches no array"
1102
1244
  end
1103
1245
 
1104
- aligned, shape = broadcast(arrays)
1246
+ # An array the block only hands to a C function whole is not walked,
1247
+ # so the expression's shape has nothing to say about it -- and it has
1248
+ # a shape of its own that need not line up with anything. Left in,
1249
+ # one of a different length dies in the broadcast below, naming an
1250
+ # axis rather than the call it was written for.
1251
+ #
1252
+ # So it joins after the broadcast, which is what a generator's state
1253
+ # already does and for the same reason. The re-substitution further
1254
+ # down stays: this is a static reading, and the analyzer's answer is
1255
+ # the final one.
1256
+ handed_over = address_array_names(source, node, c_functions)
1257
+ # The common case is that there are none, and it pays nothing: no
1258
+ # hash is built and the broadcast is handed the operands it always
1259
+ # was. Every call comes through here -- `jit_each` is re-entered per
1260
+ # call -- so the work that is only needed sometimes is done only
1261
+ # then.
1262
+ addressed = handed_over.empty? ? EMPTY_ARRAYS
1263
+ : arrays.slice(*handed_over)
1264
+ walked = addressed.empty? ? arrays
1265
+ : arrays.reject { |name, _| addressed.key?(name) }
1266
+ if walked.empty?
1267
+ handed = addressed.keys.first
1268
+ raise Unsupported,
1269
+ "the block reaches no array to walk: `#{handed}` is handed " \
1270
+ "to a C function whole rather than read by cell, so it does " \
1271
+ "not say how many cells there are to compute. " \
1272
+ "`CArray.jit_for` with a count says that, and so does an " \
1273
+ "operand the block reads a cell of"
1274
+ end
1275
+
1276
+ aligned, shape = broadcast(walked)
1277
+ addressed.each { |name, array| aligned[name] = array }
1278
+ # A generator's state joins after the broadcast, never before it.
1279
+ # It is passed whole rather than walked, so the expression's shape
1280
+ # has nothing to say about it -- and it has a shape of its own that
1281
+ # would not line up anyway: four cells, against however many the
1282
+ # expression covers. Lining it up first is what `jit_each` did to a
1283
+ # one-cell state before 314d6cb, and a four-cell one does not even
1284
+ # stretch.
1285
+ states = random_states(randoms)
1286
+ arrays = arrays.merge(states)
1287
+ aligned = aligned.merge(states)
1105
1288
  if map
1106
1289
  result = allocate_map_result(source, node, arrays, scalars, shape,
1107
- c_functions)
1290
+ c_functions, randoms)
1108
1291
  arrays = arrays.merge(MAP_RESULT => result)
1109
1292
  aligned = aligned.merge(MAP_RESULT => result)
1110
1293
  end
1111
- masked = arrays.each_value.any? { |array| array.has_mask? } ||
1294
+ # Both questions are about the arrays the loop walks. An array
1295
+ # handed over whole is not one of them: its mask is not something the
1296
+ # C can read -- `address_buffer` refuses a masked one outright -- and
1297
+ # it is neither one of the sweep's operands nor a shape the sweep has
1298
+ # to agree with.
1299
+ masked = walked.each_value.any? { |array| array.has_mask? } ||
1112
1300
  mentions_undef(source, node)
1113
- sweeping = sweepable_pass?(arrays, shape, masked)
1301
+ sweeping = sweepable_pass?(walked, shape, masked)
1114
1302
  kernel = compile(source,
1115
1303
  node: node,
1116
1304
  origin: origin,
@@ -1118,6 +1306,7 @@ class CArray
1118
1306
  storage_types: arrays.transform_values(&:data_type_name),
1119
1307
  scalar_values: scalars,
1120
1308
  c_functions: c_functions,
1309
+ randoms: randoms,
1121
1310
  masked: masked,
1122
1311
  rank: sweeping ? 1 : shape.size,
1123
1312
  map: map,
@@ -1135,6 +1324,18 @@ class CArray
1135
1324
  aligned[name] = written
1136
1325
  end
1137
1326
 
1327
+ aligned = read_once_where_the_pass_writes(aligned, kernel.written_arrays)
1328
+
1329
+ # An array handed to a C function by address is passed whole rather
1330
+ # than walked, so the expression's shape has nothing to say about it.
1331
+ # Broadcasting it would stretch a one-cell state array into a
1332
+ # read-only `CARepeat`, which the copy-back after the call cannot
1333
+ # write through -- and a state array is exactly the shape a caller
1334
+ # reaches for.
1335
+ kernel.address_arrays.each do |name|
1336
+ aligned[name] = arrays.fetch(name)
1337
+ end
1338
+
1138
1339
  if kernel.masked
1139
1340
  kernel.written_arrays.each do |name|
1140
1341
  array = arrays.fetch(name)
@@ -1147,6 +1348,72 @@ class CArray
1147
1348
  map ? result : kernel
1148
1349
  end
1149
1350
 
1351
+ # `a = b * 2.0` reads the whole of the right-hand side and then assigns,
1352
+ # which is what the expression means in Ruby and what CArray's own
1353
+ # operators do. A kernel walks cell by cell instead, and that is the
1354
+ # same thing only while no cell it reads is a cell it has written: two
1355
+ # overlapping views of one array -- `hi = lo * 2.0` for blocks that
1356
+ # share six cells, a transpose written over itself -- read the output
1357
+ # back as input, cell by cell. A gather is copied before the loop and
1358
+ # keeps the meaning; a strided view is addressed in place, which is
1359
+ # what makes it fast and what leaves it exposed here.
1360
+ #
1361
+ # So an operand that is read, and shares a root with one that is
1362
+ # written without being that array itself, is copied once before the
1363
+ # loop. `a = a + 1.0` is not that case -- the cell read is the cell
1364
+ # written, as in Ruby -- and neither is a pass whose arrays are
1365
+ # separate, which is nearly all of them.
1366
+ def read_once_where_the_pass_writes (aligned, written_names)
1367
+ written = written_names.filter_map { |name| aligned[name] }
1368
+ return aligned if written.empty?
1369
+ roots = written.map { |array| root_of(array) }
1370
+ aligned.to_h do |name, array|
1371
+ next [name, array] if written_names.include?(name)
1372
+ next [name, array] if written.any? { |other| other.equal?(array) }
1373
+ shared = roots.any? { |root| root.equal?(root_of(array)) }
1374
+ [name, shared ? array.copy : array]
1375
+ end
1376
+ end
1377
+
1378
+ # An operand a kernel cannot walk -- a gather, a lazy array -- is copied
1379
+ # into a buffer before the loop and copied back after it. For an
1380
+ # expression over whole arrays that is what the expression means: the
1381
+ # right-hand side is read, then assigned. An indexed kernel means the
1382
+ # loop instead, and a loop reads a cell when it reaches it: where the
1383
+ # array copied is one the loop also writes, the copy is a photograph of
1384
+ # cells that go on changing. `y[i] = gy[i] + 1.0`, with `gy` a gather
1385
+ # of `y`, gave the reversal of the array it started with where the Ruby
1386
+ # loop sees its own writes; and a gather written beside a direct write
1387
+ # put the whole buffer back at the end, dropping the direct one.
1388
+ #
1389
+ # Neither is a thing to fix by copying harder, so it is refused, with
1390
+ # the loop that does mean something named: index the array itself and
1391
+ # let the subscript do the gathering.
1392
+ def refuse_a_transferred_alias (kernel, arrays)
1393
+ written = kernel.written_arrays.filter_map { |name| arrays[name] }
1394
+ return if written.empty?
1395
+ roots = written.map { |array| root_of(array) }
1396
+ arrays.each do |name, array|
1397
+ next unless Access.classify(array)[:tier] == Access::TIER_XFER
1398
+ root = root_of(array)
1399
+ # Another operand on the same storage, and one of the two written:
1400
+ # a copy taken before the loop cannot answer for either of them.
1401
+ # The array alone on its root is not that case -- what it scatters
1402
+ # back at the end, nothing in the loop was reading.
1403
+ others = arrays.each_value.reject { |other| other.equal?(array) }
1404
+ .select { |other| root.equal?(root_of(other)) }
1405
+ next if others.empty?
1406
+ next unless written.any? { |one| one.equal?(array) } ||
1407
+ others.any? { |other| written.any? { |one| one.equal?(other) } }
1408
+ raise Unsupported,
1409
+ "`#{name}` cannot be walked as it stands, so the kernel would " \
1410
+ "work on a copy of it taken before the loop -- and the loop " \
1411
+ "writes the array it is a view of, which the copy would not " \
1412
+ "see. Index that array directly and let the subscript gather: " \
1413
+ "`a[order[i]]` rather than a view of `a`"
1414
+ end
1415
+ end
1416
+
1150
1417
  # CArray lines the shapes up; a stretched axis comes back as a stride of
1151
1418
  # zero, which the kernel addresses like any other stride.
1152
1419
  # CArray leaves a CScalar as it is, because its own kernels know to read
@@ -1189,10 +1456,20 @@ class CArray
1189
1456
  end
1190
1457
 
1191
1458
  # @private
1192
- def run (extents, block, reassociate = nil)
1459
+ # `into` turns the block's value into a write: with it, the last
1460
+ # statement is what the cell of `into` gets, as jit_map's is what the
1461
+ # cell of its own result gets. Without it the block writes the arrays
1462
+ # it names, which is jit_for.
1463
+ def run (extents, block, reassociate = nil, into: nil)
1193
1464
  node, source, origin = read_block(block)
1194
1465
  names = capture_names(source, node)
1195
- arrays, scalars, c_functions = split_captures(names, binding_of(block))
1466
+ arrays, scalars, c_functions, randoms =
1467
+ split_captures(names, binding_of(block))
1468
+ # A generator's state is an operand like any other array, under a
1469
+ # name this compiler made up. `jit_for` lines nothing up, so it can
1470
+ # simply join the rest.
1471
+ arrays = arrays.merge(random_states(randoms))
1472
+ arrays = arrays.merge(INIT_TARGET => into) if into
1196
1473
 
1197
1474
  # A plain CArray carries no mask; one exists only once a cell has
1198
1475
  # actually been marked. So masks are touched at all only when some
@@ -1217,9 +1494,12 @@ class CArray
1217
1494
  storage_types: arrays.transform_values(&:data_type_name),
1218
1495
  scalar_values: scalars,
1219
1496
  c_functions: c_functions,
1497
+ randoms: randoms,
1220
1498
  masked: arrays.each_value.any? { |array| array.has_mask? },
1221
1499
  steps: steps,
1222
1500
  cell_names: cell_names(arrays),
1501
+ map: into ? true : false,
1502
+ result: into ? INIT_TARGET : nil,
1223
1503
  reassociate: reassociate.nil? ? JIT.reassociate : reassociate)
1224
1504
  # The kernel decides, not the caller: mentioning UNDEF makes it a
1225
1505
  # masked kernel even when no array carries a mask yet.
@@ -1236,8 +1516,9 @@ class CArray
1236
1516
  "#{pairs.size} #{pairs.size == 1 ? 'extent was' : 'extents were'} " \
1237
1517
  "given"
1238
1518
  end
1519
+ refuse_a_transferred_alias(kernel, arrays)
1239
1520
  kernel.call(arrays, scalars, pairs.map(&:first), c_functions)
1240
- kernel
1521
+ into || kernel
1241
1522
  end
1242
1523
 
1243
1524
  # Compiles for one set of array data types and one set of scalar types,
@@ -1248,7 +1529,7 @@ class CArray
1248
1529
  scalar_values:, c_functions: {}, masked: false, rank: nil,
1249
1530
  steps: nil, contract: false, result: nil, map: false,
1250
1531
  reassociate: false, cell_names: [], windows: [], border: nil,
1251
- free_indices: nil)
1532
+ free_indices: nil, randoms: {})
1252
1533
  # A kernel that mentions UNDEF is a masked one whatever its arrays
1253
1534
  # carry, and deciding that here means no caller has to remember it.
1254
1535
  masked ||= mentions_undef(source, node)
@@ -1260,6 +1541,12 @@ class CArray
1260
1541
  # kernel, and it is compiled once. One written here adds its
1261
1542
  # symbol, which stands for its body -- see `CFunction#kernel_key`.
1262
1543
  c_functions.transform_values(&:kernel_key),
1544
+ # Which generator each name is, and nothing about its state:
1545
+ # the C pasted for a draw is decided by the kind, and the seed
1546
+ # is data the kernel is handed at the call. So two runs that
1547
+ # differ only in seed are one kernel, and the same kernel
1548
+ # serves a generator reset between calls.
1549
+ randoms.transform_values(&:generator),
1263
1550
  masked, rank, steps, contract, result, map,
1264
1551
  # A contraction whose free indices were named is not the kernel
1265
1552
  # the same source is without them, nor with them in another
@@ -1285,7 +1572,8 @@ class CArray
1285
1572
  registry[key] = build(source, node, array_names, storage_types,
1286
1573
  scalar_values, c_functions, masked, rank, steps,
1287
1574
  contract, result, origin, map, reassociate,
1288
- cell_names, windows, border, free_indices)
1575
+ cell_names, windows, border, free_indices,
1576
+ randoms)
1289
1577
  end
1290
1578
 
1291
1579
  # A kernel that mentions UNDEF is a masked kernel whatever its arrays
@@ -1318,22 +1606,76 @@ class CArray
1318
1606
  Analyzer.free_and_assigned_names(source, node: node)
1319
1607
  end
1320
1608
 
1609
+ # Which names the block makes arrays of its own under. A property of
1610
+ # the source, so it is cached with the rest of them -- and for the
1611
+ # reason the captured names are: an element-wise pass re-enters on
1612
+ # every call, so a walk of the tree here is a walk per call. It was
1613
+ # the last of this family still being walked, at 3.3 us of a call.
1614
+ def local_array_names (source, node = nil)
1615
+ cached = local_array_name_cache[source]
1616
+ return cached if cached
1617
+ local_array_name_cache[source] =
1618
+ Analyzer.local_array_names(source, node: node)
1619
+ end
1620
+
1321
1621
  # @private
1322
1622
  def registry
1323
1623
  @registry ||= {}
1324
1624
  end
1325
1625
 
1626
+ # @private
1627
+ def local_array_name_cache
1628
+ @local_array_name_cache ||= {}
1629
+ end
1630
+
1326
1631
  # @private
1327
1632
  def capture_name_cache
1328
1633
  @capture_name_cache ||= {}
1329
1634
  end
1330
1635
 
1636
+ # Which names the block only hands to a C function whole. Cached for
1637
+ # the reason the captured names are: `jit_each` is re-entered on every
1638
+ # call, so anything read off the source here is read on every call, and
1639
+ # walking the tree again cost the element-wise pass 1.7x before this
1640
+ # was memoized.
1641
+ #
1642
+ # The answer depends on the declarations as well as the source -- a
1643
+ # parameter that takes a pointer is what makes an argument an address
1644
+ # pass -- so the signatures are part of the key, as they are for the
1645
+ # map probe. Nothing below the signature can change the answer: the
1646
+ # scan asks `indexable?` and nothing else.
1647
+ def address_array_names (source, node, c_functions)
1648
+ key = [source, c_functions.transform_values(&:signature)]
1649
+ cached = address_name_cache[key]
1650
+ return cached if cached
1651
+ address_name_cache[key] =
1652
+ Analyzer.address_array_names(source, node: node,
1653
+ c_functions: c_functions)
1654
+ end
1655
+
1656
+ # @private
1657
+ def address_name_cache
1658
+ @address_name_cache ||= {}
1659
+ end
1660
+
1331
1661
  # Keyed by instruction sequence, which CRuby hands back as the same
1332
1662
  # object for every Proc made from one block literal. That makes it a
1333
1663
  # free identity for the block, and keeps the file from being read and
1334
1664
  # parsed again on each call.
1665
+ #
1666
+ # The key is held weakly. A block literal in a file keeps its sequence
1667
+ # for the life of the process, but every `eval` makes a new one -- in a
1668
+ # console, or in code that builds its kernels as text -- and a Hash kept
1669
+ # each of them alive along with a parse of the whole script it came
1670
+ # from, for as long as the process ran. Before Ruby 3.3 there is no
1671
+ # WeakKeyMap and the cache is a Hash as it was.
1335
1672
  def block_cache
1336
- @block_cache ||= {}
1673
+ @block_cache ||= new_block_cache
1674
+ end
1675
+
1676
+ # @private
1677
+ def new_block_cache
1678
+ defined?(ObjectSpace::WeakKeyMap) ? ObjectSpace::WeakKeyMap.new : {}
1337
1679
  end
1338
1680
 
1339
1681
  # @!group Kernel cache
@@ -1345,8 +1687,14 @@ class CArray
1345
1687
  def clear_registry
1346
1688
  @registry = {}
1347
1689
  @capture_name_cache = {}
1348
- @block_cache = {}
1690
+ @address_name_cache = {}
1691
+ @block_cache = new_block_cache
1692
+ @function_registry = {}
1693
+ # `jit_call`'s, keyed by the site rather than the body.
1694
+ @call_sites = {}.compare_by_identity
1349
1695
  @undef_cache = {}
1696
+ @probe_cache = {}
1697
+ @local_array_name_cache = {}
1350
1698
  end
1351
1699
 
1352
1700
  # @return [String] the directory this environment's kernels are kept in,
@@ -1413,16 +1761,20 @@ class CArray
1413
1761
  def build (source, node, array_names, storage_types, scalar_values, c_functions,
1414
1762
  masked, rank = nil, steps = nil, contract = false, result = nil,
1415
1763
  origin = nil, map = false, reassociate = false,
1416
- cell_names = [], windows = [], border = nil, free_indices = nil)
1764
+ cell_names = [], windows = [], border = nil, free_indices = nil,
1765
+ randoms = {})
1417
1766
  analyzer = Analyzer.new(source, node: node, array_names: array_names,
1418
1767
  c_functions: c_functions,
1419
1768
  rank: rank, steps: steps, contract: contract,
1420
1769
  result: result, map: map, free_indices: free_indices,
1421
- cell_names: cell_names, windows: windows)
1770
+ cell_names: cell_names, windows: windows,
1771
+ randoms: random_state_names(randoms),
1772
+ masked: masked)
1422
1773
  assignment = TypeAssignment.new(analyzer.body, storage_types,
1423
1774
  scalar_values, c_functions)
1424
1775
  generator = CGenerator.new(analyzer, storage_types, assignment.scalar_types,
1425
1776
  c_functions: c_functions,
1777
+ randoms: randoms,
1426
1778
  masked: masked, reassociate: reassociate,
1427
1779
  steps: steps, border: border,
1428
1780
  origin: origin, block_source: source)
@@ -1448,6 +1800,25 @@ class CArray
1448
1800
  # Of the names the block assigns, the ones that are arrays where it was
1449
1801
  # written. Anything else -- a name that holds a number, or no name at
1450
1802
  # all -- is a local of the block's own.
1803
+ # A name that is both an array this block makes and an array where the
1804
+ # block was written. In the whole-array spelling an assignment writes
1805
+ # the outer array's cell, so the one line says two different things
1806
+ # depending on which reading you take -- and neither of them is what the
1807
+ # other reader will assume. Refused rather than chosen between.
1808
+ def refuse_a_shadowed_array (made, binding)
1809
+ shadowed = made.find { |name|
1810
+ binding.local_variable_defined?(name) &&
1811
+ binding.local_variable_get(name).is_a?(CArray)
1812
+ }
1813
+ return unless shadowed
1814
+ raise Unsupported,
1815
+ "`#{shadowed}` is made in this block with `CArray.double` or " \
1816
+ "its kin, and the block also closes over an array named " \
1817
+ "`#{shadowed}`; in this spelling an assignment writes that " \
1818
+ "array's cell, so the line means one thing to a reader and " \
1819
+ "another to this compiler. Rename one of the two"
1820
+ end
1821
+
1451
1822
  def assigned_arrays (names, binding)
1452
1823
  names.each_with_object({}) do |name, found|
1453
1824
  next unless binding.local_variable_defined?(name)
@@ -1460,15 +1831,84 @@ class CArray
1460
1831
  arrays = {}
1461
1832
  scalars = {}
1462
1833
  c_functions = {}
1834
+ randoms = {}
1463
1835
  names.each do |name|
1464
1836
  value = capture_value(name, binding)
1465
1837
  case value
1466
1838
  when CArray then arrays[name] = value
1467
1839
  when CFunction then c_functions[name] = value
1468
- else scalars[name] = value
1840
+ else
1841
+ if generator_class && value.is_a?(generator_class)
1842
+ randoms[name] = refuse_an_unpasteable(name, value)
1843
+ else
1844
+ scalars[name] = value
1845
+ end
1469
1846
  end
1470
1847
  end
1471
- [arrays, scalars, c_functions]
1848
+ [arrays, scalars, c_functions, randoms]
1849
+ end
1850
+
1851
+ # `CArray::Rng`, or nil where the CArray in use has none.
1852
+ #
1853
+ # Asked of the object rather than of the version, because the version
1854
+ # cannot answer: 3.0.2 is where the class arrived and 3.0.2 is also
1855
+ # what the release before it called itself. Asked once and remembered,
1856
+ # and reached only after a capture has failed to be an array or a
1857
+ # function -- a bare `when CArray::Rng` is evaluated for every captured
1858
+ # number, and against a CArray without the class that is a NameError
1859
+ # loose in a kernel that never mentions a generator.
1860
+ def generator_class
1861
+ return @generator_class unless @generator_class.nil?
1862
+ @generator_class = defined?(CArray::Rng) ? CArray::Rng : false
1863
+ end
1864
+
1865
+ # A generator CArray can run but cannot hand the source of.
1866
+ #
1867
+ # Checked here rather than where a generator is made, because a
1868
+ # generator is made by `CArray::Rng.new` and this gem is not on that
1869
+ # path -- it learns one is in play when a block closes over it, which
1870
+ # is here and is every route in. What it costs to check late is
1871
+ # nothing: a kernel that cannot paste the text cannot be compiled
1872
+ # either way, and the question is which of the two says so.
1873
+ #
1874
+ # Nothing reaches this today: CArray hands out the source of every
1875
+ # generator it has. It is the seam between two of its hashes, and the
1876
+ # failure without it is a `KeyError` out of the middle of code
1877
+ # generation.
1878
+ def refuse_an_unpasteable (name, generator)
1879
+ kind = generator.generator
1880
+ return generator if CArray::Rng::SOURCE.key?(kind)
1881
+ raise Unsupported,
1882
+ "`#{name}` is a #{kind.inspect} generator, and this CArray " \
1883
+ "does not hand out that generator's C " \
1884
+ "(CArray::Rng::SOURCE has " \
1885
+ "#{CArray::Rng::SOURCE.keys.map(&:inspect).join(', ')}), so a " \
1886
+ "kernel has nothing to paste. Fill an array with " \
1887
+ "`CArray#random!` and read a cell of it instead"
1888
+ end
1889
+
1890
+ # The name a generator's state array is an operand under.
1891
+ #
1892
+ # Made up here rather than taken from the caller, because the state is
1893
+ # not something the block named: it wrote `r.call`, and the array
1894
+ # behind that is machinery. The name is derived from the one the block
1895
+ # did write, so it is the same in the next process as in this one --
1896
+ # which is what lets a compiled kernel be found in the cache rather
1897
+ # than built again.
1898
+ def random_state_name (name)
1899
+ :"__random_state_#{name}"
1900
+ end
1901
+
1902
+ # Which state array each generator draws from, as operands.
1903
+ def random_states (randoms)
1904
+ randoms.to_h { |name, generator|
1905
+ [random_state_name(name), generator.state]
1906
+ }
1907
+ end
1908
+
1909
+ # Which made-up name each generator's state is under, for the analyzer.
1910
+ def random_state_names (randoms)
1911
+ randoms.to_h { |name, _| [name, random_state_name(name)] }
1472
1912
  end
1473
1913
 
1474
1914
  # A constant is looked up where the block was written, so it means what
@@ -1477,7 +1917,7 @@ class CArray
1477
1917
  # a compiled function or a table held in a constant is how a method
1478
1918
  # gets at one.
1479
1919
  def capture_value (name, binding)
1480
- if name.to_s.start_with?(/[A-Z]/)
1920
+ if constant_name?(name)
1481
1921
  begin
1482
1922
  binding.eval(name.to_s)
1483
1923
  rescue NameError
@@ -1494,26 +1934,70 @@ class CArray
1494
1934
  end
1495
1935
  end
1496
1936
 
1937
+ # A capture spelled with a capital is a constant and is looked up as one.
1938
+ # Asked of the symbol's own name, which Ruby hands back frozen rather
1939
+ # than building, and by the byte rather than by `/[A-Z]/`: this runs for
1940
+ # every captured name on every call, and the String and the match it
1941
+ # used to make were 2.7 us of an element-wise pass. ASCII either way --
1942
+ # that is what the pattern matched too.
1943
+ def constant_name? (name)
1944
+ first = name.name.getbyte(0)
1945
+ !first.nil? && first >= 0x41 && first <= 0x5A
1946
+ end
1947
+
1497
1948
  # `rand` is Kernel's, so "not defined" would be a lie, and the reason it
1498
1949
  # is not here is worth saying where it is reached for.
1499
1950
  DRAW_NAMES = [:rand, :srand].freeze
1500
1951
 
1501
1952
  def refuse_a_draw (name)
1953
+ # `random` with no `rng:` is not a name that was left undefined, it is
1954
+ # a draw with its generator missing. It reaches here rather than
1955
+ # `random_call` because a bare name with no arguments is parsed as a
1956
+ # name read, and only the parentheses tell the two apart.
1957
+ if [:random, :randomn].include?(name.to_sym)
1958
+ raise Unsupported,
1959
+ "`#{name}` in a kernel draws from a generator and has to say " \
1960
+ "which: `#{name}(rng: r)`, where `r` is a `CArray::Rng` -- " \
1961
+ "`CArray::Rng.new(seed: 4)` makes one"
1962
+ end
1502
1963
  return unless DRAW_NAMES.include?(name.to_sym)
1503
1964
  raise Unsupported, draw_message("`#{name}`")
1504
1965
  end
1505
1966
 
1506
- # A generator has one state and hands out its numbers in the order it
1507
- # was asked in, and a kernel does not fix that order: a stencil's border
1508
- # is a second loop over the frame, a reduction may split its
1509
- # accumulator, and the loop runs with the GVL released, which is not
1510
- # where Ruby's Random -- the one `CArray#random!` draws through -- may
1511
- # be reached at all. An array filled before the call has none of those
1512
- # questions in it.
1967
+ # A generator reaches `jit_for`, `jit_each` and `jit_map`, and stops
1968
+ # there. The two that are left are not refused because a draw is
1969
+ # meaningless in them but because it would not mean what it looks like,
1970
+ # so the reason is given rather than the fact.
1971
+ def refuse_a_generator (randoms, what, because)
1972
+ return if randoms.empty?
1973
+ name = randoms.keys.first
1974
+ raise Unsupported,
1975
+ "`#{name}` is a generator, and #{what} does not draw from one: " \
1976
+ "#{because}. Fill an array with `CArray#random!` and read a " \
1977
+ "cell of it, or draw in a `jit_for` / `jit_each` / `jit_map` " \
1978
+ "block, where `random(rng: #{name})` works"
1979
+ end
1980
+
1981
+ # `rand` is Ruby's, and Ruby's generator is reached through the VM: the
1982
+ # loop runs with the GVL released, which is not where it may be reached
1983
+ # at all. That is why this one is refused, and it is the only reason --
1984
+ # a kernel *can* draw, from a generator whose C it can paste, which is
1985
+ # what `CArray::Rng` is.
1986
+ #
1987
+ # Both ways out are named because they are different answers. A
1988
+ # generator draws in the order it is asked, and a kernel does not fix
1989
+ # that order: a stencil's border is a second loop over the frame, and a
1990
+ # reduction may split its accumulator. Where which draw lands in which
1991
+ # cell has to be settled -- common random numbers, antithetic variates
1992
+ # -- an array filled before the call is the answer, and a draw in the
1993
+ # loop is not. Where it does not, drawing in the kernel saves the
1994
+ # array.
1513
1995
  def draw_message (what)
1514
- "#{what} draws from a generator, and a kernel does not fix the order " \
1515
- "it would draw in; fill an array with `CArray#random!` and read a " \
1516
- "cell of it, as the kernel reads any other array"
1996
+ "#{what} draws from a generator this compiler cannot reach: it is " \
1997
+ "Ruby's, and the loop runs without the GVL. Draw from " \
1998
+ "`CArray::Rng`, which a kernel can; or, where which draw lands " \
1999
+ "in which cell matters, fill an array with `CArray#random!` and read " \
2000
+ "a cell of it as the kernel reads any other array"
1517
2001
  end
1518
2002
 
1519
2003
  # An extent is a Range, an Integer standing for `0...n`, or an
@@ -1532,6 +2016,18 @@ class CArray
1532
2016
  low = extent.begin || 0
1533
2017
  high = extent.end
1534
2018
  raise Unsupported, "an endless range has no extent" unless high
2019
+ # `(n-2)..0` reads like a downward sweep and is not one: Ruby gives
2020
+ # that Range no elements, so the loop it looks like would run no
2021
+ # passes and say nothing. A kernel handed one ran no passes too.
2022
+ # An empty range whose ends agree -- `0...0`, a slice that came out
2023
+ # empty -- is a count of zero and is taken as one.
2024
+ if low > high
2025
+ raise Unsupported,
2026
+ "`#{extent.inspect}` counts down and Ruby gives it no " \
2027
+ "elements, so this would run no passes at all. A downward " \
2028
+ "sweep is `#{low}.step(#{high}, -1)`, which is how Ruby " \
2029
+ "iterates backwards"
2030
+ end
1535
2031
  [[low, extent.exclude_end? ? high : high + 1, 1], nil]
1536
2032
  when Integer
1537
2033
  [[0, extent, 1], nil]