From 56cb10d6e560ea32e8d522a2b293bab4c597b776 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Mon, 19 Aug 2024 14:12:40 -0400 Subject: [PATCH] Min/max scan that can skip unused argument vectors sometimes --- src/builtins/scan.c | 1 + src/singeli/src/scan.singeli | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/builtins/scan.c b/src/builtins/scan.c index ba46aa75..330a9a54 100644 --- a/src/builtins/scan.c +++ b/src/builtins/scan.c @@ -11,6 +11,7 @@ // =≤≥>- in terms of ≠<∨∧+ with adjustments // Arithmetic operand, rank 1: // ⌈⌊ Scalar, SSE, AVX in log(vector width) steps (SHOULD add NEON) +// Check in 6-vector blocks to quickly write result if constant // + Overflow-checked scalar or AVX2 // Ad-hoc boolean-valued handling for ≠∨ // SHOULD extend rank 1 special cases to cell bound 1 diff --git a/src/singeli/src/scan.singeli b/src/singeli/src/scan.singeli index bba60d64..841a0508 100644 --- a/src/singeli/src/scan.singeli +++ b/src/singeli/src/scan.singeli @@ -23,20 +23,42 @@ def scan_loop{init, x:*T, r:*T, len:(u64), scan, scan_last} = { q:= len & (step-1) if (q!=0) homMaskStoreF{rv+e, maskOf{V, q}, scan_last{load{xv,e}, p}} } -def scan_post{init, x:*T, r:*T, len:(u64), op, pre} = { +def get_scan_last{op, pre} = { def last{v, p} = op{pre{v}, p} def scan{v, p} = { n:= last{v, p} p = toLast{n} n } - scan_loop{init, x, r, len, scan, last} + tup{scan, last} } # Associative scan ?` if a?b?a = a?b = b?a, used for ⌊⌈ def scan_idem = scan_scal fn scan_idem{T, op if hasarch{'X86_64'}}(x:*T, r:*T, len:u64, init:T) : void = { - scan_post{init, x, r, len, op, make_scan_idem{T, op}} + def {scan, last} = get_scan_last{op, make_scan_idem{T, op}} + def cmp = match (op) { {(min)} => (>); {(max)} => (<) } + def step = arch_defvw/width{T} + def V = [step]T + p:= V**init + xv:= *V ~~ x + rv:= *V ~~ r + e:= len/step + # Check k vectors at a time to see if they can all be ignored + def k = 6 + ek := e / k + @for (ik to ek) { i := ik * k + def ii = iota{k} + xvi := each{load{xv + i, .}, ii} + if (not homAny{cmp{p, tree_fold{op, xvi}}}) { + each{store{rv+i, ., p}, ii} + } else @unroll (rv in rv+i over j to k) { + rv = scan{select{xvi,j}, p} + } + } + @for (xv, rv over _ from ek*k to e) rv = scan{xv,p} + q:= len & (step-1) + if (q!=0) homMaskStoreF{rv+e, maskOf{V, q}, last{load{xv,e}, p}} } export{'si_scan_min_init_i8', scan_idem{i8 , min}}; export{'si_scan_max_init_i8', scan_idem{i8 , max}} @@ -67,7 +89,7 @@ def scan_plus = scan_assoc{+} def scan_assoc_0 = scan_scal fn scan_assoc_0{T, op if hasarch{'X86_64'}}(x:*T, r:*T, len:u64, init:T) : void = { # Prefix op on entire AVX register - scan_post{init, x, r, len, op, scan_plus} + scan_loop{init, x, r, len, ...get_scan_last{op, scan_plus}} } export{'si_scan_pluswrap_u8', scan_assoc_0{u8 , +}} export{'si_scan_pluswrap_u16', scan_assoc_0{u16, +}}