From 0a3984937fb1110e460b66d019a51ecca9f00d15 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 20:43:37 +0000 Subject: [PATCH] census: optional column dump, so a consumer can answer the SIMD question MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `r2il::columns` states that whether vectorizing its scans pays "is a profiling question nobody has answered", and that the crate takes no ndarray dependency on purpose — the consumer holds one. That left no way to get real columns out of a real lift into such a consumer. Adds an env-gated dump (WIN32_CENSUS_COLUMNS_OUT, default off, no behaviour change when unset) writing the three columns in a flat LE form: u64 n, n*u8 tag, n*u8 space, n*u64 offset. Measured with it (ndarray's r2il_column_scan_probe, Xeon avx512f/bw/vl): on this fixture's 12 408 p-code ops the mask arms run 1.32-1.42x the scalar scan, the advantage inverts to 0.75-0.80x above ~200 K ops, and the whole-census saving is 2.5 us against a lift that costs milliseconds. The columns' own doc-comment caution was right. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv --- crates/r2sleigh-lift/examples/win32_census.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/r2sleigh-lift/examples/win32_census.rs b/crates/r2sleigh-lift/examples/win32_census.rs index 6eacdf64..204ba58b 100644 --- a/crates/r2sleigh-lift/examples/win32_census.rs +++ b/crates/r2sleigh-lift/examples/win32_census.rs @@ -396,6 +396,28 @@ fn main() { c.ops_total, "columns must carry exactly one entry per op" ); + + // Optional column dump, for the SIMD-crossover probe (default off). + // + // `r2il` deliberately has no `ndarray` dependency (see `columns.rs`), so + // the question "does vectorizing these scans pay?" can only be answered + // by a CONSUMER that holds one. This writes the three columns in a flat + // LE form so such a consumer can measure against the real lift instead + // of a synthetic stream. Format: u64 n, then n*u8 tag, n*u8 space, + // n*u64 offset. + if let Ok(path) = std::env::var("WIN32_CENSUS_COLUMNS_OUT") { + let n = cols.len(); + let mut buf = Vec::with_capacity(8 + n * 10); + buf.extend_from_slice(&(n as u64).to_le_bytes()); + buf.extend_from_slice(&cols.tag); + buf.extend_from_slice(&cols.space); + for &o in &cols.offset { + buf.extend_from_slice(&o.to_le_bytes()); + } + std::fs::write(&path, &buf).expect("column dump"); + eprintln!("[columns] wrote {n} ops to {path}"); + } + let hist = cols.tag_histogram(); assert_eq!( hist[OpTag::CallOther as usize],