Module: CArray::Fusion

Defined in:
lib/carray/fusion.rb

Defined Under Namespace

Classes: Const, Leaf, Op, Plan, Refused, Walk

Constant Summary collapse

LAZY_CLASSES =
[CAMonOp, CABinOp, CATriOp, CAMonCmp, CABinCmp, CALazyMarker]
BINOP_NAMES =

A lazy node names its operation by an id and the kernels name it by a symbol. These are the same operations, spelled the way each side spells them; the rest are spelled alike.

{
  :+  => :add,        :-  => :sub,        :*  => :mul,
  :/  => :div,        :** => :power,      :%  => :mod,
  :&  => :bit_and_i,  :|  => :bit_or_i,   :^  => :bit_xor_i,
  :<< => :bit_lshift, :>> => :bit_rshift,
}.freeze
TRIOP_NAMES =
{ :__clip_ki__ => :clip }.freeze
MONOP_BY_ID =
CArray::LAZY_MONOP_OP_IDS.invert.freeze
BINOP_BY_ID =
CArray::LAZY_BINOP_OP_IDS.invert.freeze
TRIOP_BY_ID =
CArray::LAZY_TRIOP_OP_IDS.invert.freeze
TRAPPING =

Integer division and its relatives raise on a zero divisor, so a cell the mask excludes must not be computed at all -- the divisor there is nobody's business (ca_binop_dispatch.c).

%i[div mod quo_i fmod].freeze
INTEGERS =
%i[int8 int16 int32 int64 uint8 uint16 uint32 uint64].freeze
THRESHOLD =

Reaching a compiled kernel costs about the same whatever the array's size, and what it buys is the passes the walk would make. Below this the walk is the faster answer. The crossing moves with how wide the expression is -- measured, thirty thousand cells at one operation, six thousand at six -- and this brackets those: a one-operation expression loses a couple of microseconds here, a six-operation one wins ten.

10_000

Class Method Summary collapse

Class Method Details

.askable?(view) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/carray/fusion.rb', line 102

def self.askable? (view)
  ! CArray.expression_evaluator.nil? && view.elements >= THRESHOLD
end

.build(view) ⇒ Object

Raises:



114
115
116
117
118
119
120
# File 'lib/carray/fusion.rb', line 114

def self.build (view)
  raise Refused, "not a lazy expression" unless lazy?(view)
  w = Walk.new
  w.visit(view)
  Plan.new(w.nodes, w.leaves, view.data_type, view.dim,
           w.leaves.any? { |a| a.has_mask? }, w.signature)
end

.evaluate(view) ⇒ Object

Returns the array, or nil where nothing computed it.



76
77
78
79
80
# File 'lib/carray/fusion.rb', line 76

def self.evaluate (view)
  return nil unless askable?(view)
  out = CArray.__alloc_uninit__(view.data_type, view.dim)
  evaluate_into(view, out) ? out : nil
end

.evaluate_into(view, out) ⇒ Object

Fills an array the caller already has. Called from the store as well, where making one and copying it over would be most of the work. Returns true when something computed it.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/carray/fusion.rb', line 85

def self.evaluate_into (view, out)
  evaluator = CArray.expression_evaluator or return false
  return false unless askable?(view)
  plan = plan(view) or return false
  # A marker over an array, or anything else with nothing to compute,
  # is not worth handing over.
  return false unless plan.nodes.any? { |n| n.is_a?(Op) }
  out.mask = 0 if plan.masked && ! out.has_mask?
  evaluator.call(plan, out) ? true : false
rescue StandardError => error
  CArray.expression_evaluator = nil
  warn "CArray: the registered expression evaluator raised " \
       "(#{error.class}: #{error.message}); expressions will be walked " \
       "from here on"
  false
end

.lazy?(x) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/carray/fusion.rb', line 122

def self.lazy? (x)
  LAZY_CLASSES.any? { |k| x.is_a?(k) }
end

.plan(view) ⇒ Object

Returns a Plan, or nil where the expression holds something a plan cannot describe. Refusing is ordinary: the caller walks instead.



108
109
110
111
112
# File 'lib/carray/fusion.rb', line 108

def self.plan (view)
  build(view)
rescue Refused
  nil
end