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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +771 -3
- data/README.md +7 -6
- data/carray-jit.gemspec +1 -3
- data/docs/00_Introduction.md +4 -3
- data/docs/01_GettingStarted.md +1 -1
- data/docs/02_KernelShapes.md +93 -14
- data/docs/03_SupportedFeatures.md +582 -26
- data/docs/04_Compiling.md +33 -6
- data/docs/05_DesignNotes.md +3 -3
- data/docs/06_Cheatsheet.md +198 -5
- data/docs/07_StepByStep.ja.md +534 -0
- data/docs/07_StepByStep.md +535 -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 +2077 -136
- data/lib/carray/jit/block_reader.rb +37 -6
- data/lib/carray/jit/c_function.rb +613 -76
- data/lib/carray/jit/c_generator.rb +1595 -156
- data/lib/carray/jit/call.rb +68 -0
- data/lib/carray/jit/compiler.rb +75 -11
- data/lib/carray/jit/kernel.rb +369 -32
- data/lib/carray/jit/node.rb +359 -9
- data/lib/carray/jit/sorting_networks.rb +182 -0
- data/lib/carray/jit/type_assignment.rb +371 -34
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +560 -64
- metadata +22 -8
- data/ext/carray_jit_access/carray_jit_access.c +0 -460
- data/ext/carray_jit_access/extconf.rb +0 -8
data/lib/carray/jit/node.rb
CHANGED
|
@@ -61,15 +61,24 @@ class CArray
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
class LocalRead < Node
|
|
64
|
-
#
|
|
65
|
-
# Integer at one point in the body and a Float
|
|
66
|
-
# cannot, so each type gets its own.
|
|
67
|
-
attr_accessor :
|
|
64
|
+
# Which of the local's variables this read resolves to, counted from 1.
|
|
65
|
+
# A Ruby local may hold an Integer at one point in the body and a Float
|
|
66
|
+
# at another; a C variable cannot, so each type gets its own.
|
|
67
|
+
attr_accessor :binding
|
|
68
|
+
# Set only on a read the generator makes of one partial accumulator of
|
|
69
|
+
# a split fold: which one.
|
|
70
|
+
attr_accessor :lane
|
|
68
71
|
attr_reader :name
|
|
69
72
|
def initialize (name, location = nil)
|
|
70
73
|
super(location)
|
|
71
74
|
@name = name
|
|
72
75
|
end
|
|
76
|
+
# What says two reads are of one variable: the name the block wrote and
|
|
77
|
+
# which of its bindings. Not a spelling -- the block may itself write a
|
|
78
|
+
# local spelled like another one's second binding.
|
|
79
|
+
def local
|
|
80
|
+
[@name, @binding]
|
|
81
|
+
end
|
|
73
82
|
end
|
|
74
83
|
|
|
75
84
|
# A scalar the block closed over; passed in as a kernel argument.
|
|
@@ -110,6 +119,40 @@ class CArray
|
|
|
110
119
|
end
|
|
111
120
|
end
|
|
112
121
|
|
|
122
|
+
# `x.clamp(low, high)`: the value, or whichever bound it ran past.
|
|
123
|
+
#
|
|
124
|
+
# Ruby hands back the receiver or one of the bounds, so which type the
|
|
125
|
+
# answer has is decided by the value -- which is why all three have to
|
|
126
|
+
# be the same type here, and why the answer is then the receiver's.
|
|
127
|
+
class Clamp < Node
|
|
128
|
+
attr_reader :value, :low, :high
|
|
129
|
+
def initialize (value, low, high, location = nil)
|
|
130
|
+
super(location)
|
|
131
|
+
@value = value
|
|
132
|
+
@low = low
|
|
133
|
+
@high = high
|
|
134
|
+
end
|
|
135
|
+
def children
|
|
136
|
+
[@value, @low, @high]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# `x.nan?` and `x.finite?`: a question about a number, answered true or
|
|
141
|
+
# false whatever the number's width. `infinite?` is not one of these --
|
|
142
|
+
# Ruby answers it with nil or 1 or -1, which is not a boolean and has no
|
|
143
|
+
# nil to be.
|
|
144
|
+
class NumericPredicate < Node
|
|
145
|
+
attr_reader :name, :operand
|
|
146
|
+
def initialize (name, operand, location = nil)
|
|
147
|
+
super(location)
|
|
148
|
+
@name = name
|
|
149
|
+
@operand = operand
|
|
150
|
+
end
|
|
151
|
+
def children
|
|
152
|
+
[@operand]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
113
156
|
# Zero of whatever type another expression has.
|
|
114
157
|
#
|
|
115
158
|
# A sum has to start from a zero of the summand's type: start it from an
|
|
@@ -145,12 +188,21 @@ class CArray
|
|
|
145
188
|
# Nothing is written through it: a kernel writes the cell its outer
|
|
146
189
|
# indices are on, and the inner loop is what runs within that cell.
|
|
147
190
|
class InnerLoop < Node
|
|
148
|
-
|
|
149
|
-
|
|
191
|
+
# `step` is the stride the loop counts by, and `to` is exclusive of it:
|
|
192
|
+
# a descending loop ends one below the last index it visits, as an
|
|
193
|
+
# extent written with `step` does.
|
|
194
|
+
attr_reader :index, :from, :to, :step, :statements
|
|
195
|
+
# The locals its block declares, as [name, binding, type], the arrays it
|
|
196
|
+
# declares, as [name, binding, storage, shape], and the ones that were
|
|
197
|
+
# live when the loop was entered, by name as [type, binding] -- all
|
|
198
|
+
# settled by the typing.
|
|
199
|
+
attr_accessor :declarations, :array_declarations, :entering
|
|
200
|
+
def initialize (index, from, to, statements, location = nil, step = 1)
|
|
150
201
|
super(location)
|
|
151
202
|
@index = index
|
|
152
203
|
@from = from
|
|
153
204
|
@to = to
|
|
205
|
+
@step = step
|
|
154
206
|
@statements = statements
|
|
155
207
|
end
|
|
156
208
|
def children
|
|
@@ -333,6 +385,41 @@ class CArray
|
|
|
333
385
|
end
|
|
334
386
|
end
|
|
335
387
|
|
|
388
|
+
# A draw from a captured CArray::Rng.
|
|
389
|
+
#
|
|
390
|
+
# The generator is not a function this kernel closed over -- nothing of
|
|
391
|
+
# it is compiled here. Its C arrives as text from CArray, is pasted into
|
|
392
|
+
# the preamble beside the helpers this compiler writes itself, and the
|
|
393
|
+
# draw is a call to it. So the sequence a kernel gets is the sequence
|
|
394
|
+
# `CArray#random!` gets, because it is the same code advancing the same
|
|
395
|
+
# cells and not a second implementation agreeing with the first.
|
|
396
|
+
#
|
|
397
|
+
# The state is an ordinary captured array under a name this compiler made
|
|
398
|
+
# up, handed over as an address the way any array a C function writes
|
|
399
|
+
# through is. That is what carries the sequence across the call: the
|
|
400
|
+
# cells the kernel advanced are the cells the generator still holds when
|
|
401
|
+
# it comes back.
|
|
402
|
+
class RandomDraw < Node
|
|
403
|
+
# The name the block reached the generator by, for a message to say.
|
|
404
|
+
attr_reader :generator
|
|
405
|
+
# The made-up name of its state array.
|
|
406
|
+
attr_reader :state
|
|
407
|
+
# What this draw answers: `:rand` a double in [0.0, 1.0), `:bits` the
|
|
408
|
+
# raw word the generator produced. Both advance the same state by one
|
|
409
|
+
# draw -- one step of the generator read two ways, not two sequences --
|
|
410
|
+
# so a kernel mixing them is still walking the one sequence.
|
|
411
|
+
attr_reader :kind
|
|
412
|
+
def initialize (generator, state, kind, location = nil)
|
|
413
|
+
super(location)
|
|
414
|
+
@generator = generator
|
|
415
|
+
@state = state
|
|
416
|
+
@kind = kind
|
|
417
|
+
end
|
|
418
|
+
def children
|
|
419
|
+
[]
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
336
423
|
# `p[i]` where `p` is a pointer parameter of a compiled function.
|
|
337
424
|
#
|
|
338
425
|
# Not an ElementRead: an array's cell is reached through a base and a
|
|
@@ -524,18 +611,49 @@ class CArray
|
|
|
524
611
|
end
|
|
525
612
|
|
|
526
613
|
class Assignment < Node
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
614
|
+
# Which of the local's variables this writes, as LocalRead#binding.
|
|
615
|
+
attr_accessor :binding
|
|
616
|
+
# Which scope the local belongs to, counted out from the kernel's block
|
|
617
|
+
# -- or a function's -- at 0, one more for each inner loop's block.
|
|
618
|
+
attr_reader :name, :expression, :scope
|
|
619
|
+
def initialize (name, expression, location = nil, scope = 0)
|
|
530
620
|
super(location)
|
|
531
621
|
@name = name
|
|
532
622
|
@expression = expression
|
|
623
|
+
@scope = scope
|
|
624
|
+
end
|
|
625
|
+
def local
|
|
626
|
+
[@name, @binding]
|
|
533
627
|
end
|
|
534
628
|
def children
|
|
535
629
|
[@expression]
|
|
536
630
|
end
|
|
537
631
|
end
|
|
538
632
|
|
|
633
|
+
# `prev, cur = cur, prev + cur`
|
|
634
|
+
#
|
|
635
|
+
# Ruby settles every value on the right before it writes any of them, so
|
|
636
|
+
# the temporaries below are the statement rather than a way of writing
|
|
637
|
+
# it: without them the line above would write `prev` and then read the
|
|
638
|
+
# new one back while working out `cur`, which is the one thing the
|
|
639
|
+
# spelling exists to prevent.
|
|
640
|
+
#
|
|
641
|
+
# Held as the statements it became -- one assignment to a name of its own
|
|
642
|
+
# per value, in the order they were written, and then the writes. So
|
|
643
|
+
# everything downstream reads ordinary assignments and writes: the types,
|
|
644
|
+
# the masks a local carries, the C names, the order the body is swept in.
|
|
645
|
+
# Nothing here has to know that a parallel assignment is what they were.
|
|
646
|
+
class ParallelAssignment < Node
|
|
647
|
+
attr_reader :statements
|
|
648
|
+
def initialize (statements, location = nil)
|
|
649
|
+
super(location)
|
|
650
|
+
@statements = statements
|
|
651
|
+
end
|
|
652
|
+
def children
|
|
653
|
+
@statements
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
|
|
539
657
|
# A cell of an array: out[i, j] = ...
|
|
540
658
|
#
|
|
541
659
|
# Writes are always at the cell the loop is on. Writing elsewhere would
|
|
@@ -558,8 +676,240 @@ class CArray
|
|
|
558
676
|
end
|
|
559
677
|
end
|
|
560
678
|
|
|
679
|
+
# `w = CArray.double(9)`, `CArray.new(:int64, [256])`, or
|
|
680
|
+
# `CArray.empty(:float64, [9])`: an array the body owns.
|
|
681
|
+
#
|
|
682
|
+
# It is declared at the head of the C block its Ruby scope stands for, as
|
|
683
|
+
# a local is, and lives on that block's stack. The zeroed spellings are
|
|
684
|
+
# cleared where the statement stands -- in Ruby a fresh array is made
|
|
685
|
+
# there each time the line runs, and the clearing is what says the same
|
|
686
|
+
# thing in C. `empty` says nothing there, and its cells are whatever the
|
|
687
|
+
# stack held.
|
|
688
|
+
#
|
|
689
|
+
# Not an ElementWrite or an Assignment. Those are tied to the operand
|
|
690
|
+
# tables -- the line-up, the dispatcher, the extents, the masks, the box a
|
|
691
|
+
# view is transferred in -- and this array is in none of them: the block
|
|
692
|
+
# wrote its shape, so its strides are constants and its bounds are settled
|
|
693
|
+
# either where the block is read or at the access. `PointerRead` stands
|
|
694
|
+
# apart for the same reason.
|
|
695
|
+
class LocalArrayDeclaration < Node
|
|
696
|
+
# Which of the name's variables this declares, as LocalRead#binding.
|
|
697
|
+
attr_accessor :binding
|
|
698
|
+
# `zeroed` is false for the `empty` spelling.
|
|
699
|
+
# `scope` is counted the way Assignment#scope is.
|
|
700
|
+
# `heap` is true where the array does not go on the stack: its shape is
|
|
701
|
+
# larger than a frame should hold, or is not known until the kernel
|
|
702
|
+
# runs. Then it is one allocation at the kernel's entry and a free at
|
|
703
|
+
# its exit, and the name is a pointer rather than an automatic array --
|
|
704
|
+
# which changes the declaration and nothing about how a cell is
|
|
705
|
+
# reached.
|
|
706
|
+
attr_reader :name, :storage, :shape, :zeroed, :scope, :heap
|
|
707
|
+
def initialize (name, storage, shape, zeroed, location = nil, scope = 0,
|
|
708
|
+
heap = false)
|
|
709
|
+
super(location)
|
|
710
|
+
@name = name
|
|
711
|
+
@storage = storage
|
|
712
|
+
@shape = shape
|
|
713
|
+
@zeroed = zeroed
|
|
714
|
+
@scope = scope
|
|
715
|
+
@heap = heap
|
|
716
|
+
end
|
|
717
|
+
def local
|
|
718
|
+
[@name, @binding]
|
|
719
|
+
end
|
|
720
|
+
# The shape's axes, as Nodes, for whatever has to walk them: an extent
|
|
721
|
+
# the block wrote as an expression is an expression like any other, and
|
|
722
|
+
# is typed and checked as one.
|
|
723
|
+
def children
|
|
724
|
+
@shape.filter_map { |extent|
|
|
725
|
+
extent.node if extent.is_a?(LocalArrayExtent)
|
|
726
|
+
}
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# `w[k]`, `w[j + 1]`, `w[counts[i]]`.
|
|
731
|
+
#
|
|
732
|
+
# `subscripts` holds one [index, offset] pair per axis, the shape
|
|
733
|
+
# ElementRead's take: an index with a literal offset where the reach was
|
|
734
|
+
# settled as the block was read, or [nil, expression] where the position
|
|
735
|
+
# is one only the running kernel knows and is checked there.
|
|
736
|
+
class LocalArrayRead < Node
|
|
737
|
+
attr_accessor :binding
|
|
738
|
+
attr_reader :name, :storage, :shape, :subscripts
|
|
739
|
+
def initialize (name, storage, shape, subscripts, location = nil)
|
|
740
|
+
super(location)
|
|
741
|
+
@name = name
|
|
742
|
+
@storage = storage
|
|
743
|
+
@shape = shape
|
|
744
|
+
@subscripts = subscripts
|
|
745
|
+
end
|
|
746
|
+
def local
|
|
747
|
+
[@name, @binding]
|
|
748
|
+
end
|
|
749
|
+
def children
|
|
750
|
+
@subscripts.filter_map { |_index, offset| offset if offset.is_a?(Node) }
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
class LocalArrayWrite < Node
|
|
755
|
+
attr_accessor :binding
|
|
756
|
+
attr_reader :name, :storage, :shape, :subscripts, :expression
|
|
757
|
+
def initialize (name, storage, shape, subscripts, expression,
|
|
758
|
+
location = nil)
|
|
759
|
+
super(location)
|
|
760
|
+
@name = name
|
|
761
|
+
@storage = storage
|
|
762
|
+
@shape = shape
|
|
763
|
+
@subscripts = subscripts
|
|
764
|
+
@expression = expression
|
|
765
|
+
end
|
|
766
|
+
def local
|
|
767
|
+
[@name, @binding]
|
|
768
|
+
end
|
|
769
|
+
def children
|
|
770
|
+
[@expression] +
|
|
771
|
+
@subscripts.filter_map { |_index, offset| offset if offset.is_a?(Node) }
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
# One axis of a local array whose length the block wrote as an
|
|
776
|
+
# expression over captured integers rather than as a number.
|
|
777
|
+
#
|
|
778
|
+
# Not a Node: it stands in a shape, where every other entry is an
|
|
779
|
+
# Integer, and what a shape is asked is how long an axis is -- not what
|
|
780
|
+
# type it computes in. `text` is what the block wrote, which is what
|
|
781
|
+
# makes two of these the same extent: the expression is worked out at the
|
|
782
|
+
# kernel's entry, so two declarations that wrote the same thing are one C
|
|
783
|
+
# array, exactly as two that wrote the same number are.
|
|
784
|
+
class LocalArrayExtent
|
|
785
|
+
attr_reader :text, :node
|
|
786
|
+
def initialize (text, node)
|
|
787
|
+
@text = text
|
|
788
|
+
@node = node
|
|
789
|
+
end
|
|
790
|
+
def == (other)
|
|
791
|
+
other.is_a?(LocalArrayExtent) && other.text == @text
|
|
792
|
+
end
|
|
793
|
+
alias eql? ==
|
|
794
|
+
def hash
|
|
795
|
+
@text.hash
|
|
796
|
+
end
|
|
797
|
+
def to_s
|
|
798
|
+
@text
|
|
799
|
+
end
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
# `w[k] = UNDEF`: marks the cell missing and leaves its bytes alone.
|
|
803
|
+
#
|
|
804
|
+
# Beside LocalArrayWrite for the reason MaskWrite stands beside
|
|
805
|
+
# ElementWrite: UNDEF is a mark rather than a value, so there is no
|
|
806
|
+
# expression here to compute or to store.
|
|
807
|
+
class LocalArrayMaskWrite < Node
|
|
808
|
+
attr_accessor :binding
|
|
809
|
+
attr_reader :name, :storage, :shape, :subscripts
|
|
810
|
+
def initialize (name, storage, shape, subscripts, location = nil)
|
|
811
|
+
super(location)
|
|
812
|
+
@name = name
|
|
813
|
+
@storage = storage
|
|
814
|
+
@shape = shape
|
|
815
|
+
@subscripts = subscripts
|
|
816
|
+
end
|
|
817
|
+
def local
|
|
818
|
+
[@name, @binding]
|
|
819
|
+
end
|
|
820
|
+
def children
|
|
821
|
+
@subscripts.filter_map { |_index, offset| offset if offset.is_a?(Node) }
|
|
822
|
+
end
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
# `w[k] == UNDEF`, and `!=` with `negated`: reads the shadow byte beside
|
|
826
|
+
# the cell rather than the cell.
|
|
827
|
+
class LocalArrayMaskTest < Node
|
|
828
|
+
attr_accessor :binding
|
|
829
|
+
attr_reader :name, :storage, :shape, :subscripts, :negated
|
|
830
|
+
def initialize (name, storage, shape, subscripts, negated, location = nil)
|
|
831
|
+
super(location)
|
|
832
|
+
@name = name
|
|
833
|
+
@storage = storage
|
|
834
|
+
@shape = shape
|
|
835
|
+
@subscripts = subscripts
|
|
836
|
+
@negated = negated
|
|
837
|
+
end
|
|
838
|
+
def local
|
|
839
|
+
[@name, @binding]
|
|
840
|
+
end
|
|
841
|
+
def children
|
|
842
|
+
@subscripts.filter_map { |_index, offset| offset if offset.is_a?(Node) }
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
# `sum(w)`, `min(w)`, `max(w)`: a value worked out over a local array.
|
|
847
|
+
#
|
|
848
|
+
# `name` is the array's, not the function's, so that a read of the array's
|
|
849
|
+
# binding is the same question here as it is for LocalArrayRead -- the C
|
|
850
|
+
# name a helper is handed comes from the same place. `intrinsic` is which
|
|
851
|
+
# function was written.
|
|
852
|
+
class IntrinsicCall < Node
|
|
853
|
+
attr_accessor :binding
|
|
854
|
+
attr_reader :intrinsic, :name, :storage, :shape
|
|
855
|
+
def initialize (intrinsic, name, storage, shape, location = nil)
|
|
856
|
+
super(location)
|
|
857
|
+
@intrinsic = intrinsic
|
|
858
|
+
@name = name
|
|
859
|
+
@storage = storage
|
|
860
|
+
@shape = shape
|
|
861
|
+
end
|
|
862
|
+
def local
|
|
863
|
+
[@name, @binding]
|
|
864
|
+
end
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
# `sort(w)`: rearranges a local array where it stands. A statement and
|
|
868
|
+
# never a value -- what it did is in the array, which is the same bargain
|
|
869
|
+
# a call to a `void` function makes.
|
|
870
|
+
class IntrinsicStatement < Node
|
|
871
|
+
attr_accessor :binding
|
|
872
|
+
attr_reader :intrinsic, :name, :storage, :shape
|
|
873
|
+
def initialize (intrinsic, name, storage, shape, location = nil)
|
|
874
|
+
super(location)
|
|
875
|
+
@intrinsic = intrinsic
|
|
876
|
+
@name = name
|
|
877
|
+
@storage = storage
|
|
878
|
+
@shape = shape
|
|
879
|
+
end
|
|
880
|
+
def local
|
|
881
|
+
[@name, @binding]
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
# A local array standing where a C function wants a pointer.
|
|
886
|
+
#
|
|
887
|
+
# Not `ArrayAddress`, which is an operand's: that one is spelled by
|
|
888
|
+
# `bare_name` and is entered in `@address_arrays`, the list of arrays the
|
|
889
|
+
# caller packs and hands over through `data[]`. This array is already
|
|
890
|
+
# here, on the stack of the block that made it, so it is spelled by the
|
|
891
|
+
# name its declaration was written under and the caller has nothing to
|
|
892
|
+
# pack.
|
|
893
|
+
class LocalArrayAddress < Node
|
|
894
|
+
attr_accessor :binding
|
|
895
|
+
attr_reader :name, :storage, :shape
|
|
896
|
+
def initialize (name, storage, shape, location = nil)
|
|
897
|
+
super(location)
|
|
898
|
+
@name = name
|
|
899
|
+
@storage = storage
|
|
900
|
+
@shape = shape
|
|
901
|
+
end
|
|
902
|
+
def local
|
|
903
|
+
[@name, @binding]
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
|
|
561
907
|
class KernelBody < Node
|
|
562
908
|
attr_reader :statements
|
|
909
|
+
# The locals the kernel's block, or a function's, declares, as
|
|
910
|
+
# [name, binding, type], and the arrays it declares, as
|
|
911
|
+
# [name, binding, storage, shape].
|
|
912
|
+
attr_accessor :declarations, :array_declarations
|
|
563
913
|
def initialize (statements)
|
|
564
914
|
super(nil)
|
|
565
915
|
@statements = statements
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
class CArray
|
|
2
|
+
module JIT
|
|
3
|
+
|
|
4
|
+
# Comparator networks for the lengths a local array is sorted at without a
|
|
5
|
+
# loop: a fixed sequence of compare-exchange pairs, the same one every
|
|
6
|
+
# time, with no branch and nothing to predict.
|
|
7
|
+
#
|
|
8
|
+
# A network is a list of `[i, j]` with `i < j`, applied in order. Each
|
|
9
|
+
# pair says "put the smaller of these two channels in i and the larger in
|
|
10
|
+
# j"; run them all and the channels come out ordered, whatever they held.
|
|
11
|
+
# That is the whole of the interface -- the generator turns each pair into
|
|
12
|
+
# one compare-exchange in C, and what decides which pair comes first is
|
|
13
|
+
# nothing but this list.
|
|
14
|
+
#
|
|
15
|
+
# ## Where these came from
|
|
16
|
+
#
|
|
17
|
+
# The sizes are the smallest known for each length, and for n <= 12 they
|
|
18
|
+
# are known to be optimal (Codish, Cruz-Filipe, Frank and Schneider-Kamp,
|
|
19
|
+
# "Twenty-Five Comparators Is Optimal When Sorting Nine Inputs (and
|
|
20
|
+
# Twenty-Nine for Ten)", 2014, for 9 and 10; the smaller ones are in
|
|
21
|
+
# Knuth, *The Art of Computer Programming*, vol. 3, section 5.3.4). For
|
|
22
|
+
# 13 to 16 they are the best known rather than proven optimal; the
|
|
23
|
+
# 16-input network with 60 comparators is Green's, given in Knuth's
|
|
24
|
+
# section 5.3.4 and figure 51.
|
|
25
|
+
#
|
|
26
|
+
# | n | comparators | n | comparators |
|
|
27
|
+
# |---|---|---|---|
|
|
28
|
+
# | 2 | 1 | 10 | 29 |
|
|
29
|
+
# | 3 | 3 | 11 | 35 |
|
|
30
|
+
# | 4 | 5 | 12 | 39 |
|
|
31
|
+
# | 5 | 9 | 13 | 45 |
|
|
32
|
+
# | 6 | 12 | 14 | 51 |
|
|
33
|
+
# | 7 | 16 | 15 | 56 |
|
|
34
|
+
# | 8 | 19 | 16 | 60 |
|
|
35
|
+
# | 9 | 25 | | |
|
|
36
|
+
#
|
|
37
|
+
# ## Why they can be trusted
|
|
38
|
+
#
|
|
39
|
+
# A table of pairs is exactly the kind of data that is wrong in one place
|
|
40
|
+
# and silently almost-right, so none of it is taken on the word of a
|
|
41
|
+
# citation. Every network here is checked by the **zero-one principle**:
|
|
42
|
+
# a comparator network sorts every input if and only if it sorts every
|
|
43
|
+
# sequence of zeros and ones. For n channels that is 2**n sequences --
|
|
44
|
+
# 65536 at the largest length here -- and `test/test_sorting_networks.rb`
|
|
45
|
+
# runs all of them for all fifteen lengths, along with the comparator
|
|
46
|
+
# count and the shape of each pair. A network that is wrong fails there
|
|
47
|
+
# rather than in somebody's median filter.
|
|
48
|
+
#
|
|
49
|
+
# ## Pruning
|
|
50
|
+
#
|
|
51
|
+
# Where one of these was derived from a longer one, it was derived by
|
|
52
|
+
# pruning: set the last input to positive infinity and every comparator
|
|
53
|
+
# touching that channel becomes a no-op on the others, so deleting them
|
|
54
|
+
# leaves a network that sorts the rest. 14 and 15 are the 16-input
|
|
55
|
+
# network pruned that way, and 9 is the 10-input one pruned. The others
|
|
56
|
+
# are written out.
|
|
57
|
+
module SortingNetworks
|
|
58
|
+
|
|
59
|
+
# The longest length that gets a network. Above it the generator emits
|
|
60
|
+
# an insertion sort, which is a loop: the tables would keep growing and
|
|
61
|
+
# the win over a loop shrinks as the length rises, there being more
|
|
62
|
+
# comparators than the machine has registers long before this.
|
|
63
|
+
LONGEST = 16
|
|
64
|
+
|
|
65
|
+
# Green's 16-input network, 60 comparators. 14 and 15 are pruned from
|
|
66
|
+
# it below, which is where their 51 and 56 come from.
|
|
67
|
+
GREEN_16 = [
|
|
68
|
+
[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13], [14, 15],
|
|
69
|
+
[0, 2], [4, 6], [8, 10], [12, 14], [1, 3], [5, 7], [9, 11], [13, 15],
|
|
70
|
+
[0, 4], [8, 12], [1, 5], [9, 13], [2, 6], [10, 14], [3, 7], [11, 15],
|
|
71
|
+
[0, 8], [1, 9], [2, 10], [3, 11], [4, 12], [5, 13], [6, 14], [7, 15],
|
|
72
|
+
[5, 10], [6, 9], [3, 12], [13, 14], [7, 11], [1, 2], [4, 8],
|
|
73
|
+
[1, 4], [7, 13], [2, 8], [11, 14],
|
|
74
|
+
[5, 6], [9, 10],
|
|
75
|
+
[2, 4], [11, 13], [3, 8], [7, 12],
|
|
76
|
+
[6, 8], [10, 12], [3, 5], [7, 9],
|
|
77
|
+
[3, 4], [5, 6], [7, 8], [9, 10], [11, 12],
|
|
78
|
+
[6, 7], [8, 9],
|
|
79
|
+
].freeze
|
|
80
|
+
|
|
81
|
+
# The 10-input network, 29 comparators. 9 is pruned from it.
|
|
82
|
+
TEN = [
|
|
83
|
+
[0, 8], [1, 9], [2, 7], [3, 5], [4, 6],
|
|
84
|
+
[0, 2], [1, 4], [5, 8], [7, 9],
|
|
85
|
+
[0, 3], [2, 4], [5, 7], [6, 9],
|
|
86
|
+
[0, 1], [3, 6], [8, 9],
|
|
87
|
+
[1, 5], [2, 3], [4, 8], [6, 7],
|
|
88
|
+
[1, 2], [3, 5], [4, 6], [7, 8],
|
|
89
|
+
[2, 3], [4, 5], [6, 7],
|
|
90
|
+
[3, 4], [5, 6],
|
|
91
|
+
].freeze
|
|
92
|
+
|
|
93
|
+
# Sets the channels at or above `channels` to positive infinity, which
|
|
94
|
+
# makes every comparator that touches one a no-op on the rest.
|
|
95
|
+
def self.prune (network, channels)
|
|
96
|
+
network.reject { |low, high| low >= channels || high >= channels }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
NETWORKS = {
|
|
100
|
+
2 => [[0, 1]],
|
|
101
|
+
|
|
102
|
+
3 => [[0, 2], [0, 1], [1, 2]],
|
|
103
|
+
|
|
104
|
+
4 => [[0, 1], [2, 3], [0, 2], [1, 3], [1, 2]],
|
|
105
|
+
|
|
106
|
+
5 => [[0, 1], [3, 4], [2, 4], [2, 3], [1, 4],
|
|
107
|
+
[0, 3], [0, 2], [1, 3], [1, 2]],
|
|
108
|
+
|
|
109
|
+
6 => [[1, 2], [4, 5], [0, 2], [3, 5], [0, 1], [3, 4],
|
|
110
|
+
[2, 5], [0, 3], [1, 4], [2, 4], [1, 3], [2, 3]],
|
|
111
|
+
|
|
112
|
+
7 => [[1, 2], [3, 4], [5, 6], [0, 2], [3, 5], [4, 6],
|
|
113
|
+
[0, 1], [4, 5], [2, 6], [0, 4], [1, 5], [0, 3],
|
|
114
|
+
[2, 5], [1, 3], [2, 4], [2, 3]],
|
|
115
|
+
|
|
116
|
+
# Batcher's odd-even merge, which at this length is also the smallest.
|
|
117
|
+
8 => [[0, 2], [1, 3], [4, 6], [5, 7],
|
|
118
|
+
[0, 4], [1, 5], [2, 6], [3, 7],
|
|
119
|
+
[0, 1], [2, 3], [4, 5], [6, 7],
|
|
120
|
+
[2, 4], [3, 5],
|
|
121
|
+
[1, 4], [3, 6],
|
|
122
|
+
[1, 2], [3, 4], [5, 6]],
|
|
123
|
+
|
|
124
|
+
9 => prune(TEN, 9),
|
|
125
|
+
|
|
126
|
+
10 => TEN,
|
|
127
|
+
|
|
128
|
+
11 => [[0, 9], [1, 6], [2, 4], [3, 7], [5, 8],
|
|
129
|
+
[0, 1], [3, 5], [4, 10], [6, 9], [7, 8],
|
|
130
|
+
[1, 3], [2, 5], [4, 7], [8, 10],
|
|
131
|
+
[0, 4], [1, 2], [3, 7], [5, 9], [6, 8],
|
|
132
|
+
[0, 1], [2, 6], [4, 5], [7, 8], [9, 10],
|
|
133
|
+
[2, 4], [3, 6], [5, 7], [8, 9],
|
|
134
|
+
[1, 2], [3, 4], [5, 6], [7, 8],
|
|
135
|
+
[2, 3], [4, 5], [6, 7]],
|
|
136
|
+
|
|
137
|
+
12 => [[0, 8], [1, 7], [2, 6], [3, 11], [4, 10], [5, 9],
|
|
138
|
+
[0, 1], [2, 5], [3, 4], [6, 9], [7, 8], [10, 11],
|
|
139
|
+
[0, 2], [1, 6], [5, 10], [9, 11],
|
|
140
|
+
[0, 3], [1, 2], [4, 6], [5, 7], [8, 11], [9, 10],
|
|
141
|
+
[1, 4], [3, 5], [6, 8], [7, 10],
|
|
142
|
+
[1, 3], [2, 5], [6, 9], [8, 10],
|
|
143
|
+
[2, 3], [4, 5], [6, 7], [8, 9],
|
|
144
|
+
[4, 6], [5, 7],
|
|
145
|
+
[3, 4], [5, 6], [7, 8]],
|
|
146
|
+
|
|
147
|
+
13 => [[0, 12], [1, 10], [2, 9], [3, 7], [5, 11], [6, 8],
|
|
148
|
+
[1, 6], [2, 3], [4, 11], [7, 9], [8, 10],
|
|
149
|
+
[0, 4], [1, 2], [3, 6], [7, 8], [9, 10], [11, 12],
|
|
150
|
+
[4, 6], [5, 9], [8, 11], [10, 12],
|
|
151
|
+
[0, 5], [3, 8], [4, 7], [6, 11], [9, 10],
|
|
152
|
+
[0, 1], [2, 5], [6, 9], [7, 8], [10, 11],
|
|
153
|
+
[1, 3], [2, 4], [5, 6], [9, 10],
|
|
154
|
+
[1, 2], [3, 4], [5, 7], [6, 8],
|
|
155
|
+
[2, 3], [4, 5], [6, 7], [8, 9],
|
|
156
|
+
[3, 4], [5, 6]],
|
|
157
|
+
|
|
158
|
+
14 => prune(GREEN_16, 14),
|
|
159
|
+
|
|
160
|
+
15 => prune(GREEN_16, 15),
|
|
161
|
+
|
|
162
|
+
16 => GREEN_16,
|
|
163
|
+
}.each_value(&:freeze).freeze
|
|
164
|
+
|
|
165
|
+
# The comparator count per length, which the tests assert and the docs
|
|
166
|
+
# quote. Derived rather than written down a second time.
|
|
167
|
+
SIZES = NETWORKS.transform_values(&:size).freeze
|
|
168
|
+
|
|
169
|
+
# The network for `length`, or nil where there is none -- length 1 is
|
|
170
|
+
# already sorted and needs no comparator, and anything above LONGEST
|
|
171
|
+
# gets a loop instead.
|
|
172
|
+
def self.for (length)
|
|
173
|
+
NETWORKS[length]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def self.covers? (length)
|
|
177
|
+
length == 1 || NETWORKS.key?(length)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|