forked from 1b-agent/opencadstudio-upstream
perf(glyphs): memoize constraint-glyph placements behind constraints_epoch
Placements (entity lookups, intersections, String labels, projections) recomputed per view() and per mouse-move hit-test, with overlay re-clones and 2-3x offset recomputes. New constraints_epoch bumped on every set mutation/visibility/undo/DWG-load (plus new set_constraint_enabled API); single-slot Arc<[GlyphEntry]> memo on an 11-field key (hover/selection excluded); overlay de-cloned/de-duped to the single geometry source; hit paths reuse the cache. Tests: - cached_glyph_placements_hit_returns_same_arc: same-key Arc identity - cached_glyph_placements_invalidated_by_metadata_edit: edit + selection change yield new Arc - glyph_hit_test_entries_matches_precomputed_boxes: shared hit-test helper Bench (cargo bench glyph, 60 constraints): 8.18us -> 0.05-0.15us (~55x). A/B reference deleted.
This commit is contained in:
1 parent
2d49204c50
commit
4432f633e7
12 files changed
+767
-239
No files matched your search
+2
-1
@@ -19,7 +19,7 @@ cargo bench --bench performance_benchmarks -- --quick
|
||||
|
||||
---
|
||||
|
||||
## Benchmark Suite Catalog (29 Metrics)
|
||||
## Benchmark Suite Catalog (30 Metrics)
|
||||
|
||||
### 1. Scene & Entity Management
|
||||
- `scene_entity_ingestion_10k`: Ingestion throughput for 10,000 mixed 2D entities (Lines, Circles, Arcs, Polylines).
|
||||
@@ -55,6 +55,7 @@ cargo bench --bench performance_benchmarks -- --quick
|
||||
- `ui_statusbar_derived_data`: Status-bar cached derived-data hit (layout names + scale picker `Arc` clones).
|
||||
- `ui_grip_budget`: Budgeted per-frame selection-grip projection (dense-polyline grips capped to `MAX_SELECTED_GRIPS`).
|
||||
- `ui_grip_budget_build`: Selection-change `apply_grip_budget` cost over the full grip set (sort + FxHashSet + filter).
|
||||
- `ui_constraint_glyphs`: Constraint-glyph cache hit (`cached_glyph_placements` key build + `Arc` clone over dozens of glyphs, no recompute).
|
||||
|
||||
### 6. State Management & Draworder
|
||||
- `selection_state_deep_clone`: Deep cloning of `SelectionState` (with 64 polygon points).
|
||||
|
||||
@@ -1618,6 +1618,99 @@ fn bench_ui_grip_budget(runner: &mut BenchmarkRunner) {
|
||||
);
|
||||
}
|
||||
|
||||
// ── 12e. UI Constraint-Glyph Cache Hit (memoised placements) ────────────────
|
||||
// Covers the parametric constraint-glyph memo (`Scene::cached_glyph_placements`,
|
||||
// src/scene/parametric_constraints.rs): dozens of constraints whose per-frame
|
||||
// view + hit-test + dwell + click share one key, so the hot path is a cache
|
||||
// hit (key build + Arc clone), not a placements recompute (entity lookups,
|
||||
// intersections, String labels, projections).
|
||||
//
|
||||
// NOTE: the bench calls the real `cached_glyph_placements` with the same key
|
||||
// inputs the production consumers (app/view, viewport hit paths, overlay) and
|
||||
// the `cached_glyph_placements_*` unit-test fixture use
|
||||
// (`ParametricScope::ModelSpace`, full-canvas vp, values on, display 3,
|
||||
// bar 4095), so the measured hit is exactly what the frame shares.
|
||||
|
||||
fn bench_ui_constraint_glyphs(runner: &mut BenchmarkRunner) {
|
||||
if !runner.should_run("ui_constraint_glyphs") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fixture: dozens of horizontal constraints over a grid of short lines
|
||||
// around the origin — mirrors the `cached_glyph_placements_*` unit-test
|
||||
// fixture (Scene::new + add_entity + constraint add + note applied).
|
||||
let n_constraints = if runner.quick_mode { 24 } else { 60 };
|
||||
let mut scene = Scene::new();
|
||||
let cols = 8;
|
||||
for i in 0..n_constraints {
|
||||
let x = ((i % cols) as f64) * 30.0 - 100.0;
|
||||
let y = ((i / cols) as f64) * 30.0 - 100.0;
|
||||
let line = scene.add_entity(EntityType::Line(Line::from_points(
|
||||
Vector3::new(x, y, 0.0),
|
||||
Vector3::new(x + 20.0, y, 0.0),
|
||||
)));
|
||||
let id = scene
|
||||
.parametric_constraint_set_mut(ParametricScope::ModelSpace)
|
||||
.add(
|
||||
ConstraintKind::Horizontal,
|
||||
vec![ParametricRef::whole(line)],
|
||||
None,
|
||||
);
|
||||
scene.note_parametric_constraint_applied(ParametricScope::ModelSpace, id, 3);
|
||||
}
|
||||
scene.selection.borrow_mut().vp_size = (1920.0, 1080.0);
|
||||
let vp = (1920.0_f32, 1080.0_f32);
|
||||
|
||||
// Prime the cache so every timed call below is a hit (same key, same Arc).
|
||||
let primed =
|
||||
scene.cached_glyph_placements(ParametricScope::ModelSpace, vp, true, 3, 4095);
|
||||
assert!(
|
||||
!primed.is_empty(),
|
||||
"glyph fixture must yield placements"
|
||||
);
|
||||
let n_glyphs = primed.len();
|
||||
|
||||
// Warm-up for allocator settling.
|
||||
for _ in 0..10 {
|
||||
let hit =
|
||||
scene.cached_glyph_placements(ParametricScope::ModelSpace, vp, true, 3, 4095);
|
||||
black_box(hit);
|
||||
}
|
||||
|
||||
let n = if runner.quick_mode { 200 } else { 1_000 };
|
||||
let runs = 5;
|
||||
let mut samples = Vec::with_capacity(runs);
|
||||
|
||||
for _ in 0..runs {
|
||||
let t0 = Instant::now();
|
||||
for _ in 0..n {
|
||||
let hit = scene.cached_glyph_placements(
|
||||
black_box(ParametricScope::ModelSpace),
|
||||
black_box(vp),
|
||||
black_box(true),
|
||||
black_box(3),
|
||||
black_box(4095),
|
||||
);
|
||||
black_box(hit);
|
||||
}
|
||||
let per_us = (t0.elapsed().as_micros() as f64) / (n as f64);
|
||||
samples.push(per_us);
|
||||
}
|
||||
|
||||
let median_us = samples[samples.len() / 2];
|
||||
runner.record(
|
||||
"ui_constraint_glyphs",
|
||||
&format!(
|
||||
"Constraint-glyph cache hit over {} glyphs (key build + Arc clone, no recompute)",
|
||||
n_glyphs
|
||||
),
|
||||
"µs",
|
||||
samples,
|
||||
Some(((n_glyphs as f64) / (median_us / 1_000_000.0), "glyphs/s")),
|
||||
Some(0.5), // Target threshold < 0.5 µs (measured ~0.06 µs quick / ~0.15 µs full, ~3x headroom)
|
||||
);
|
||||
}
|
||||
|
||||
// ── 13. Wide & Tapered Arc + Donut Tessellation ─────────────────────────────
|
||||
|
||||
fn bench_wide_and_tapered_arc_tessellation(runner: &mut BenchmarkRunner) {
|
||||
@@ -2023,6 +2116,7 @@ fn main() {
|
||||
bench_ui_plotstyle_layer_usage(&mut runner);
|
||||
bench_ui_statusbar_derived_data(&mut runner);
|
||||
bench_ui_grip_budget(&mut runner);
|
||||
bench_ui_constraint_glyphs(&mut runner);
|
||||
bench_wide_and_tapered_arc_tessellation(&mut runner);
|
||||
bench_zoom_extents_calculation(&mut runner);
|
||||
bench_batch_entity_mutation(&mut runner);
|
||||
|
||||
Reference in new issue
Block a user