carray 3.0.5 → 3.0.6
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 +233 -0
- data/README.md +1 -1
- data/carray.gemspec +3 -2
- data/ext/ca_axis_dispatch.c +6 -0
- data/ext/ca_axis_group.c +1 -1
- data/ext/ca_categorical_iterator.c +7 -7
- data/ext/ca_kernel_iterator.c +116 -231
- data/ext/ca_kernel_iterator.h +410 -254
- data/ext/ca_obj_bincmp.c +9 -1
- data/ext/ca_obj_binop.c +10 -1
- data/ext/ca_obj_bitfield.c +2 -0
- data/ext/ca_obj_byte_swap.c +2 -0
- data/ext/ca_obj_face.c +2 -0
- data/ext/ca_obj_fake.c +3 -1
- data/ext/ca_obj_grid.c +167 -12
- data/ext/ca_obj_meld.c +69 -113
- data/ext/ca_obj_moncmp.c +9 -1
- data/ext/ca_obj_monop.c +40 -2
- data/ext/ca_obj_object.c +54 -20
- data/ext/ca_obj_reduce.c +15 -1
- data/ext/ca_obj_refer.c +65 -20
- data/ext/ca_obj_remap.c +69 -10
- data/ext/ca_obj_roll.c +1 -0
- data/ext/ca_obj_select.c +4 -1
- data/ext/ca_obj_select_axis.c +1 -0
- data/ext/ca_obj_shift.c +1 -0
- data/ext/ca_obj_stack.c +310 -111
- data/ext/ca_obj_stride.c +131 -45
- data/ext/ca_obj_string.c +18 -2
- data/ext/ca_obj_tile.c +1 -0
- data/ext/ca_obj_triop.c +9 -1
- data/ext/ca_obj_window.c +1 -0
- data/ext/ca_op_ipower.c +8 -8
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +2 -1
- data/ext/carray_access.c +27 -64
- data/ext/carray_address_basis.c +134 -0
- data/ext/carray_attribute.c +8 -0
- data/ext/carray_broadcast.c +3 -4
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_cast.c +8 -11
- data/ext/carray_conversion.c +9 -5
- data/ext/carray_core.c +811 -72
- data/ext/carray_element.c +13 -9
- data/ext/carray_histogram.c +25 -3
- data/ext/carray_internal.h +147 -12
- data/ext/carray_kernels_bincmp.c +2007 -3
- data/ext/carray_kernels_binop.c +25365 -5199
- data/ext/carray_kernels_init.c +8 -8
- data/ext/carray_kernels_monop.c +10696 -3678
- data/ext/carray_kernels_reduce_aggregate.c +4429 -702
- data/ext/carray_kernels_reduce_boolean.c +1533 -15
- data/ext/carray_kernels_reduce_cumulative.c +1808 -248
- data/ext/carray_kernels_reduce_extreme.c +4508 -511
- data/ext/carray_kernels_reduce_variance.c +2284 -152
- data/ext/carray_kernels_scan.c +240 -240
- data/ext/carray_kernels_search.c +4211 -264
- data/ext/carray_kernels_sort.c +367 -450
- data/ext/carray_lazy.c +47 -0
- data/ext/carray_loop.c +1 -1
- data/ext/carray_mask.c +130 -139
- data/ext/carray_median_percentile.c +4 -4
- data/ext/carray_memory_view.c +13 -7
- data/ext/carray_operator.c +147 -44
- data/ext/carray_order.c +3 -2
- data/ext/carray_random.c +4 -1
- data/ext/carray_slab.c +97 -21
- data/ext/carray_sort.c +1 -1
- data/ext/carray_test.c +74 -19
- data/ext/carray_time_text.c +2 -2
- data/ext/extconf.rb +11 -0
- data/ext/mkkernel.rb +346 -94
- data/ext/ruby_carray.c +4 -1
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +25 -1
- data/lib/carray/autoload_carray.rb +3 -3
- data/lib/carray/autoload_method_extension.rb +5 -3
- data/lib/carray/axis_group.rb +13 -7
- data/lib/carray/basics.rb +1 -1
- data/lib/carray/block_iterator.rb +17 -3
- data/lib/carray/categorical.rb +6 -8
- data/lib/carray/categorical_iterator.rb +2 -2
- data/lib/carray/complex.rb +2 -2
- data/lib/carray/conditional.rb +3 -2
- data/lib/carray/frame/column_verbs.rb +45 -14
- data/lib/carray/frame/convert.rb +19 -0
- data/lib/carray/frame/csv_parser.rb +32 -32
- data/lib/carray/frame/describe.rb +4 -2
- data/lib/carray/frame/frame.rb +399 -78
- data/lib/carray/frame/group.rb +679 -53
- data/lib/carray/frame/io.rb +209 -71
- data/lib/carray/frame/join.rb +146 -37
- data/lib/carray/frame/records.rb +3 -3
- data/lib/carray/frame/reshape.rb +89 -11
- data/lib/carray/frame/sort.rb +57 -9
- data/lib/carray/frame/verbs.rb +384 -49
- data/lib/carray/frame/windows.rb +44 -6
- data/lib/carray/inspect.rb +7 -2
- data/lib/carray/iterator.rb +32 -0
- data/lib/carray/lazy.rb +17 -8
- data/lib/carray/mask_gap_fill.rb +37 -17
- data/lib/carray/meld_reduce.rb +1 -1
- data/lib/carray/methods/choose.rb +3 -2
- data/lib/carray/methods/meshgrid.rb +3 -1
- data/lib/carray/runtime.rb +47 -8
- data/lib/carray/segment_iterator.rb +23 -8
- data/lib/carray/serialize.rb +34 -17
- data/lib/carray/stack.rb +18 -6
- data/lib/carray/time.rb +5 -0
- data/lib/carray/window_iterator.rb +16 -4
- data/yard-stubs/ca_obj_array.rb +26 -1
- data/yard-stubs/carray_cast.rb +8 -5
- data/yard-stubs/carray_conversion.rb +8 -2
- data/yard-stubs/carray_mask.rb +20 -1
- data/yard-stubs/carray_math.rb +31 -16
- data/yard-stubs/carray_memory_view.rb +7 -0
- data/yard-stubs/carray_test.rb +7 -4
- metadata +18 -4
data/ext/mkkernel.rb
CHANGED
|
@@ -328,8 +328,8 @@ module MkKernel
|
|
|
328
328
|
# # :match_source (legacy) reads the weight at
|
|
329
329
|
# # the source type -- wrong for fractional
|
|
330
330
|
# # weights, kept only for DSL forward-compat.
|
|
331
|
-
# shape: :rev5_strict, # W-A1 scalar / W-
|
|
332
|
-
# #
|
|
331
|
+
# shape: :rev5_strict, # W-A1 scalar / W-A4 shape of the reduced axes /
|
|
332
|
+
# # W-A3 self.shape (:match_source = W-A3 only)
|
|
333
333
|
# mask: :overlay, # weights.mask OR'd into self.mask before
|
|
334
334
|
# # kernel call (= legacy parity, Q3 (A))
|
|
335
335
|
# }
|
|
@@ -362,6 +362,13 @@ module MkKernel
|
|
|
362
362
|
object_escape: nil,
|
|
363
363
|
identity_on_empty: false,
|
|
364
364
|
all_nan_result: nil,
|
|
365
|
+
undefined_if: nil,
|
|
366
|
+
# settled: a C expression over `acc` that is true once no
|
|
367
|
+
# further cell can change the result (any: "acc", all:
|
|
368
|
+
# "!acc"). The slab walk then stops at the first block
|
|
369
|
+
# where it holds (CA_SLAB_REDUCE_T_SETTLE). Only for a
|
|
370
|
+
# single-accumulator reduction without mask_policy.
|
|
371
|
+
settled: nil,
|
|
365
372
|
outputs: 1,
|
|
366
373
|
# Two-pass centred algorithm (variance / stddev family).
|
|
367
374
|
# When algorithm: :two_pass_centred is set, `state / init /
|
|
@@ -448,6 +455,10 @@ module MkKernel
|
|
|
448
455
|
raise "#{name}: identity_on_empty requires a single-accumulator reduction (sum/prod/count/accumulate/wsum); mean/variance/wmean/minmax are excluded" \
|
|
449
456
|
unless single_acc && outputs == 1
|
|
450
457
|
end
|
|
458
|
+
if settled
|
|
459
|
+
raise "#{name}: settled requires a single-accumulator reduction without mask_policy, value_arg or array_arg" \
|
|
460
|
+
unless state.nil? && mask_policy.nil? && value_arg.nil? && array_arg.nil? && outputs == 1
|
|
461
|
+
end
|
|
451
462
|
# SL.1.0: reduction_kind: licenses a SIMD-friendly contig-branch macro
|
|
452
463
|
# variant (CA_SLAB_REDUCE_T_{PLUS,MIN,MAX,STAR,VAR}_EX). default
|
|
453
464
|
# :none keeps the existing CA_SLAB_REDUCE_T_EX path (= unchanged
|
|
@@ -491,6 +502,14 @@ module MkKernel
|
|
|
491
502
|
raise "#{name}: all_nan_result: :undef needs a mask_policy " \
|
|
492
503
|
"(UNDEF is written through the mask_policy trigger)"
|
|
493
504
|
end
|
|
505
|
+
# undefined_if: a C expression over the state that, when true for a
|
|
506
|
+
# slab, leaves the answer undefined though cells contributed -- wmean's
|
|
507
|
+
# weights summing to 0, whose quotient has no value. It is folded into
|
|
508
|
+
# the mask_policy trigger, so the cell is UNDEF (or fill_value).
|
|
509
|
+
if undefined_if && mask_policy.nil?
|
|
510
|
+
raise "#{name}: undefined_if needs a mask_policy " \
|
|
511
|
+
"(UNDEF is written through the mask_policy trigger)"
|
|
512
|
+
end
|
|
494
513
|
if value_arg
|
|
495
514
|
raise "#{name}: value_arg must be a Hash" unless value_arg.is_a?(Hash)
|
|
496
515
|
raise "#{name}: value_arg: target must be :T_IN" unless value_arg[:target] == :T_IN
|
|
@@ -570,9 +589,9 @@ module MkKernel
|
|
|
570
589
|
raise "#{name}: array_arg: data_type must be :promote or :match_source" \
|
|
571
590
|
unless [:promote, :match_source].include?(array_arg[:data_type])
|
|
572
591
|
# PROPOSAL_REDUCTION_PER_FIBER_AUX_OPERAND rev1: shape acceptance expanded
|
|
573
|
-
# to W-A1 (scalar) / W-
|
|
592
|
+
# to W-A1 (scalar) / W-A4 (shape of the reduced axes) / W-A3 (self.shape).
|
|
574
593
|
# :match_source = legacy W-A3 only (kept for forward-compat in DSL).
|
|
575
|
-
# :rev5_strict = W-A1 + W-
|
|
594
|
+
# :rev5_strict = W-A1 + W-A4 + W-A3 acceptance (= new default for wsum/wmean).
|
|
576
595
|
raise "#{name}: array_arg: shape must be :match_source or :rev5_strict" \
|
|
577
596
|
unless [:match_source, :rev5_strict].include?(array_arg[:shape])
|
|
578
597
|
raise "#{name}: array_arg: mask must be :overlay (W.1 MVP)" \
|
|
@@ -652,6 +671,8 @@ module MkKernel
|
|
|
652
671
|
object_escape: object_escape,
|
|
653
672
|
identity_on_empty: identity_on_empty,
|
|
654
673
|
all_nan_result: all_nan_result,
|
|
674
|
+
undefined_if: undefined_if,
|
|
675
|
+
settled: settled,
|
|
655
676
|
outputs: outputs,
|
|
656
677
|
algorithm: algorithm,
|
|
657
678
|
divisor: divisor,
|
|
@@ -1057,11 +1078,14 @@ module MkKernel
|
|
|
1057
1078
|
# => ...` form).
|
|
1058
1079
|
#
|
|
1059
1080
|
# `bind:` defaults to true (= register CArray#<name> + CArray#<name>!).
|
|
1081
|
+
# `bang: false` leaves CArray#<name>! unregistered too, for a method whose
|
|
1082
|
+
# Ruby surface is defined by hand on top of the generated rb_ca_<name> and
|
|
1083
|
+
# rb_ca_<name>_bang.
|
|
1060
1084
|
# `op:` defaults to name (= the public method name; some mkmath ops
|
|
1061
1085
|
# use a different operator name from the kernel suffix, e.g. operator
|
|
1062
1086
|
# methods).
|
|
1063
|
-
def self.monop(name, source:, expr:, op: nil, bind: true,
|
|
1064
|
-
widening: nil, output: :preserve)
|
|
1087
|
+
def self.monop(name, source:, expr:, op: nil, bind: true, bang: true,
|
|
1088
|
+
cmath: true, widening: nil, output: :preserve)
|
|
1065
1089
|
op ||= name
|
|
1066
1090
|
# widening = true => emit wrapper that auto-casts integer input to
|
|
1067
1091
|
# CA_FLOAT64 (= mkmath monfunc behavior). Default: auto-detect based
|
|
@@ -1084,6 +1108,7 @@ module MkKernel
|
|
|
1084
1108
|
source: source,
|
|
1085
1109
|
expr: expr,
|
|
1086
1110
|
bind: bind,
|
|
1111
|
+
bang: bang,
|
|
1087
1112
|
cmath: cmath,
|
|
1088
1113
|
widening: widening,
|
|
1089
1114
|
output: output,
|
|
@@ -1095,8 +1120,10 @@ module MkKernel
|
|
|
1095
1120
|
# of integer input to f64 is decided by `monop`'s widening auto-detect
|
|
1096
1121
|
# based on whether `source:` includes integer data_types — matching mkmath's
|
|
1097
1122
|
# rule "if no INT_TYPES key, auto-cast; otherwise rely on integer kernel".
|
|
1098
|
-
def self.monfunc(name, source:, expr:, op: nil, bind: true,
|
|
1099
|
-
|
|
1123
|
+
def self.monfunc(name, source:, expr:, op: nil, bind: true, bang: true,
|
|
1124
|
+
cmath: true)
|
|
1125
|
+
monop(name, source: source, expr: expr, op: op, bind: bind, bang: bang,
|
|
1126
|
+
cmath: cmath)
|
|
1100
1127
|
end
|
|
1101
1128
|
|
|
1102
1129
|
# Register an alias of an existing monop method under a different
|
|
@@ -1358,6 +1385,12 @@ module MkKernel
|
|
|
1358
1385
|
when "_STAR" then "CA_SLAB_REDUCE_STAR8_EX"
|
|
1359
1386
|
end
|
|
1360
1387
|
acc_c = resolve_state_type(k[:state][acc_var], oi, si, src)
|
|
1388
|
+
# An 8- or 16-bit integer cell converted to double does not vectorize;
|
|
1389
|
+
# the narrow form sums blocks in int32 first.
|
|
1390
|
+
if macro8 == "CA_SLAB_REDUCE_SUM8_EX" && %i[i8 u8 i16 u16].include?(src) &&
|
|
1391
|
+
acc_c == DTYPES[:f64][:c]
|
|
1392
|
+
macro8 = "CA_SLAB_REDUCE_SUM8_NARROW_EX"
|
|
1393
|
+
end
|
|
1361
1394
|
expr_macro = "__#{k[:name]}_#{src}_EXPR"
|
|
1362
1395
|
io.puts "#define #{expr_macro}(__x) ((#{acc_c})(__x))"
|
|
1363
1396
|
io.puts "#{indent}#{macro8}(#{si[:c]}, #{acc_c}, st, p, m, #{acc_var}, #{acc_init}, #{expr_macro}, #{masked_cnt_var});"
|
|
@@ -1877,7 +1910,11 @@ module MkKernel
|
|
|
1877
1910
|
out.puts "{"
|
|
1878
1911
|
out.puts " #{frame} fr;"
|
|
1879
1912
|
params.each { |_, name| out.puts " fr.#{name} = #{name};" }
|
|
1880
|
-
|
|
1913
|
+
if params.any? { |type, name| name == "ca" && type.include?("CArray") } && fn.end_with?("_object")
|
|
1914
|
+
out.puts " return ca_iter_ensure_computing(ca, #{states.size}, fr.states, #{fn}_body, (VALUE) &fr);"
|
|
1915
|
+
else
|
|
1916
|
+
out.puts " return ca_iter_ensure(#{states.size}, fr.states, #{fn}_body, (VALUE) &fr);"
|
|
1917
|
+
end
|
|
1881
1918
|
out.puts "}"
|
|
1882
1919
|
out.print rest
|
|
1883
1920
|
out.string
|
|
@@ -2035,6 +2072,7 @@ module MkKernel
|
|
|
2035
2072
|
io.puts " if ( naxes != 1 || ! ca_too_few_present(ca->dim[slab_axes[0]], min_count, 0) ) {" if li_gate
|
|
2036
2073
|
emit_reduce_loop_interchange(io, k, si, oi, ruby_wrap, src,
|
|
2037
2074
|
acc_var, acc_init, reduce_stmt, finish_expr)
|
|
2075
|
+
emit_reduce_short_fiber(io, k, si, oi, src, reduce_stmt, finish_expr)
|
|
2038
2076
|
io.puts " }" if li_gate
|
|
2039
2077
|
end
|
|
2040
2078
|
|
|
@@ -2168,6 +2206,7 @@ module MkKernel
|
|
|
2168
2206
|
"#{k[:identity_on_empty] ? 0 : 1})"
|
|
2169
2207
|
end
|
|
2170
2208
|
trigger = "(#{trigger}) || #{all_nan_flag}" if all_nan_flag
|
|
2209
|
+
trigger = "(#{trigger}) || (#{k[:undefined_if]})" if k[:undefined_if]
|
|
2171
2210
|
finish_emit = view_flat \
|
|
2172
2211
|
? "(transform_active ? (outer_off + ((ca_size_t)(#{finish_expr})) * axis_vstride) : ((ca_size_t)(#{finish_expr})))" \
|
|
2173
2212
|
: "(#{finish_expr})"
|
|
@@ -2192,7 +2231,9 @@ module MkKernel
|
|
|
2192
2231
|
# Non-mask_policy path: the slab macros always take a masked_cnt,
|
|
2193
2232
|
# so hand them a throwaway. The generic form (suffix "", the
|
|
2194
2233
|
# :object lane) has a wrapper that hides it.
|
|
2195
|
-
if
|
|
2234
|
+
if k[:settled]
|
|
2235
|
+
io.puts " CA_SLAB_REDUCE_T_SETTLE(#{si[:c]}, st, p, m, #{acc_var}, #{acc_init}, #{reduce_stmt}, #{k[:settled]});"
|
|
2236
|
+
elsif suffix == ""
|
|
2196
2237
|
io.puts " CA_SLAB_REDUCE_T(#{si[:c]}, st, p, m, #{acc_var}, #{acc_init}, #{reduce_stmt});"
|
|
2197
2238
|
else
|
|
2198
2239
|
io.puts " ca_size_t __sr_throwaway_mc = 0;"
|
|
@@ -2540,8 +2581,16 @@ module MkKernel
|
|
|
2540
2581
|
expr_macro_defs = "#define #{p1_macro}(__x) #{p1_expr}\n" \
|
|
2541
2582
|
"#define #{p2_macro}(__x) #{p2_expr}\n"
|
|
2542
2583
|
expr_macro_undefs = "#undef #{p1_macro}\n#undef #{p2_macro}\n"
|
|
2543
|
-
|
|
2544
|
-
|
|
2584
|
+
if %i[i8 u8 i16 u16].include?(src)
|
|
2585
|
+
# Per-cell conversion to double does not vectorize for these widths
|
|
2586
|
+
# (see the CA_SLAB_REDUCE_*_NARROW_EX header comments).
|
|
2587
|
+
q_c = %i[i8 u8].include?(src) ? "int32_t" : "int64_t"
|
|
2588
|
+
p1_reduce_call = "CA_SLAB_REDUCE_SUM8_NARROW_EX(#{si[:c]}, #{sum_c}, st, p, m, sum, #{sum_init}, #{p1_macro}, masked_cnt);"
|
|
2589
|
+
p2_reduce_call = "CA_SLAB_REDUCE_M2_NARROW_EX(#{si[:c]}, #{q_c}, st, p, m, M2, #{m2_init}, mean, #{p2_macro}, __unused_mc);"
|
|
2590
|
+
else
|
|
2591
|
+
p1_reduce_call = "CA_SLAB_REDUCE_SUM8_EX(#{si[:c]}, #{sum_c}, st, p, m, sum, #{sum_init}, #{p1_macro}, masked_cnt);"
|
|
2592
|
+
p2_reduce_call = "CA_SLAB_REDUCE_SUM8_EX(#{si[:c]}, #{m2_c}, st, p, m, M2, #{m2_init}, #{p2_macro}, __unused_mc);"
|
|
2593
|
+
end
|
|
2545
2594
|
end
|
|
2546
2595
|
|
|
2547
2596
|
io.puts
|
|
@@ -2603,6 +2652,101 @@ module MkKernel
|
|
|
2603
2652
|
io.print expr_macro_undefs
|
|
2604
2653
|
end
|
|
2605
2654
|
|
|
2655
|
+
# Short-fiber fast path (shortcut short_fiber_reduce): a single-axis
|
|
2656
|
+
# reduction over the innermost axis of a row-major buffer whose fibers are
|
|
2657
|
+
# short. The general walk asks the kernel iterator for each fiber, and for
|
|
2658
|
+
# fibers of a few cells that per-fiber step costs more than the cells
|
|
2659
|
+
# (1M fibers of 4 float64 cells: 12.6 ms against 1.0 ms for a plain loop,
|
|
2660
|
+
# M2 Max). Here every fiber is folded in place from the buffer, in the
|
|
2661
|
+
# order the general walk folds it, into scalar accumulators. Eligibility
|
|
2662
|
+
# is the loop interchange's (same reduction kinds, no mask, an alias
|
|
2663
|
+
# attach); a fiber longer than 32 cells (16 for min / max) goes to the
|
|
2664
|
+
# general walk, which is already faster than this loop there.
|
|
2665
|
+
def self.emit_reduce_short_fiber(io, k, si, oi, src, reduce_stmt, finish_expr)
|
|
2666
|
+
buf_kinds = %i[plus star min max]
|
|
2667
|
+
rk = k[:reduction_kind]
|
|
2668
|
+
if buf_kinds.include?(rk)
|
|
2669
|
+
acc_vars = [k[:state].keys.first]
|
|
2670
|
+
induction_var = nil
|
|
2671
|
+
else
|
|
2672
|
+
acc_vars = k[:state].keys.select { |v| buf_kinds.include?(rk[v]) }
|
|
2673
|
+
ind_pair = rk.find { |_, kind| kind == :induction }
|
|
2674
|
+
induction_var = ind_pair && ind_pair[0]
|
|
2675
|
+
end
|
|
2676
|
+
|
|
2677
|
+
info = acc_vars.map do |v|
|
|
2678
|
+
{ var: v,
|
|
2679
|
+
c_type: resolve_state_type(k[:state][v], oi, si, src),
|
|
2680
|
+
init: resolve_init_expr(k[:init][v], oi, si, src) }
|
|
2681
|
+
end
|
|
2682
|
+
|
|
2683
|
+
reduce_sf = reduce_stmt.dup
|
|
2684
|
+
finish_sf = finish_expr.dup
|
|
2685
|
+
if induction_var
|
|
2686
|
+
ind_pat = Regexp.escape(induction_var.to_s)
|
|
2687
|
+
reduce_sf = reduce_sf.gsub(/\b#{ind_pat}\+\+/, "0")
|
|
2688
|
+
.gsub(/\+\+\b#{ind_pat}\b/, "0")
|
|
2689
|
+
finish_sf = finish_sf.gsub(/\b#{ind_pat}\b/, "((int64_t)__sf_M)")
|
|
2690
|
+
end
|
|
2691
|
+
|
|
2692
|
+
decls = info.map { |i| "#{i[:c_type]} #{i[:var]} = (#{i[:c_type]}) (#{i[:init]});" }
|
|
2693
|
+
.join("\n ")
|
|
2694
|
+
|
|
2695
|
+
# A float min / max whose accumulator is still at the init saw only NaN
|
|
2696
|
+
# or only +/-INFINITY; the fiber is re-read to tell which, as the
|
|
2697
|
+
# general walk's all-NaN rule answers NaN for the first.
|
|
2698
|
+
nan_fixup =
|
|
2699
|
+
if k[:all_nan_result] == :nan && FLOAT_DTYPES.include?(src) && info.size == 1
|
|
2700
|
+
i = info.first
|
|
2701
|
+
<<~C.rstrip
|
|
2702
|
+
if ( #{i[:var]} == (#{i[:c_type]}) (#{i[:init]}) ) {
|
|
2703
|
+
int __sf_seen = 0;
|
|
2704
|
+
for ( ca_size_t __j = 0; __j < __sf_M; __j++ ) __sf_seen |= (__sf_row[__j] == __sf_row[__j]);
|
|
2705
|
+
if ( ! __sf_seen ) #{i[:var]} = (#{i[:c_type]}) NAN;
|
|
2706
|
+
}
|
|
2707
|
+
C
|
|
2708
|
+
else
|
|
2709
|
+
""
|
|
2710
|
+
end
|
|
2711
|
+
|
|
2712
|
+
# A min / max fold is a compare per cell that the general walk splits
|
|
2713
|
+
# eight ways; past 16 cells that split is faster than this loop.
|
|
2714
|
+
max_m = info.any? { |i| %i[min max].include?(buf_kinds.include?(rk) ? rk : rk[i[:var]]) } ? 16 : 32
|
|
2715
|
+
|
|
2716
|
+
io.puts <<~C
|
|
2717
|
+
if ( naxes == 1
|
|
2718
|
+
&& ca->ndim >= 2
|
|
2719
|
+
&& slab_axes[0] == (int8_t)(ca->ndim - 1)
|
|
2720
|
+
&& ca->dim[ca->ndim - 1] >= 1
|
|
2721
|
+
&& ca->dim[ca->ndim - 1] <= #{max_m}
|
|
2722
|
+
&& ! ca_has_mask(ca)
|
|
2723
|
+
&& ca_attach_is_alias(ca)
|
|
2724
|
+
&& ca_fast_path(CA_SC_SHORT_FIBER_REDUCE) ) {
|
|
2725
|
+
ca_size_t __sf_M = ca->dim[ca->ndim - 1];
|
|
2726
|
+
ca_size_t __sf_OUTER = ca->elements / __sf_M;
|
|
2727
|
+
VALUE vout = rb_ca_new_reduced(self, slab_axes, naxes, #{oi[:ca]}, keep_axis);
|
|
2728
|
+
CArray *co;
|
|
2729
|
+
GetCArray(vout, co);
|
|
2730
|
+
#{oi[:c]} *op = (#{oi[:c]} *) co->ptr;
|
|
2731
|
+
ca_attach(ca); /* window: numeric lanes only, nothing raises inside; O(1) alias attach */
|
|
2732
|
+
const #{si[:c]} *p_root = (const #{si[:c]} *) ca->ptr;
|
|
2733
|
+
for ( ca_size_t __sf_o = 0; __sf_o < __sf_OUTER; __sf_o++ ) {
|
|
2734
|
+
const #{si[:c]} *__sf_row = p_root + __sf_o * __sf_M;
|
|
2735
|
+
#{decls}
|
|
2736
|
+
for ( ca_size_t __j = 0; __j < __sf_M; __j++ ) {
|
|
2737
|
+
#{si[:c]} v = __sf_row[__j];
|
|
2738
|
+
#{reduce_sf};
|
|
2739
|
+
(void) v;
|
|
2740
|
+
}
|
|
2741
|
+
#{nan_fixup}
|
|
2742
|
+
op[__sf_o] = (#{oi[:c]}) (#{finish_sf});
|
|
2743
|
+
}
|
|
2744
|
+
ca_detach(ca);
|
|
2745
|
+
return vout;
|
|
2746
|
+
}
|
|
2747
|
+
C
|
|
2748
|
+
end
|
|
2749
|
+
|
|
2606
2750
|
def self.emit_reduce_loop_interchange(io, k, si, oi, ruby_wrap, src,
|
|
2607
2751
|
acc_var, acc_init, reduce_stmt, finish_expr)
|
|
2608
2752
|
# Classify state vars by reduction_kind:
|
|
@@ -2752,7 +2896,7 @@ module MkKernel
|
|
|
2752
2896
|
&& slab_axes[0] != (int8_t)(ca->ndim - 1)
|
|
2753
2897
|
&& ! ca_has_mask(ca)
|
|
2754
2898
|
&& ca_attach_is_alias(ca)
|
|
2755
|
-
&&
|
|
2899
|
+
&& ca_fast_path(CA_SC_LOOP_INTERCHANGE) ) {
|
|
2756
2900
|
int8_t __li_ax = slab_axes[0];
|
|
2757
2901
|
ca_size_t __li_M = ca->dim[__li_ax];
|
|
2758
2902
|
ca_size_t __li_INNER = 1;
|
|
@@ -2803,7 +2947,7 @@ module MkKernel
|
|
|
2803
2947
|
&& ! ca_has_mask(ca)
|
|
2804
2948
|
&& ca_func[ca->obj_type].attach == ca_stack_func.attach
|
|
2805
2949
|
&& ((CAStack *) ca)->k_axis == 0
|
|
2806
|
-
&&
|
|
2950
|
+
&& ca_fast_path(CA_SC_STACK_LOOP_INTERCHANGE) ) {
|
|
2807
2951
|
CAStack *__li_st = (CAStack *) ca;
|
|
2808
2952
|
int8_t __li_ax = slab_axes[0];
|
|
2809
2953
|
ca_size_t __li_M = ca->dim[__li_ax];
|
|
@@ -2831,7 +2975,7 @@ module MkKernel
|
|
|
2831
2975
|
ca_size_t __li_parent_out = __li_OUTER * __li_INNER;
|
|
2832
2976
|
const ca_size_t __li_TILE = 512;
|
|
2833
2977
|
for ( int32_t __kk = 0; __kk < __li_st->n_parents; __kk++ ) {
|
|
2834
|
-
ca_attach(__li_st->parents[__kk]); /* window:
|
|
2978
|
+
ca_attach(__li_st->parents[__kk]); /* window: a parent may run Ruby to fill itself (a CAObject's copy_data); a raising attach holds nothing and the earlier parents are already detached, and the numeric loop after it does not raise */
|
|
2835
2979
|
const #{si[:c]} *p_root = (const #{si[:c]} *) __li_st->parents[__kk]->ptr;
|
|
2836
2980
|
#{oi[:c]} *__li_op_k = op + (ca_size_t) __kk * __li_parent_out;
|
|
2837
2981
|
#{core.call("p_root", "__li_op_k", "__li_OUTER")}
|
|
@@ -2869,7 +3013,7 @@ module MkKernel
|
|
|
2869
3013
|
&& ((CAStack *) ca)->k_axis > 0
|
|
2870
3014
|
&& ( slab_axes[0] > ((CAStack *) ca)->k_axis
|
|
2871
3015
|
|| slab_axes[0] + 1 == ((CAStack *) ca)->k_axis )
|
|
2872
|
-
&&
|
|
3016
|
+
&& ca_fast_path(CA_SC_STACK_LOOP_INTERCHANGE) ) {
|
|
2873
3017
|
CAStack *__li_st = (CAStack *) ca;
|
|
2874
3018
|
int8_t __li_ax = slab_axes[0];
|
|
2875
3019
|
int8_t __li_kax = (int8_t) __li_st->k_axis;
|
|
@@ -2926,7 +3070,7 @@ module MkKernel
|
|
|
2926
3070
|
ca_size_t __li_plane_elems = __li_M * __li_INNER;
|
|
2927
3071
|
const ca_size_t __li_TILE = 512;
|
|
2928
3072
|
for ( int32_t __kk = 0; __kk < __li_st->n_parents; __kk++ ) {
|
|
2929
|
-
ca_attach(__li_st->parents[__kk]); /* window:
|
|
3073
|
+
ca_attach(__li_st->parents[__kk]); /* window: a parent may run Ruby to fill itself (a CAObject's copy_data); a raising attach holds nothing and the earlier parents are already detached, and the numeric loop after it does not raise */
|
|
2930
3074
|
const #{si[:c]} *p_root = (const #{si[:c]} *) __li_st->parents[__kk]->ptr;
|
|
2931
3075
|
ca_size_t __li_k_base = (ca_size_t) __kk * __li_k_stride;
|
|
2932
3076
|
|
|
@@ -3012,7 +3156,7 @@ module MkKernel
|
|
|
3012
3156
|
&& ca_func[ca->obj_type].attach == ca_stack_func.attach
|
|
3013
3157
|
&& ((CAStack *) ca)->k_axis > 0
|
|
3014
3158
|
&& slab_axes[0] + 1 < ((CAStack *) ca)->k_axis
|
|
3015
|
-
&&
|
|
3159
|
+
&& ca_fast_path(CA_SC_STACK_LOOP_INTERCHANGE) ) {
|
|
3016
3160
|
CAStack *__li_st = (CAStack *) ca;
|
|
3017
3161
|
int8_t __li_ax = slab_axes[0];
|
|
3018
3162
|
int8_t __li_kax = (int8_t) __li_st->k_axis;
|
|
@@ -3084,7 +3228,7 @@ module MkKernel
|
|
|
3084
3228
|
|
|
3085
3229
|
const ca_size_t __li_TILE = 512;
|
|
3086
3230
|
for ( int32_t __kk = 0; __kk < __li_st->n_parents; __kk++ ) {
|
|
3087
|
-
ca_attach(__li_st->parents[__kk]); /* window:
|
|
3231
|
+
ca_attach(__li_st->parents[__kk]); /* window: a parent may run Ruby to fill itself (a CAObject's copy_data); a raising attach holds nothing and the earlier parents are already detached, and the numeric loop after it does not raise */
|
|
3088
3232
|
const #{si[:c]} *p_root = (const #{si[:c]} *) __li_st->parents[__kk]->ptr;
|
|
3089
3233
|
ca_size_t __li_k_base = (ca_size_t) __kk * __li_k_stride;
|
|
3090
3234
|
|
|
@@ -3394,7 +3538,7 @@ module MkKernel
|
|
|
3394
3538
|
io.puts " ca->elements > 0 &&"
|
|
3395
3539
|
io.puts " ! ca_too_few_present(ca->elements, min_count, 0) &&" if k[:mask_policy] == :min_count
|
|
3396
3540
|
io.puts " ca_is_lazy_view(ca) && ! ca_has_mask(ca) &&"
|
|
3397
|
-
io.puts "
|
|
3541
|
+
io.puts " ca_fast_path(CA_SC_LAZY_STREAMING_REDUCE) ) {"
|
|
3398
3542
|
io.puts " #{fn}_t __ctx;"
|
|
3399
3543
|
io.puts " __ctx.chunk = NULL;"
|
|
3400
3544
|
io.puts " __ctx.ca = ca;"
|
|
@@ -3513,12 +3657,12 @@ module MkKernel
|
|
|
3513
3657
|
# computation type (so a float weight against an int source is not
|
|
3514
3658
|
# truncated -- the body reads it as `double`); :match_source keeps the
|
|
3515
3659
|
# legacy source-type storage. waca is the CA_* constant the three
|
|
3516
|
-
# materialization sites (W-A1 / W-
|
|
3660
|
+
# materialization sites (W-A1 / W-A4 / W-A3) allocate/wrap against, and
|
|
3517
3661
|
# must agree with the T_W the reduce macro reads (double for :promote).
|
|
3518
3662
|
waca = (k[:array_arg][:data_type] == :promote) ? "CA_FLOAT64" : "src->data_type"
|
|
3519
3663
|
# PROPOSAL_REDUCTION_PER_FIBER_AUX_OPERAND rev1: weights acceptance
|
|
3520
|
-
# expanded to 3 forms (W-A1 scalar / W-
|
|
3521
|
-
# full per-element). W-A1 / W-
|
|
3664
|
+
# expanded to 3 forms (W-A1 scalar / W-A4 shape of the reduced axes / W-A3
|
|
3665
|
+
# full per-element). W-A1 / W-A4 are materialized to W-A3 form
|
|
3522
3666
|
# at dispatch (= simple, uniform kernel path). rev5 strict acceptance
|
|
3523
3667
|
# mirrors PROPOSAL_LINEAR_INTERP_PER_FIBER_MATCHED rev5 in spirit.
|
|
3524
3668
|
io.puts " if ( argc < 1 ) {"
|
|
@@ -3526,13 +3670,18 @@ module MkKernel
|
|
|
3526
3670
|
io.puts " }"
|
|
3527
3671
|
io.puts " volatile VALUE rweights = argv[0];"
|
|
3528
3672
|
io.puts " argc--; argv++;"
|
|
3529
|
-
#
|
|
3530
|
-
#
|
|
3673
|
+
# A CScalar is a scalar weight. Any other array, one with a single
|
|
3674
|
+
# element included, is held to the shape rules below.
|
|
3531
3675
|
io.puts " if ( rb_obj_is_carray(rweights) ) {"
|
|
3532
3676
|
io.puts " CArray *cv_pre_;"
|
|
3533
3677
|
io.puts " GetCArray(rweights, cv_pre_);"
|
|
3534
|
-
io.puts " if ( cv_pre_
|
|
3678
|
+
io.puts " if ( ca_is_scalar(cv_pre_) ) {"
|
|
3679
|
+
io.puts " int8_t wtype_ = cv_pre_->data_type;"
|
|
3535
3680
|
io.puts " rweights = rb_funcall(rweights, rb_intern(\"[]\"), 1, INT2NUM(0));"
|
|
3681
|
+
# A boolean cell reads as true / false; as a weight it is 1 / 0.
|
|
3682
|
+
io.puts " if ( wtype_ == CA_BOOLEAN && rweights != CA_UNDEF ) {"
|
|
3683
|
+
io.puts " rweights = INT2FIX(RTEST(rweights) ? 1 : 0);"
|
|
3684
|
+
io.puts " }"
|
|
3536
3685
|
io.puts " }"
|
|
3537
3686
|
io.puts " }"
|
|
3538
3687
|
# The weights are attached from here to the end of the call, so they
|
|
@@ -3545,7 +3694,16 @@ module MkKernel
|
|
|
3545
3694
|
wio.puts " /* W-A1: Ruby Numeric scalar -> full-shape constant weight CArray */"
|
|
3546
3695
|
wio.puts " vcw = rb_carray_new(#{waca}, src->ndim, src->dim, 0, NULL);"
|
|
3547
3696
|
wio.puts " GetCArray(vcw, cw); /* a new entity: its ptr needs no attach */"
|
|
3548
|
-
wio.puts "
|
|
3697
|
+
wio.puts " if ( rweights == CA_UNDEF ) {"
|
|
3698
|
+
wio.puts " /* A masked weight (UNDEF, or a one-element array masked): every"
|
|
3699
|
+
wio.puts " cell's weight is masked, so no cell contributes. */"
|
|
3700
|
+
wio.puts " memset(cw->ptr, 0, (size_t) cw->elements * cw->bytes);"
|
|
3701
|
+
wio.puts " ca_create_mask(cw);"
|
|
3702
|
+
wio.puts " memset(cw->mask->ptr, 1, (size_t) cw->elements);"
|
|
3703
|
+
wio.puts " }"
|
|
3704
|
+
wio.puts " else {"
|
|
3705
|
+
wio.puts " rb_ca_obj2ptr(vcw, rweights, cw->ptr); /* fill first cell */"
|
|
3706
|
+
wio.puts " }"
|
|
3549
3707
|
wio.puts " /* broadcast first cell to all elements (= constant fill) */"
|
|
3550
3708
|
wio.puts " {"
|
|
3551
3709
|
wio.puts " char *p0 = cw->ptr;"
|
|
@@ -3557,72 +3715,75 @@ module MkKernel
|
|
|
3557
3715
|
wio.puts " else {"
|
|
3558
3716
|
wio.puts " /* CArray val: coerce to the weight storage type (f64 for :promote, so a"
|
|
3559
3717
|
wio.puts " float weight against an int source is not truncated) */"
|
|
3560
|
-
wio.puts " /* vcv holds the view while cv is read: W-
|
|
3718
|
+
wio.puts " /* vcv holds the view while cv is read: W-A4 below moves vcw on to"
|
|
3561
3719
|
wio.puts " the materialised copy before it is done with cv. */"
|
|
3562
3720
|
wio.puts " volatile VALUE vcv = rb_ca_wrap_readonly(rweights, INT2NUM(#{waca}));"
|
|
3563
3721
|
wio.puts " CArray *cv;"
|
|
3564
3722
|
wio.puts " GetCArray(vcv, cv);"
|
|
3565
3723
|
wio.puts " vcw = vcv;"
|
|
3566
3724
|
wio.puts ""
|
|
3567
|
-
wio.puts " /*
|
|
3568
|
-
wio.puts "
|
|
3569
|
-
wio.puts "
|
|
3570
|
-
wio.puts "
|
|
3571
|
-
wio.puts "
|
|
3572
|
-
wio.puts
|
|
3573
|
-
wio.puts "
|
|
3574
|
-
wio.puts "
|
|
3575
|
-
wio.puts "
|
|
3725
|
+
wio.puts " /* form detection: W-A3 (self.shape) -> W-A4 (shape of the reduced axes,"
|
|
3726
|
+
wio.puts " taken in ascending axis order whatever order axis: lists them) -> raise */"
|
|
3727
|
+
wio.puts " int8_t wax[CA_RANK_MAX];"
|
|
3728
|
+
wio.puts " for ( int8_t i = 0; i < naxes; i++ ) {"
|
|
3729
|
+
wio.puts " int8_t a = slab_axes[i], j = i;"
|
|
3730
|
+
wio.puts " while ( j > 0 && wax[j-1] > a ) { wax[j] = wax[j-1]; j--; }"
|
|
3731
|
+
wio.puts " wax[j] = a;"
|
|
3732
|
+
wio.puts " }"
|
|
3733
|
+
wio.puts " int is_full_shape = ( cv->ndim == src->ndim );"
|
|
3734
|
+
wio.puts " for ( int8_t i = 0; is_full_shape && i < src->ndim; i++ ) {"
|
|
3735
|
+
wio.puts " if ( cv->dim[i] != src->dim[i] ) is_full_shape = 0;"
|
|
3736
|
+
wio.puts " }"
|
|
3737
|
+
wio.puts " int is_reduced_shape = ( ! is_full_shape && naxes >= 1 && cv->ndim == naxes );"
|
|
3738
|
+
wio.puts " for ( int8_t i = 0; is_reduced_shape && i < naxes; i++ ) {"
|
|
3739
|
+
wio.puts " if ( cv->dim[i] != src->dim[wax[i]] ) is_reduced_shape = 0;"
|
|
3740
|
+
wio.puts " }"
|
|
3741
|
+
wio.puts " if ( is_full_shape ) {"
|
|
3742
|
+
wio.puts " /* W-A3: per-element weights, used as they are */"
|
|
3576
3743
|
wio.puts " cw = cv;"
|
|
3577
3744
|
wio.puts " }"
|
|
3578
|
-
wio.puts " else if (
|
|
3579
|
-
wio.puts " /* W-
|
|
3580
|
-
wio.puts "
|
|
3745
|
+
wio.puts " else if ( is_reduced_shape ) {"
|
|
3746
|
+
wio.puts " /* W-A4: one weight per cell of the reduced axes, repeated along the"
|
|
3747
|
+
wio.puts " kept axes; materialized to self.shape. cw and its mask are made"
|
|
3748
|
+
wio.puts " before cv is attached: making them allocates. */"
|
|
3581
3749
|
wio.puts " vcw = rb_carray_new(#{waca}, src->ndim, src->dim, 0, NULL);"
|
|
3582
3750
|
wio.puts " GetCArray(vcw, cw);"
|
|
3583
3751
|
wio.puts " if ( ca_has_mask(cv) ) {"
|
|
3584
3752
|
wio.puts " ca_create_mask(cw);"
|
|
3585
3753
|
wio.puts " }"
|
|
3586
3754
|
wio.puts " ca_attach(cv); /* window: nothing raises inside */"
|
|
3587
|
-
wio.puts " /* broadcast cv (1-D, length src->dim[slab_axes[0]]) along reduce axis */"
|
|
3588
3755
|
wio.puts " {"
|
|
3589
|
-
wio.puts "
|
|
3590
|
-
wio.puts " ca_size_t
|
|
3591
|
-
wio.puts "
|
|
3592
|
-
wio.puts "
|
|
3593
|
-
wio.puts "
|
|
3594
|
-
wio.puts "
|
|
3595
|
-
wio.puts "
|
|
3596
|
-
wio.puts " for ( ca_size_t in = 0; in < inner_stride; in++ ) {"
|
|
3597
|
-
wio.puts " ca_size_t flat = (o * axis_n + a) * inner_stride + in;"
|
|
3598
|
-
wio.puts " memcpy(cw->ptr + flat * cw->bytes, cv->ptr + a * cv->bytes, cw->bytes);"
|
|
3599
|
-
wio.puts " }"
|
|
3600
|
-
wio.puts " }"
|
|
3756
|
+
wio.puts " ca_size_t idx[CA_RANK_MAX], wstep[CA_RANK_MAX];"
|
|
3757
|
+
wio.puts " ca_size_t step = 1;"
|
|
3758
|
+
wio.puts " boolean8_t *cw_m = ca_has_mask(cv) ? (boolean8_t *) cw->mask->ptr : NULL;"
|
|
3759
|
+
wio.puts " boolean8_t *cv_m = ca_has_mask(cv) ? (boolean8_t *) cv->mask->ptr : NULL;"
|
|
3760
|
+
wio.puts " for ( int8_t i = 0; i < src->ndim; i++ ) {"
|
|
3761
|
+
wio.puts " idx[i] = 0;"
|
|
3762
|
+
wio.puts " wstep[i] = 0; /* kept axes: the weight repeats */"
|
|
3601
3763
|
wio.puts " }"
|
|
3602
|
-
wio.puts "
|
|
3603
|
-
wio.puts "
|
|
3604
|
-
wio.puts "
|
|
3605
|
-
wio.puts "
|
|
3606
|
-
wio.puts "
|
|
3607
|
-
wio.puts "
|
|
3608
|
-
wio.puts "
|
|
3609
|
-
wio.puts "
|
|
3610
|
-
wio.puts "
|
|
3611
|
-
wio.puts "
|
|
3612
|
-
wio.puts "
|
|
3613
|
-
wio.puts "
|
|
3614
|
-
wio.puts " for ( ca_size_t a = 0; a < axis_n; a++ ) {"
|
|
3615
|
-
wio.puts " for ( ca_size_t in = 0; in < inner_stride; in++ ) {"
|
|
3616
|
-
wio.puts " ca_size_t flat = (o * axis_n + a) * inner_stride + in;"
|
|
3617
|
-
wio.puts " cw_m[flat] = cv_m[a];"
|
|
3618
|
-
wio.puts " }"
|
|
3764
|
+
wio.puts " for ( int8_t i = naxes - 1; i >= 0; i-- ) {"
|
|
3765
|
+
wio.puts " wstep[wax[i]] = step;"
|
|
3766
|
+
wio.puts " step *= cv->dim[i];"
|
|
3767
|
+
wio.puts " }"
|
|
3768
|
+
wio.puts " for ( ca_size_t flat = 0; flat < src->elements; flat++ ) {"
|
|
3769
|
+
wio.puts " ca_size_t w = 0;"
|
|
3770
|
+
wio.puts " for ( int8_t i = 0; i < src->ndim; i++ ) w += idx[i] * wstep[i];"
|
|
3771
|
+
wio.puts " memcpy(cw->ptr + flat * cw->bytes, cv->ptr + w * cv->bytes, cw->bytes);"
|
|
3772
|
+
wio.puts " if ( cw_m ) cw_m[flat] = cv_m[w];"
|
|
3773
|
+
wio.puts " for ( int8_t i = src->ndim - 1; i >= 0; i-- ) {"
|
|
3774
|
+
wio.puts " if ( ++idx[i] < src->dim[i] ) break;"
|
|
3775
|
+
wio.puts " idx[i] = 0;"
|
|
3619
3776
|
wio.puts " }"
|
|
3620
3777
|
wio.puts " }"
|
|
3621
3778
|
wio.puts " }"
|
|
3622
3779
|
wio.puts " ca_detach(cv);"
|
|
3623
3780
|
wio.puts " }"
|
|
3624
3781
|
wio.puts " else {"
|
|
3625
|
-
wio.puts
|
|
3782
|
+
wio.puts " volatile VALUE rexpect = rb_ary_new();"
|
|
3783
|
+
wio.puts " for ( int8_t i = 0; i < naxes; i++ ) {"
|
|
3784
|
+
wio.puts " rb_ary_push(rexpect, SIZE2NUM(src->dim[wax[i]]));"
|
|
3785
|
+
wio.puts " }"
|
|
3786
|
+
wio.puts %Q[ rb_raise(rb_eArgError, "%s: weights of shape %"PRIsVALUE" not accepted (expected a scalar, the shape of the reduced axes %"PRIsVALUE", or self.shape %"PRIsVALUE")", ca_calling_method_name(), rb_funcall(vcv, rb_intern("shape"), 0), rexpect, rb_funcall(self, rb_intern("shape"), 0));]
|
|
3626
3787
|
wio.puts " }"
|
|
3627
3788
|
wio.puts " }"
|
|
3628
3789
|
# Mask overlay (= legacy W-A3 path; if the materialized cw carries a mask, overlay it)
|
|
@@ -5116,15 +5277,16 @@ module MkKernel
|
|
|
5116
5277
|
# The object lane calls Ruby for every cell of the case B/C walk, which
|
|
5117
5278
|
# runs between a direct attach and detach of self and val; a raise there
|
|
5118
5279
|
# would leave both attached. An entity holds no attach, so the lane
|
|
5119
|
-
# reads an entity copy of whichever is a view
|
|
5280
|
+
# reads an entity copy of whichever is a view, or an entity that leaves
|
|
5281
|
+
# ptr NULL at rest (a CASource).
|
|
5120
5282
|
entity_bc = if is_object
|
|
5121
5283
|
<<~C.chomp
|
|
5122
5284
|
volatile VALUE vca_entity = self;
|
|
5123
|
-
if ( ! ca_is_entity(ca) ) {
|
|
5285
|
+
if ( ! ca_is_entity(ca) || ! ca->ptr ) {
|
|
5124
5286
|
vca_entity = rb_ca_copy(self);
|
|
5125
5287
|
GetCArray(vca_entity, ca);
|
|
5126
5288
|
}
|
|
5127
|
-
if ( ! ca_is_entity(cv) ) {
|
|
5289
|
+
if ( ! ca_is_entity(cv) || ! cv->ptr ) {
|
|
5128
5290
|
vsrc_val = rb_ca_copy(vsrc_val);
|
|
5129
5291
|
GetCArray(vsrc_val, cv);
|
|
5130
5292
|
}
|
|
@@ -6051,6 +6213,9 @@ module MkKernel
|
|
|
6051
6213
|
# `op_powi_<type>` resolves to `op_powi_int8_t` for int8 source).
|
|
6052
6214
|
# Substitution uses math_suffix (= typedef name, matches the suffix
|
|
6053
6215
|
# used in op_powi macro instantiations in ca_op_powi.h).
|
|
6216
|
+
# An object body calls Ruby on every cell and does not vectorize; the
|
|
6217
|
+
# pragma there only adds a "loop not vectorized" warning.
|
|
6218
|
+
simd = ( src == :object ) ? "" : "#pragma omp simd"
|
|
6054
6219
|
expr_body = raw.gsub(/#1/, "*p1").gsub(/#2/, "*p2").gsub(/#3/, "*p3").gsub(/<type>/, suffix)
|
|
6055
6220
|
# Option B+C pilot (binop emit substrate hardening, MEMO_BINOP_STRIDED_AUDIT.md):
|
|
6056
6221
|
# The no-mask body is split into a contig branch (i1==i2==i3==1) using the
|
|
@@ -6103,7 +6268,7 @@ module MkKernel
|
|
|
6103
6268
|
#{c_type} * __restrict__ r1 = q1;
|
|
6104
6269
|
#{c_type} * __restrict__ r2 = q2;
|
|
6105
6270
|
#{c_type} * __restrict__ r3 = q3;
|
|
6106
|
-
#
|
|
6271
|
+
#{simd}
|
|
6107
6272
|
for (k=0; k<n; k++) {
|
|
6108
6273
|
p1 = r1 + k; p2 = r2 + k; p3 = r3 + k;
|
|
6109
6274
|
{ #{expr_body} }
|
|
@@ -6111,7 +6276,7 @@ module MkKernel
|
|
|
6111
6276
|
}
|
|
6112
6277
|
else if ( ( apart1 || a1 == a3 ) && ( apart2 || a2 == a3 ) ) {
|
|
6113
6278
|
/* in place: an iteration reads and writes only its own cell */
|
|
6114
|
-
#
|
|
6279
|
+
#{simd}
|
|
6115
6280
|
for (k=0; k<n; k++) {
|
|
6116
6281
|
p1 = q1 + k; p2 = q2 + k; p3 = q3 + k;
|
|
6117
6282
|
{ #{expr_body} }
|
|
@@ -6123,6 +6288,66 @@ module MkKernel
|
|
|
6123
6288
|
{ #{expr_body} }
|
|
6124
6289
|
}
|
|
6125
6290
|
}
|
|
6291
|
+
} else if ( i3 == 1 && ( ( i1 == 1 && i2 == 0 ) || ( i1 == 0 && i2 == 1 ) ) ) {
|
|
6292
|
+
/* scalar branch: one operand is a scalar (stride 0). It is read
|
|
6293
|
+
into a local before the loop, so a store to the output cannot
|
|
6294
|
+
change it and the loop vectorizes like the contig branch. */
|
|
6295
|
+
#{c_type} sv = ( i1 == 0 ) ? *q1 : *q2;
|
|
6296
|
+
#{c_type} *qa = ( i1 == 0 ) ? q2 : q1;
|
|
6297
|
+
#{c_type} *p1, *p2, *p3;
|
|
6298
|
+
uintptr_t aa = (uintptr_t) qa, a3 = (uintptr_t) q3;
|
|
6299
|
+
uintptr_t nb = (uintptr_t) n * sizeof(*q3);
|
|
6300
|
+
(void)p1; (void)p2; (void)p3;
|
|
6301
|
+
if ( i1 == 0 ) {
|
|
6302
|
+
p1 = &sv;
|
|
6303
|
+
if ( a3 + nb <= aa || aa + nb <= a3 ) {
|
|
6304
|
+
#{c_type} * __restrict__ ra = qa;
|
|
6305
|
+
#{c_type} * __restrict__ r3 = q3;
|
|
6306
|
+
#{simd}
|
|
6307
|
+
for (k=0; k<n; k++) {
|
|
6308
|
+
p2 = ra + k; p3 = r3 + k;
|
|
6309
|
+
{ #{expr_body} }
|
|
6310
|
+
}
|
|
6311
|
+
}
|
|
6312
|
+
else if ( aa == a3 ) {
|
|
6313
|
+
#{simd}
|
|
6314
|
+
for (k=0; k<n; k++) {
|
|
6315
|
+
p2 = qa + k; p3 = q3 + k;
|
|
6316
|
+
{ #{expr_body} }
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
else {
|
|
6320
|
+
for (k=0; k<n; k++) {
|
|
6321
|
+
p2 = qa + k; p3 = q3 + k;
|
|
6322
|
+
{ #{expr_body} }
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
else {
|
|
6327
|
+
p2 = &sv;
|
|
6328
|
+
if ( a3 + nb <= aa || aa + nb <= a3 ) {
|
|
6329
|
+
#{c_type} * __restrict__ ra = qa;
|
|
6330
|
+
#{c_type} * __restrict__ r3 = q3;
|
|
6331
|
+
#{simd}
|
|
6332
|
+
for (k=0; k<n; k++) {
|
|
6333
|
+
p1 = ra + k; p3 = r3 + k;
|
|
6334
|
+
{ #{expr_body} }
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
else if ( aa == a3 ) {
|
|
6338
|
+
#{simd}
|
|
6339
|
+
for (k=0; k<n; k++) {
|
|
6340
|
+
p1 = qa + k; p3 = q3 + k;
|
|
6341
|
+
{ #{expr_body} }
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
else {
|
|
6345
|
+
for (k=0; k<n; k++) {
|
|
6346
|
+
p1 = qa + k; p3 = q3 + k;
|
|
6347
|
+
{ #{expr_body} }
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6126
6351
|
} else {
|
|
6127
6352
|
/* strided branch: numo-shape `p += s` increment form. */
|
|
6128
6353
|
#{c_type} *p1 = q1, *p2 = q2, *p3 = q3;
|
|
@@ -6455,6 +6680,24 @@ module MkKernel
|
|
|
6455
6680
|
{ #{expr_body} }
|
|
6456
6681
|
}
|
|
6457
6682
|
}
|
|
6683
|
+
} else if ( i3 == 1 && i1 == 1 && i2 == 0 ) {
|
|
6684
|
+
/* scalar on the right: read it once, so the boolean store
|
|
6685
|
+
cannot be taken to change it and the loop vectorizes */
|
|
6686
|
+
#{c_type} sv = *q2;
|
|
6687
|
+
p2 = &sv;
|
|
6688
|
+
for (k=0; k<n; k++) {
|
|
6689
|
+
p1 = q1 + k;
|
|
6690
|
+
p3 = q3 + k;
|
|
6691
|
+
{ #{expr_body} }
|
|
6692
|
+
}
|
|
6693
|
+
} else if ( i3 == 1 && i1 == 0 && i2 == 1 ) {
|
|
6694
|
+
#{c_type} sv = *q1;
|
|
6695
|
+
p1 = &sv;
|
|
6696
|
+
for (k=0; k<n; k++) {
|
|
6697
|
+
p2 = q2 + k;
|
|
6698
|
+
p3 = q3 + k;
|
|
6699
|
+
{ #{expr_body} }
|
|
6700
|
+
}
|
|
6458
6701
|
} else {
|
|
6459
6702
|
for (k=0; k<n; k++) {
|
|
6460
6703
|
p1 = q1 + k*i1;
|
|
@@ -6730,7 +6973,7 @@ module MkKernel
|
|
|
6730
6973
|
# surface) + optional CAMath module function, NOT as `<name>_ki`.
|
|
6731
6974
|
if k[:kind] == :monop
|
|
6732
6975
|
io.puts %Q[ rb_define_method(rb_cCArray, "#{k[:op]}", rb_ca_#{k[:name]}, 0);] if k[:bind]
|
|
6733
|
-
io.puts %Q[ rb_define_method(rb_cCArray, "#{k[:name]}!", rb_ca_#{k[:name]}_bang, 0);]
|
|
6976
|
+
io.puts %Q[ rb_define_method(rb_cCArray, "#{k[:name]}!", rb_ca_#{k[:name]}_bang, 0);] if k[:bang]
|
|
6734
6977
|
io.puts %Q[ rb_define_module_function(rb_mCAMath, "#{k[:name]}", rb_cmath_#{k[:name]}, 1);] if k[:cmath]
|
|
6735
6978
|
return
|
|
6736
6979
|
end
|
|
@@ -6958,11 +7201,10 @@ MkKernel.reduce :max,
|
|
|
6958
7201
|
# its identity value (1 for all, 0 for any) for those cells. An all-
|
|
6959
7202
|
# masked slab returns the identity (all([]) == true, any([]) == false).
|
|
6960
7203
|
#
|
|
6961
|
-
#
|
|
6962
|
-
#
|
|
6963
|
-
#
|
|
6964
|
-
#
|
|
6965
|
-
# added later as a separate kernel.
|
|
7204
|
+
# The walk stops once the answer is settled (settled:): any at the first
|
|
7205
|
+
# true, all and none at the first false / true. The test runs between
|
|
7206
|
+
# blocks of branch-free `acc |= v` / `acc &= v`, so a slab that never
|
|
7207
|
+
# settles keeps the speed of the plain fold.
|
|
6966
7208
|
MkKernel.header_block <<~C
|
|
6967
7209
|
/* The square root of a variance: an ε-negative from rounding becomes 0,
|
|
6968
7210
|
a NaN stays NaN (fmax(NaN, 0.0) is 0.0). */
|
|
@@ -6986,6 +7228,8 @@ C
|
|
|
6986
7228
|
MkKernel.reduce :all,
|
|
6987
7229
|
init: "1",
|
|
6988
7230
|
reduce: "acc &= v",
|
|
7231
|
+
settled: "!acc",
|
|
7232
|
+
reduction_kind: :min,
|
|
6989
7233
|
source: [:bool],
|
|
6990
7234
|
output: :bool,
|
|
6991
7235
|
ruby_scalar: :BOOL2VAL,
|
|
@@ -6995,6 +7239,8 @@ MkKernel.reduce :all,
|
|
|
6995
7239
|
MkKernel.reduce :any,
|
|
6996
7240
|
init: "0",
|
|
6997
7241
|
reduce: "acc |= v",
|
|
7242
|
+
settled: "acc",
|
|
7243
|
+
reduction_kind: :max,
|
|
6998
7244
|
source: [:bool],
|
|
6999
7245
|
output: :bool,
|
|
7000
7246
|
ruby_scalar: :BOOL2VAL,
|
|
@@ -7009,6 +7255,8 @@ MkKernel.reduce :any,
|
|
|
7009
7255
|
MkKernel.reduce :none,
|
|
7010
7256
|
init: "1",
|
|
7011
7257
|
reduce: "acc &= !v",
|
|
7258
|
+
settled: "!acc",
|
|
7259
|
+
reduction_kind: :min,
|
|
7012
7260
|
source: [:bool],
|
|
7013
7261
|
output: :bool,
|
|
7014
7262
|
ruby_scalar: :BOOL2VAL,
|
|
@@ -7457,9 +7705,9 @@ MkKernel.reduce :wsum,
|
|
|
7457
7705
|
# framework as wsum, but uses state+finish form (= sum + den) for the
|
|
7458
7706
|
# normalisation `mean = Σv*w / Σw` semantic.
|
|
7459
7707
|
#
|
|
7460
|
-
#
|
|
7461
|
-
#
|
|
7462
|
-
#
|
|
7708
|
+
# Weights summing to zero (den == 0 with cells present, weights of 0 or of
|
|
7709
|
+
# both signs) leave the mean undefined: the cell is UNDEF, as for a slab
|
|
7710
|
+
# with no valid cell, rather than 0/0 = NaN or x/0 = +-Infinity.
|
|
7463
7711
|
#
|
|
7464
7712
|
# 3.0 breaking semantic changes vs legacy (= identical to wsum W.2):
|
|
7465
7713
|
# - calling convention positional -> kwargs (axes positional, opts kw)
|
|
@@ -7476,6 +7724,7 @@ MkKernel.reduce :wmean,
|
|
|
7476
7724
|
reduce: "(sum += (double) v * (double) w, den += (double) w)",
|
|
7477
7725
|
reduction_kind: { sum: :plus, den: :plus }, # SL.1.4b
|
|
7478
7726
|
finish: "sum / den",
|
|
7727
|
+
undefined_if: "den == 0",
|
|
7479
7728
|
source: MkKernel::ALL_NUMERIC,
|
|
7480
7729
|
output: :f64,
|
|
7481
7730
|
ruby_scalar: :rb_float_new,
|
|
@@ -8632,7 +8881,11 @@ MkKernel.monfunc :floor,
|
|
|
8632
8881
|
object: '(#2) = rb_funcall((#1), rb_intern("floor"), 0);',
|
|
8633
8882
|
}
|
|
8634
8883
|
|
|
8884
|
+
# round: the Ruby methods `round(ndigits)` / `round!(ndigits)` are defined in
|
|
8885
|
+
# carray_round.c on top of this kernel, so none is registered here.
|
|
8635
8886
|
MkKernel.monfunc :round,
|
|
8887
|
+
bind: false,
|
|
8888
|
+
bang: false,
|
|
8636
8889
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8637
8890
|
expr: {
|
|
8638
8891
|
int: "(#2) = (#1);",
|
|
@@ -8952,22 +9205,23 @@ MkKernel.monfunc :rad_pi,
|
|
|
8952
9205
|
# Float NaN included), maximum / minimum let it win. In both flavours, of
|
|
8953
9206
|
# two zeros the max is +0 and the min is -0 in either order. C's fmax /
|
|
8954
9207
|
# fmin leave that choice to the library (glibc and macOS answer
|
|
8955
|
-
# differently), so the float branch does not call them.
|
|
9208
|
+
# differently), so the float branch does not call them. The sign of a
|
|
9209
|
+
# zero is read with copysign(1, x) rather than signbit(x): GCC does not
|
|
9210
|
+
# vectorize signbit on a double, which left the float64 kernels 17x
|
|
9211
|
+
# slower than float32. NaN is handled before, so copysign never sees one.
|
|
8956
9212
|
MkKernel.binop :pmax,
|
|
8957
9213
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8958
9214
|
expr: {
|
|
8959
9215
|
int: "(#3) = (#1) > (#2) ? (#1) : (#2);",
|
|
8960
|
-
float: "(#3) = isnan(#1) ? (#2) : isnan(#2) ? (#1) : ((#1) > (#2) || ((#1) == (#2) && !signbit(#1))) ? (#1) : (#2);",
|
|
8961
9216
|
object: '(#3) = ca_obj_extremum_replaces((#2), (#1), rb_intern(">")) ? (#2) : (#1);',
|
|
8962
|
-
}
|
|
9217
|
+
}.merge(MkKernel.float_widths("(#3) = isnan(#1) ? (#2) : isnan(#2) ? (#1) : ((#1) > (#2) || ((#1) == (#2) && copysign<f>(1, (#1)) > 0)) ? (#1) : (#2);"))
|
|
8963
9218
|
|
|
8964
9219
|
MkKernel.binop :pmin,
|
|
8965
9220
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8966
9221
|
expr: {
|
|
8967
9222
|
int: "(#3) = (#1) < (#2) ? (#1) : (#2);",
|
|
8968
|
-
float: "(#3) = isnan(#1) ? (#2) : isnan(#2) ? (#1) : ((#1) < (#2) || ((#1) == (#2) && signbit(#1))) ? (#1) : (#2);",
|
|
8969
9223
|
object: '(#3) = ca_obj_extremum_replaces((#2), (#1), rb_intern("<")) ? (#2) : (#1);',
|
|
8970
|
-
}
|
|
9224
|
+
}.merge(MkKernel.float_widths("(#3) = isnan(#1) ? (#2) : isnan(#2) ? (#1) : ((#1) < (#2) || ((#1) == (#2) && copysign<f>(1, (#1)) < 0)) ? (#1) : (#2);"))
|
|
8971
9225
|
|
|
8972
9226
|
# NaN-propagate variants. Float
|
|
8973
9227
|
# branch tests both operands for NaN and short-circuits to NaN; non-
|
|
@@ -8978,17 +9232,15 @@ MkKernel.binop :maximum,
|
|
|
8978
9232
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8979
9233
|
expr: {
|
|
8980
9234
|
int: "(#3) = (#1) > (#2) ? (#1) : (#2);",
|
|
8981
|
-
float: "(#3) = isnan(#1) ? (#1) : isnan(#2) ? (#2) : ((#1) > (#2) || ((#1) == (#2) && !signbit(#1))) ? (#1) : (#2);",
|
|
8982
9235
|
object: '(#3) = CA_OBJ_ISNAN(#1) ? (#1) : CA_OBJ_ISNAN(#2) ? (#2) : ca_obj_cmp((#2), (#1)) > 0 ? (#2) : (#1);',
|
|
8983
|
-
}
|
|
9236
|
+
}.merge(MkKernel.float_widths("(#3) = isnan(#1) ? (#1) : isnan(#2) ? (#2) : ((#1) > (#2) || ((#1) == (#2) && copysign<f>(1, (#1)) > 0)) ? (#1) : (#2);"))
|
|
8984
9237
|
|
|
8985
9238
|
MkKernel.binop :minimum,
|
|
8986
9239
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8987
9240
|
expr: {
|
|
8988
9241
|
int: "(#3) = (#1) < (#2) ? (#1) : (#2);",
|
|
8989
|
-
float: "(#3) = isnan(#1) ? (#1) : isnan(#2) ? (#2) : ((#1) < (#2) || ((#1) == (#2) && signbit(#1))) ? (#1) : (#2);",
|
|
8990
9242
|
object: '(#3) = CA_OBJ_ISNAN(#1) ? (#1) : CA_OBJ_ISNAN(#2) ? (#2) : ca_obj_cmp((#2), (#1)) < 0 ? (#2) : (#1);',
|
|
8991
|
-
}
|
|
9243
|
+
}.merge(MkKernel.float_widths("(#3) = isnan(#1) ? (#1) : isnan(#2) ? (#2) : ((#1) < (#2) || ((#1) == (#2) && copysign<f>(1, (#1)) < 0)) ? (#1) : (#2);"))
|
|
8992
9244
|
|
|
8993
9245
|
# + and - work on the parts independently, so one generic expression
|
|
8994
9246
|
# covers every numeric data_type at any width.
|