carray-jit 0.1.5 → 0.1.7

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.
@@ -1,4 +1,4 @@
1
- require "fiddle"
1
+ require "carray/jit/native"
2
2
  require "carray/jit/sweep"
3
3
 
4
4
  class CArray
@@ -7,8 +7,9 @@ class CArray
7
7
  # A compiled kernel, bound to one set of array data types and one set of
8
8
  # scalar types.
9
9
  #
10
- # Every kernel has the same C signature, so the Fiddle::Function shape is
11
- # fixed and the per-kernel detail travels in the six buffers.
10
+ # Every kernel has the same C signature, so the shape of the function
11
+ # Native makes is fixed and the per-kernel detail travels in the six
12
+ # buffers.
12
13
  class CompiledKernel
13
14
 
14
15
  # @!attribute [r] source
@@ -54,6 +55,12 @@ class CArray
54
55
  @rank = analyzer.rank
55
56
  @index_names = analyzer.index_names
56
57
  @written_arrays = analyzer.written_arrays
58
+ # Which operands the sweep writes, in CArray's spelling: one character
59
+ # per operand. Decided by the body, so spelled once rather than on
60
+ # every call.
61
+ @fsync = @arrays.map { |name|
62
+ @written_arrays.include?(name) ? "1" : "0"
63
+ }.join.freeze
57
64
  @array_ranks = analyzer.array_ranks
58
65
  @inner_ranges = analyzer.inner_ranges
59
66
  @unbounded_indices = analyzer.unbounded_indices
@@ -126,9 +133,8 @@ class CArray
126
133
  @handle, @compiled = Compiler.build(generator.generate_slab(SLAB_NAME),
127
134
  CGenerator::FUNCTION_NAME,
128
135
  header: generator.provenance)
129
- @function = Fiddle::Function.new(@handle[CGenerator::FUNCTION_NAME],
130
- [Fiddle::TYPE_VOIDP] * 10,
131
- Fiddle::TYPE_VOID)
136
+ @function = Native.function(Native.address(@handle, CGenerator::FUNCTION_NAME),
137
+ [:pointer] * 10, :void, release_gvl: true)
132
138
  end
133
139
 
134
140
  # `bounds` is one [start, limit, step] per axis.
@@ -209,9 +215,6 @@ class CArray
209
215
  # arguments the kernel's own loop takes are the ones a chunk arrives as.
210
216
  def sweep (array_values, scalar_values, c_function_values = {})
211
217
  arrays = @arrays.map { |name| array_values.fetch(name) }
212
- fsync = @arrays.map { |name|
213
- @written_arrays.include?(name) ? "1" : "0"
214
- }.join
215
218
 
216
219
  error = [0].pack("l")
217
220
  reals = packed_reals(scalar_values)
@@ -233,12 +236,10 @@ class CArray
233
236
  # signature has the slots.
234
237
  masks = ("\0" * 8)
235
238
 
236
- held = [buffer(reals), buffer(integers), buffer(functions),
237
- buffer(data), masks, masks, error]
238
- error = in_one_block(held) { |context, slots|
239
- Sweep.call(slab_pointer, fsync, arrays, context)
240
- slots.last[0, error.bytesize]
241
- }
239
+ # The slab writes the error slot in `error` itself.
240
+ Sweep.run(slab_pointer, @fsync, arrays,
241
+ buffer(reals), buffer(integers), buffer(functions),
242
+ buffer(data), masks, masks, error)
242
243
 
243
244
  packed.each_with_index do |(array, item), index|
244
245
  next if array.equal?(item)
@@ -332,7 +333,7 @@ class CArray
332
333
  @shape_scalar_names.map { |name| scalars[name] }]
333
334
  end
334
335
 
335
- # A kernel is shared, and Fiddle lets go of the GVL for the call, so two
336
+ # A kernel is shared, and its call lets go of the GVL, so two
336
337
  # threads can be reading one plan while a third prepares another. That
337
338
  # is sound only while nothing in a plan is written after it is published
338
339
  # -- and a box is arrays inside an array, so freezing the top of it
@@ -456,7 +457,7 @@ class CArray
456
457
  end
457
458
 
458
459
  def slab_pointer
459
- @slab_pointer ||= @handle[SLAB_NAME]
460
+ @slab_pointer ||= Native.address(@handle, SLAB_NAME)
460
461
  end
461
462
 
462
463
  # Resolved when the frame is first walked rather than at build time: a
@@ -464,8 +465,8 @@ class CArray
464
465
  # runs its interior first.
465
466
  def border_function
466
467
  @border_function ||=
467
- Fiddle::Function.new(@handle[CGenerator::BORDER_NAME],
468
- [Fiddle::TYPE_VOIDP] * 10, Fiddle::TYPE_VOID)
468
+ Native.function(Native.address(@handle, CGenerator::BORDER_NAME),
469
+ [:pointer] * 10, :void, release_gvl: true)
469
470
  end
470
471
 
471
472
  # What a C function may be handed as a pointer, and what has to be true
@@ -523,7 +524,7 @@ class CArray
523
524
 
524
525
  def address_buffer (name, array)
525
526
  @address_parameters.fetch(name).each do |parameter|
526
- wanted = CDeclaration::DATA_TYPES.fetch(parameter.element.fiddle)
527
+ wanted = CDeclaration::DATA_TYPES.fetch(parameter.element.abi)
527
528
  unless array.data_type_name == wanted.to_s
528
529
  raise Unsupported,
529
530
  "`#{name}` is handed to a C function as `#{parameter.text}`, " \
@@ -553,37 +554,12 @@ class CArray
553
554
  Access.classify(array)[:entity] ? array : array.copy
554
555
  end
555
556
 
556
- # A pointer into an empty string is not something Fiddle can hand over,
557
- # and a kernel with no scalars has an empty buffer.
557
+ # A pointer into an empty string is not something Native is asked to
558
+ # hand over, and a kernel with no scalars has an empty buffer.
558
559
  def buffer (packed)
559
560
  packed.empty? ? "\0" * 8 : packed
560
561
  end
561
562
 
562
- # Copies `parts` into one block of C memory, each part 8-byte aligned,
563
- # after a table of their addresses, and yields a pointer to the table
564
- # and a pointer to each part; the block is freed when this returns.
565
- #
566
- # For a call that is handed addresses inside other memory rather than
567
- # arguments: a Ruby String's bytes may be moved by GC compaction, and
568
- # an address taken from one and packed elsewhere is not one Ruby knows
569
- # to keep in place. Memory from malloc is never moved.
570
- def in_one_block (parts)
571
- table = parts.size * Fiddle::SIZEOF_VOIDP
572
- offsets = parts.each_with_object([]) { |part, list|
573
- last = list.empty? ? table : list.last + parts[list.size - 1].bytesize
574
- list << ((last + 7) & ~7)
575
- }
576
- size = offsets.last + parts.last.bytesize
577
- Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) do |block|
578
- slots = parts.zip(offsets).map { |part, offset|
579
- block[offset, part.bytesize] = part
580
- block + offset
581
- }
582
- block[0, table] = slots.map(&:to_i).pack("Q*")
583
- yield block, slots
584
- end
585
- end
586
-
587
563
  # The xfer tier transfers a box rather than the whole view. The box
588
564
  # is the loop range grown by how far the kernel reaches from the cell it
589
565
  # is on -- per array, because two arrays in one kernel need not be read
@@ -0,0 +1,149 @@
1
+ require "fiddle"
2
+ require "fiddle/import"
3
+ require "carray/jit/errors"
4
+
5
+ class CArray
6
+ module JIT
7
+
8
+ # Everything this library asks of the layer that reaches C from Ruby, and
9
+ # the one place that layer is named. The rest of the library says what
10
+ # it wants -- open this object, find this symbol, make a function of
11
+ # these types, read these bytes -- in the words below, and only this
12
+ # file knows that Fiddle is what answers.
13
+ #
14
+ # That is so the layer can be replaced by changing one file. Fiddle does
15
+ # not let a call be made from a Ractor other than the main one, and a
16
+ # layer that does would be a second version of this module rather than a
17
+ # second version of the library (devel/PROPOSAL_FFI_SEAM.md).
18
+ #
19
+ # What is asked:
20
+ #
21
+ # - a shared object opened, and the libraries already loaded searched
22
+ # - a symbol's address found in one
23
+ # - a function made from an address and its types, and called
24
+ # - bytes read and written at an address
25
+ # - a Ruby object handed to C as the VALUE it is
26
+ #
27
+ # A function is called by calling what #function returns: its #call is
28
+ # the layer's own, so nothing stands between a kernel and the C beyond
29
+ # what the layer itself costs. The arguments it takes for a `:pointer`
30
+ # are an Integer address, a String -- whose bytes are what C is handed --
31
+ # or nil.
32
+ # @private
33
+ module Native
34
+
35
+ # The C types a function is made with, by this library's names for
36
+ # them. `:pointer` is any pointer; a function's arguments say what it
37
+ # points at only to the C, which is all that needs to know.
38
+ TYPES = {
39
+ void: Fiddle::TYPE_VOID,
40
+ pointer: Fiddle::TYPE_VOIDP,
41
+ char: Fiddle::TYPE_CHAR,
42
+ uchar: Fiddle::TYPE_UCHAR,
43
+ short: Fiddle::TYPE_SHORT,
44
+ ushort: Fiddle::TYPE_USHORT,
45
+ int: Fiddle::TYPE_INT,
46
+ uint: Fiddle::TYPE_UINT,
47
+ long: Fiddle::TYPE_LONG,
48
+ ulong: Fiddle::TYPE_ULONG,
49
+ long_long: Fiddle::TYPE_LONG_LONG,
50
+ ulong_long: Fiddle::TYPE_ULONG_LONG,
51
+ float: Fiddle::TYPE_FLOAT,
52
+ double: Fiddle::TYPE_DOUBLE,
53
+ }.freeze
54
+
55
+ # The other way round, for what Fiddle's parser answers. Every code it
56
+ # gives for a spelling CDeclaration hands it is one of the fourteen
57
+ # above: `size_t` and the other platform words arrive as the code of
58
+ # whichever of these they are on this machine.
59
+ NAMES = TYPES.invert.freeze
60
+ private_constant :NAMES
61
+
62
+ POINTER_SIZE = Fiddle::SIZEOF_VOIDP
63
+
64
+ # What a library is, for a caller that was handed one. `jit_extern`'s
65
+ # `from:` takes one of these as it stands.
66
+ Library = Fiddle::Handle
67
+
68
+ # Raised when a library will not open or a symbol is not in it.
69
+ LinkError = Fiddle::DLError
70
+
71
+ PARSER = ::Object.new.extend(Fiddle::CParser)
72
+ private_constant :PARSER
73
+
74
+ # Opens a shared object. `global:` is whether its symbols join the
75
+ # process-wide namespace -- RTLD_GLOBAL -- and it is a required word
76
+ # because layers differ on what they do unasked: Fiddle opens globally,
77
+ # and a layer that opened locally would change which definition a name
78
+ # in one generated object resolves to (see Compiler's link flags).
79
+ # Fiddle has no word for the other way, and nothing here asks for it.
80
+ def self.open_library (path, global:)
81
+ raise ArgumentError, "Fiddle opens a library only globally" unless global
82
+ Fiddle::Handle.new(path, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
83
+ end
84
+
85
+ # What the process has already loaded, searched as one library.
86
+ def self.loaded_libraries
87
+ Fiddle::Handle::DEFAULT
88
+ end
89
+
90
+ # The address of `name` in `library`, as an Integer.
91
+ def self.address (library, name)
92
+ library[name]
93
+ end
94
+
95
+ # A function at `address` taking `arguments` and returning `result`,
96
+ # each one of the names in TYPES.
97
+ #
98
+ # `release_gvl:` is whether the GVL is let go for the length of a call,
99
+ # and it is a required word because layers differ on what they do
100
+ # unasked -- Fiddle lets it go, FFI keeps it. Letting it go is right
101
+ # for C that touches no Ruby value, which is every generated kernel; it
102
+ # is wrong for C that does, which is CArray's own sweep.
103
+ def self.function (address, arguments, result, release_gvl:, name: nil)
104
+ types = arguments.map { |type| TYPES.fetch(type) }
105
+ options = name ? { name: name } : {}
106
+ return Fiddle::Function.new(address, types, TYPES.fetch(result), **options) if release_gvl
107
+ begin
108
+ Fiddle::Function.new(address, types, TYPES.fetch(result),
109
+ need_gvl: true, **options)
110
+ rescue ArgumentError
111
+ # A Fiddle too old to be told. It is also too old to have let go
112
+ # of the GVL in the first place -- the keyword and the releasing
113
+ # arrived together -- so what is left here is what that Fiddle
114
+ # would have done anyway.
115
+ Fiddle::Function.new(address, types, TYPES.fetch(result), **options)
116
+ end
117
+ end
118
+
119
+ # The type a C spelling is on this machine, as a name in TYPES, or nil
120
+ # where it is not one. The spelling is one CDeclaration has already
121
+ # put in order: `unsigned long` rather than `long unsigned`, `void *`
122
+ # for any pointer.
123
+ def self.c_type (spelling)
124
+ NAMES.fetch(PARSER.parse_ctype(spelling))
125
+ rescue StandardError
126
+ nil
127
+ end
128
+
129
+ # `size` bytes at `address`, as a String. Fiddle::Pointer.read and
130
+ # .write are not in the fiddle Ruby 3.2 ships (1.1.1).
131
+ def self.read (address, size)
132
+ Fiddle::Pointer.new(address)[0, size]
133
+ end
134
+
135
+ # `bytes` written at `address`.
136
+ def self.write (address, bytes)
137
+ Fiddle::Pointer.new(address)[0, bytes.bytesize] = bytes
138
+ end
139
+
140
+ # `object` as the VALUE C sees -- for CArray's own C, which takes the
141
+ # array itself rather than its memory.
142
+ def self.value (object)
143
+ Fiddle.dlwrap(object)
144
+ end
145
+
146
+ end
147
+
148
+ end
149
+ end
@@ -1,4 +1,4 @@
1
- require "fiddle"
1
+ require "carray"
2
2
 
3
3
  class CArray
4
4
  module JIT
@@ -19,64 +19,6 @@ class CArray
19
19
  # as saying it is the form a sweep can drive.
20
20
  module Sweep
21
21
 
22
- # The sweep is reached by address, the way this library reaches every
23
- # other C function: CArray's extension is already loaded, so the symbol
24
- # is there to be found rather than linked against.
25
- def self.handle
26
- return @handle if defined?(@handle)
27
- path = $LOADED_FEATURES.find { |feature|
28
- File.basename(feature) =~ /\Acarray_ext\.(so|bundle|dylib)\z/
29
- }
30
- @handle = path && begin
31
- Fiddle::Handle.new(path)
32
- rescue Fiddle::DLError
33
- nil
34
- end
35
- end
36
-
37
- # True when this CArray is new enough to have the chunked slab family.
38
- def self.available?
39
- return @available if defined?(@available)
40
- @available = !handle.nil? && begin
41
- handle["ca_call_cslab_1_r"]
42
- true
43
- rescue Fiddle::DLError
44
- false
45
- end
46
- end
47
-
48
- # ca_call_cslab_N_r(func, fsync, rcx0..rcxN-1, userdata)
49
- #
50
- # `need_gvl: true` is not a detail. Fiddle releases the GVL for a call
51
- # unless it is told otherwise, which is right for every other function
52
- # this library reaches -- a generated kernel is pure C, touches no Ruby
53
- # value and is the better for running while other threads do. This one
54
- # is not that: it is CArray's own C, and acquiring the operands attaches
55
- # them, allocates with `xmalloc`, creates a mask where an output needs
56
- # one and raises on a shape that will not broadcast. All of that is the
57
- # Ruby runtime's, and none of it may be done without the GVL. The
58
- # kernel it calls back into is still pure C, but the GVL is held for the
59
- # whole call rather than the callback, because Fiddle's choice is made
60
- # once at the boundary and there is nowhere inside to make it again.
61
- def self.function (arity)
62
- @functions ||= {}
63
- @functions[arity] ||=
64
- begin
65
- types = [Fiddle::TYPE_VOIDP] * (arity + 3)
66
- begin
67
- Fiddle::Function.new(handle["ca_call_cslab_#{arity}_r"], types,
68
- Fiddle::TYPE_VOIDP, need_gvl: true)
69
- rescue ArgumentError
70
- # A Fiddle too old to be told. It is also too old to have let
71
- # go of the GVL in the first place -- the keyword and the
72
- # releasing arrived together -- so what is left here is what
73
- # that Fiddle would have done anyway.
74
- Fiddle::Function.new(handle["ca_call_cslab_#{arity}_r"], types,
75
- Fiddle::TYPE_VOIDP)
76
- end
77
- end
78
- end
79
-
80
22
  # The largest arity the family was generated for. Whether a pass can
81
23
  # go this way at all is decided in CArray::JIT.sweepable_pass?, before
82
24
  # there is a kernel -- because the answer changes what is compiled.
@@ -84,11 +26,19 @@ class CArray
84
26
 
85
27
  # `arrays` are the operands in the order the kernel addresses them, and
86
28
  # `fsync` says which of them it writes -- CArray's own spelling, one
87
- # character per operand.
88
- def self.call (slab, fsync, arrays, context)
89
- function(arrays.size).call(slab, fsync,
90
- *arrays.map { |array| Fiddle.dlwrap(array) },
91
- context)
29
+ # character per operand. `parts` become the table the slab is handed
30
+ # as `userdata`, in order; one that is not frozen is the slab's to
31
+ # write.
32
+ #
33
+ # CArray makes the call, and points the slab at the Strings' own bytes:
34
+ # they arrive as arguments, and an argument is pinned against
35
+ # compaction for the length of the call. It holds the GVL throughout,
36
+ # unlike every other call this library makes into C. Acquiring the
37
+ # operands attaches them, allocates and raises on a shape that will not
38
+ # broadcast, none of which may be done without it, and the kernel is
39
+ # called back from inside, where there is nowhere to let go of it.
40
+ def self.run (slab, fsync, arrays, *parts)
41
+ CArray::AddressBasis.sweep(slab, fsync, arrays, *parts)
92
42
  end
93
43
 
94
44
  end
@@ -1,5 +1,5 @@
1
1
  class CArray
2
2
  module JIT
3
- VERSION = "0.1.5"
3
+ VERSION = "0.1.7"
4
4
  end
5
5
  end
data/lib/carray/jit.rb CHANGED
@@ -315,9 +315,9 @@ class CArray
315
315
  # CArray.jit_each { out = j0.call(x) }
316
316
  #
317
317
  # Nothing is compiled here: `extern` is C's word for a body that lives
318
- # elsewhere, and finding it is Fiddle's job. What this gem adds is that the
319
- # kernel calls the address directly instead of reaching it per cell through
320
- # Fiddle.
318
+ # elsewhere, and finding it is the dynamic loader's job. What this gem adds
319
+ # is that the kernel calls the address directly instead of reaching it per
320
+ # cell through a foreign-function call from Ruby.
321
321
  #
322
322
  # @param prototype [String] the function's C declaration, as C writes it.
323
323
  # @param from [String, nil] the library to open; `nil` searches the process.
@@ -905,7 +905,6 @@ class CArray
905
905
  # that every operand already has the shape the pass covers, which is
906
906
  # stricter than agreeing on the count of cells.
907
907
  def sweepable_pass? (arrays, shape, masked)
908
- return false unless Sweep.available?
909
908
  return false if masked
910
909
  return false if arrays.empty? || arrays.size > Sweep::MAX_ARITY
911
910
  return false unless arrays.each_value.all? { |array|
@@ -1526,6 +1525,16 @@ class CArray
1526
1525
  written = written_names.filter_map { |name| aligned[name] }
1527
1526
  return aligned if written.empty?
1528
1527
  roots = written.map { |array| root_of(array) }
1528
+ # Asked first without building anything, because the answer is nearly
1529
+ # always that nothing is shared, and this runs on every call. The
1530
+ # root is compared first since it is what nearly always says no.
1531
+ shared = aligned.any? { |name, array|
1532
+ next false if written_names.include?(name)
1533
+ root = root_of(array)
1534
+ roots.any? { |one| one.equal?(root) } &&
1535
+ written.none? { |one| one.equal?(array) }
1536
+ }
1537
+ return aligned unless shared
1529
1538
  aligned.to_h do |name, array|
1530
1539
  next [name, array] if written_names.include?(name)
1531
1540
  next [name, array] if written.any? { |other| other.equal?(array) }
@@ -1645,21 +1654,23 @@ class CArray
1645
1654
  end
1646
1655
  pairs = bounds(extents, extents.size)
1647
1656
  steps = pairs.map { |triple, _| triple[2] }
1657
+ reassociate = JIT.reassociate if reassociate.nil?
1658
+
1659
+ # Which kernel runs, and whether any operand would be transferred,
1660
+ # answer the same for two calls whose operands agree on name, type,
1661
+ # mask and tier -- so they are worked out once per such agreement,
1662
+ # as an element-wise pass does (#run_over_whole_arrays).
1663
+ key = indexed_run_key(arrays, scalars, c_functions, randoms, steps,
1664
+ into, reassociate)
1665
+ plan = key && found_run_plan(source, key)
1666
+ unless plan
1667
+ plan = plan_indexed_run(source, node, origin, arrays, scalars,
1668
+ c_functions, randoms, steps, into,
1669
+ reassociate)
1670
+ keep_run_plan(source, key, plan) if key
1671
+ end
1648
1672
 
1649
- kernel = compile(source,
1650
- node: node,
1651
- origin: origin,
1652
- array_names: arrays.keys,
1653
- storage_types: arrays.transform_values(&:data_type_name),
1654
- scalar_values: scalars,
1655
- c_functions: c_functions,
1656
- randoms: randoms,
1657
- masked: arrays.each_value.any? { |array| array.has_mask? },
1658
- steps: steps,
1659
- cell_names: cell_names(arrays),
1660
- map: into ? true : false,
1661
- result: into ? INIT_TARGET : nil,
1662
- reassociate: reassociate.nil? ? JIT.reassociate : reassociate)
1673
+ kernel = plan.kernel
1663
1674
  # The kernel decides, not the caller: mentioning UNDEF makes it a
1664
1675
  # masked kernel even when no array carries a mask yet.
1665
1676
  if kernel.masked
@@ -1675,11 +1686,77 @@ class CArray
1675
1686
  "#{pairs.size} #{pairs.size == 1 ? 'extent was' : 'extents were'} " \
1676
1687
  "given"
1677
1688
  end
1678
- refuse_a_transferred_alias(kernel, arrays)
1689
+ # Only an operand opened at the xfer tier can be refused, and the tier
1690
+ # is in the key; which arrays share a root is not, and is asked here.
1691
+ refuse_a_transferred_alias(kernel, arrays) if plan.transferred
1679
1692
  kernel.call(arrays, scalars, pairs.map(&:first), c_functions)
1680
1693
  into || kernel
1681
1694
  end
1682
1695
 
1696
+ # What an indexed pass works out from its operands without their data
1697
+ # or their shapes: the kernel, and whether any operand is opened at the
1698
+ # xfer tier. The shapes are the kernel's own plan to keep, since they
1699
+ # are what its bounds are checked against (CompiledKernel#call).
1700
+ #
1701
+ # @private
1702
+ IndexedRunPlan = Data.define(:kernel, :transferred)
1703
+
1704
+ # What two calls of one indexed block have to agree on for one to run
1705
+ # the other's kernel: what #compile is keyed on, read off the operands
1706
+ # rather than built. Each operand's name and type, whether it is a
1707
+ # CScalar (read at its one cell rather than walked) and whether it has
1708
+ # a mask; and its tier, because whether a transferred operand has to be
1709
+ # refused depends on it. Not its shape: the kernel is the same over
1710
+ # any shape, and a caller that grows its arrays would miss on every
1711
+ # call. Not a scalar's value, for the reason #run_key gives.
1712
+ #
1713
+ # The leading symbol keeps these apart from an element-wise pass's
1714
+ # keys, which share the store.
1715
+ #
1716
+ # nil where a scalar is not one a kernel takes, so that the call is
1717
+ # refused where and as it always was rather than here.
1718
+ def indexed_run_key (arrays, scalars, c_functions, randoms, steps, into,
1719
+ reassociate)
1720
+ key = [:indexed, into ? true : false, reassociate, steps, arrays.size]
1721
+ arrays.each do |name, array|
1722
+ key << name << array.data_type << array.is_a?(CScalar) <<
1723
+ array.has_mask? << Access.tier(array)
1724
+ end
1725
+ key << scalars.size
1726
+ scalars.each do |name, value|
1727
+ key << name << TypeAssignment.scalar_type(value)
1728
+ end
1729
+ key << c_functions.size
1730
+ c_functions.each { |name, function| key << name << function.kernel_key }
1731
+ randoms.each { |name, generator| key << name << generator.generator }
1732
+ key
1733
+ rescue Unsupported
1734
+ nil
1735
+ end
1736
+
1737
+ # @private
1738
+ def plan_indexed_run (source, node, origin, arrays, scalars, c_functions,
1739
+ randoms, steps, into, reassociate)
1740
+ kernel = compile(source,
1741
+ node: node,
1742
+ origin: origin,
1743
+ array_names: arrays.keys,
1744
+ storage_types: arrays.transform_values(&:data_type_name),
1745
+ scalar_values: scalars,
1746
+ c_functions: c_functions,
1747
+ randoms: randoms,
1748
+ masked: arrays.each_value.any? { |array| array.has_mask? },
1749
+ steps: steps,
1750
+ cell_names: cell_names(arrays),
1751
+ map: into ? true : false,
1752
+ result: into ? INIT_TARGET : nil,
1753
+ reassociate: reassociate)
1754
+ transferred = arrays.each_value.any? { |array|
1755
+ Access.tier(array) == Access::TIER_XFER
1756
+ }
1757
+ IndexedRunPlan.new(kernel: kernel, transferred: transferred)
1758
+ end
1759
+
1683
1760
  # Compiles for one set of array data types and one set of scalar types,
1684
1761
  # and returns the same CompiledKernel for every later call with the
1685
1762
  # same ones. Nothing on this path is cheap relative to running the
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carray-jit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - himotoyoshi
@@ -15,7 +15,7 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 3.0.4
18
+ version: 3.0.6
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '3.1'
@@ -25,7 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 3.0.4
28
+ version: 3.0.6
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '3.1'
@@ -35,14 +35,14 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: '0'
38
+ version: 1.1.1
39
39
  type: :runtime
40
40
  prerelease: false
41
41
  version_requirements: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: '0'
45
+ version: 1.1.1
46
46
  description: |
47
47
  Write the loop in Ruby, and it runs at the speed of C -- one to two orders
48
48
  of magnitude faster than interpreted. It is for what a CArray expression
@@ -125,6 +125,7 @@ files:
125
125
  - lib/carray/jit/errors.rb
126
126
  - lib/carray/jit/expression.rb
127
127
  - lib/carray/jit/kernel.rb
128
+ - lib/carray/jit/native.rb
128
129
  - lib/carray/jit/node.rb
129
130
  - lib/carray/jit/sorting_networks.rb
130
131
  - lib/carray/jit/sweep.rb
@@ -148,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
149
  - !ruby/object:Gem::Version
149
150
  version: '0'
150
151
  requirements: []
151
- rubygems_version: 4.0.17
152
+ rubygems_version: 4.0.8
152
153
  specification_version: 4
153
154
  summary: JIT compilation of CArray kernels written in Ruby
154
155
  test_files: []