From 7bf095e02be49764fdda1fa32ef13a1675592246 Mon Sep 17 00:00:00 2001 From: Panos <25664832+mechpanos@users.noreply.github.com> Date: Fri, 4 Sep 2026 19:10:13 +0300 Subject: [PATCH] perf: parse wire names across cores when building the handle index With the block geometry cached, the per-edit upload is 135 ms of which only 92 ms was accounted for by the regular and block halves. The rest is the handle index: a `String::parse` per wire over the whole 198 k resident set, rebuilt from scratch on every edit. The arena has an incremental path for this (`patch_handle_index`), but the shared path never reaches it -- the same gate that keeps the arena away from this drawing. Short of that, the parses are independent, so run them across cores and keep only the map insertion serial. `par_iter().enumerate()` collects in index order, so the slot lists keep the emission order they depend on. Adds a `wire-handle-index` PERF line, since this was the unaccounted third of the upload. Co-Authored-By: Claude Opus 5 --- src/scene/pipeline/mod.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 8e983d497..a0b8f1c26 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -2402,12 +2402,32 @@ impl Pipeline { // Index handle → wire slots once, here, so the per-hover selection // overlay can gather just the highlighted wires instead of scanning + // string-parsing the whole set every time the hovered entity changes. + // + // Parsing 198 k names is the bulk of this and each one is independent, + // so it runs across cores; only the map insertion stays serial, and + // that is cheap once the parse is done. Emission order is preserved -- + // `par_iter().enumerate()` collects in index order -- which the slot + // lists rely on. + let t_index = crate::perf::enabled().then(iced::time::Instant::now); + let parsed: Vec<(u64, u32)> = { + use crate::par::prelude::*; + wires + .par_iter() + .enumerate() + .filter_map(|(idx, w)| w.name.parse::().ok().map(|h| (h, idx as u32))) + .collect() + }; let mut index: rustc_hash::FxHashMap> = rustc_hash::FxHashMap::default(); - index.reserve(wires.len()); - for (idx, w) in wires.iter().enumerate() { - if let Ok(h) = w.name.parse::() { - index.entry(h).or_default().push(idx as u32); - } + index.reserve(parsed.len()); + for (handle, idx) in parsed { + index.entry(handle).or_default().push(idx); + } + if let Some(started) = t_index { + crate::perf_record!( + "[perf] wire-handle-index {:.1}ms wires={}", + started.elapsed().as_secs_f64() * 1000.0, + wires.len(), + ); } ( std::sync::Arc::new(batches),