Skip to content

Add scripted-measurement MCP tools and fix render_to_image PNG leak (perf-report follow-ups)#161

Merged
philliphoff merged 4 commits into
mainfrom
philliphoff/profile-portrayal-render
Jun 1, 2026
Merged

Add scripted-measurement MCP tools and fix render_to_image PNG leak (perf-report follow-ups)#161
philliphoff merged 4 commits into
mainfrom
philliphoff/profile-portrayal-render

Conversation

@philliphoff
Copy link
Copy Markdown
Owner

Implements the highest-priority items from the perf-report optimisation plan: 3 new Viewer MCP tools that unblock scripted performance measurement, plus the P1 fix for the PNG-buffer / native-bitmap retention leak in render_to_image.

Commits (split for per-commit review)

Commit Plan ID Summary
2c3dd08 PR-A1 set_viewport MCP tool — bbox or center+resolution; extends IMapHost with SetViewportToExtent / SetViewportToCenterAndResolution
5855e97 PR-A2 set_palette + set_display_category MCP tools; introduces IRenderStateController UI-thread marshaling abstraction
b283f18 PR-A3 set_time_step MCP tool — accepts int index or ISO timestamp, snaps to nearest sample
dbe48f3 P1 Fix render_to_image snapshot-Map retention leak (responsible for 144 MB → 1.46 GB RSS growth over 150 renders in the perf report)

Why these four together

The 3 MCP tools (set_viewport, set_palette/set_display_category, set_time_step) are the scripted-measurement primitives the perf-report's Track-A and Track-B harnesses need to drive deterministic warm-render scenarios. The P1 fix is in MapsuiMapHost.RenderCurrentViewToPngAsync — the body of the existing render_to_image MCP tool, which is itself one of those measurement primitives. Without P1, every Track-B retention measurement is contaminated by ~10 MB of leaked PNG buffer and native-bitmap memory per render call. The plan called this out as must land before any other Track-B retention work.

P1 root cause + fix

Mapsui.Map implements IDisposable, but the snapshot Map constructed per-call in RenderCurrentViewToPngAsync was let go to the GC un-disposed. Its LayerCollection and property-changed plumbing kept the snapshot rooted indefinitely, holding the per-call PNG buffer plus its associated SkiaSharp bitmaps.

Fix: detach live layers (snapshot.Layers.ClearAllGroups()) before disposing the snapshot, so the snapshot's dispose path only releases its own owned resources and never touches the live layer instances we share with the on-screen Map. Layer-collection teardown is wrapped in a best-effort try/catch so a teardown failure cannot mask a render failure.

Verification

  • All 579 viewer unit tests pass.
  • New unit tests for the 3 MCP tools (full coverage of the validation paths and error shapes).
  • P1 has no unit-test surface — MapsuiMapHost requires a real Avalonia MapControl. Validation for the leak fix belongs to the perf-report's Track-B path: rerun the 150-render dotnet-gcdump harness against this branch and expect the RSS ceiling to drop from ~1.46 GB to roughly 200 MB. Adding a headless Avalonia harness for MapsuiMapHost is a candidate follow-up tooling gap (it would also enable end-to-end testing of PR-A1's viewport setters).

Docs

docs/mcp-server.md and src/EncDotNet.S100.Viewer/README.md updated to describe the 4 new tools and the read-only / mutating tool convention.

Not in this PR

The remaining 12 items from the optimisation plan (PR-A4 close_dataset, PR-A5, the 4 PerfRunner harness improvements, and P2–P7) are deliberately deferred — they all benefit from re-running the perf harness against the post-P1 baseline, and shipping them blind would violate the plan's own sequencing rule.

Phillip Hoff and others added 4 commits May 31, 2026 19:29
Adds the first mutating MCP tool, distinguished from the existing
read-only render_to_image. Accepts either a WGS-84 bbox
(south/west/north/east) or a centre+zoom pair (centerLat/centerLon/
zoom) and drives the live viewer's navigator. Closes part of the
tooling gap identified in the portrayal/render perf report (§7):
without this, scripted measurement runs could not drive pan/zoom
from outside the GUI, so warm 'viewport-change' scenarios were
unmeasurable.

Pattern (request/result records, IMapHost surface, MCP adapter,
FakeMapHost test seam) can be reused for set_palette,
set_time_step, and close_dataset in follow-up PRs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two more mutating MCP tools, alongside set_viewport. The viewer's
palette (Day/Dusk/Night) and ECDIS display category (DisplayBase/
Standard/OtherInformation/All) can now be driven from scripted
measurement runs.

Introduces IRenderStateController + accessor as the indirection
between MCP tools and SettingsViewModel / EcdisDisplayState. The
controller marshals every setter through the Avalonia UI dispatcher,
so off-thread MCP request handlers can drive UI-affine state
without races. Mirrors the IMapHostAccessor late-binding pattern.

Extracts a small ToolErrorPayload helper so every adapter emits the
same failure shape — it was getting copy-pasted.

Closes part of the tooling gap from the portrayal/render perf
report (§7); palette-switch and display-category-change scenarios
were not externally drivable before this.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drives the viewer's GlobalTimeService to a specific sample for
time-aware datasets (S-104 / S-111 / S-411). Accepts either a
0-based index or an ISO-8601 timestamp (snapped to the nearest
sample), mirroring the --time-step CLI flag but applicable
mid-session.

Internal dispatcher seam (Func<Action, Task>) lets the tool be
unit-tested without a running Avalonia application; production
construction wires it to Dispatcher.UIThread.InvokeAsync so the
notify path runs where bindings expect.

Closes part of the tooling gap from the perf report (§7); time-step
churn scenarios for S-111 / S-104 were not externally drivable
before this. Final tool in the 'set_*' family for PR-A2..A4 of the
optimisation plan.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The MCP render_to_image clone-Map path was constructing a Mapsui Map
on every call and letting it go to the GC unsdisposed. Mapsui.Map
implements IDisposable, and its un-run dispose path leaves
LayerCollection / property-changed plumbing rooted indefinitely.
Over 150 sequential renders, perf-report Track-B measurements saw
RSS climb from ~144 MB to ~1.46 GB — the per-call PNG buffer plus
its associated SkiaSharp bitmaps could not be reclaimed.

Fix: detach the live layers from the snapshot Map before disposing
it (so the snapshot's dispose path only touches its own owned
resources, never the live layer instances we don't own), then call
Dispose explicitly via try/finally. Layer-collection teardown is
best-effort — a teardown failure should never mask a render failure
or crash the dispatcher.

This was the highest-priority candidate from the perf report (§6
P1) and the plan called it out as 'must land before any other
Track-B retention work' — without it, every gcdump comparison is
contaminated by this leak. Validation belongs to the Track-B path
(live viewer + dotnet-gcdump over 150 renders); MapsuiMapHost has
no unit-test surface because its real implementation requires an
Avalonia MapControl.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

Performance Gate

PASSED — no regressions.

Threshold: 10.0%, MAD multiplier (k): 3.0, retry-zone mult: 2.0×

Scenario summary

Scenario Status Δ median (%) z (Δ/MAD) Base median (ms) Samples (b/c)
exchange-set-open ✅ pass +1.4 +0.09 0.56 20/20
s101-portray-cold ✅ pass -0.1 -0.01 434.39 20/20
s101-portray-warm ✅ pass +2.1 +0.38 277.16 20/20
s101-real-cold ✅ pass +11.6 +4.41 0.24 5/5
s101-real-warm ✅ pass -0.8 -0.06 0.28 5/5
s101-render-warm ✅ pass +3.6 +0.97 258.60 20/20
s102-coverage ✅ pass -6.4 -0.45 1.36 20/20
s102-coverage-open ✅ pass -0.8 -0.18 3.06 20/20
s102-coverage-render-large ✅ pass +0.8 +0.88 131.71 20/20
s102-real-warm ✅ pass +2.8 +0.38 0.25 5/5
s111-real-warm ✅ pass +5.6 +13.44 0.22 5/5
s124-vector ✅ pass +3.3 +0.39 0.31 20/20
s201-vector ✅ pass -3.7 -0.40 0.35 20/20

exchange-set-open

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.56 0.57
Baseline MAD (ms) 0.09
Δ median +1.4%
z (Δ/MAD) +0.09

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.asset.read 11.88 11.66 -1.9% ▫️
s100.exchangeset.parse 42.93 43.86 +2.2% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.asset.read.duration 19.68 19.61 -0.3% ▫️

s101-portray-cold

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 434.39 434.03
Baseline MAD (ms) 28.09
Δ median -0.1%
z (Δ/MAD) -0.01

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 7991.84 8687.78 +8.7%
s100.lua.rule.invoke 6899.00 7506.67 +8.8%
s100.pipeline.vector.process 8222.42 8958.45 +9.0%
s100.pipeline.vector.stage.assemble 0.26 0.25 -2.4% ▫️
s100.pipeline.vector.stage.feature_xml 167.70 208.76 +24.5%
s100.pipeline.vector.stage.lua 7994.75 8690.29 +8.7%
s100.pipeline.vector.stage.rule_select 6.85 7.17 +4.7% ▫️
s100.pipeline.vector.stage.sort 13.26 14.53 +9.6%
s100.pipeline.vector.stage.viewing_groups 15.33 16.50 +7.6%
s100.pipeline.vector.stage.xslt 0.29 0.30 +4.3% ▫️
s100.render.frame 1994.42 1975.22 -1.0% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 7.00 7.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.lua.execute.duration 2658.20 3169.36 +19.2%
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 42.00 42.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 217.00 217.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 21.00 21.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.features.count 2478.00 2478.00 +0.0% ▫️
s100.lua.instructions.emitted.count 4004.00 4004.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 2250.29 2732.76 +21.4%
s100.lua.rule.invoke.duration 4.73 5.15 +9.0%
s100.lua.source.cache.hit.count 552.00 552.00 +0.0% ▫️
s100.lua.source.cache.miss.count 43.00 43.00 +0.0% ▫️
s100.pattern.cache.hit.count 210.00 210.00 +0.0% ▫️
s100.pattern.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 4004.00 4004.00 +0.0% ▫️
s100.pipeline.duration 2741.00 3255.33 +18.8%
s100.pipeline.features.in 217.00 217.00 +0.0% ▫️
s100.pipeline.stage.duration 0.32 0.31 -5.6% ▫️
s100.pipeline.stage.duration 65.09 68.48 +5.2%
s100.pipeline.stage.duration 2659.90 3171.15 +19.2%
s100.pipeline.stage.duration 5.39 5.38 -0.2% ▫️
s100.pipeline.stage.duration 5.85 5.27 -9.9% ▫️
s100.pipeline.stage.duration 1.08 1.19 +10.1%
s100.pipeline.stage.duration 0.94 0.93 -0.6% ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.portrayal.cache.hit.count 12.00 12.00 +0.0% ▫️
s100.portrayal.cache.hit.count 552.00 552.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 96.00 96.00 +0.0% ▫️
s100.portrayal.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.portrayal.cache.miss.count 43.00 43.00 +0.0% ▫️
s100.portrayal.cache.miss.count 16.00 16.00 +0.0% ▫️
s100.render.frame.duration 744.76 744.49 -+0.0% ▫️
s100.render.instructions.processed.count 4004.00 4004.00 +0.0% ▫️
s100.render.styles.applied.count 4256.00 4256.00 +0.0% ▫️
s100.symbol.cache.hit.count 364.00 364.00 +0.0% ▫️
s100.symbol.cache.miss.count 98.00 98.00 +0.0% ▫️
s100.symbol.resolve.duration 0.33 0.26 -20.6%
s100.symbol.resolve.duration 17.24 17.31 +0.4% ▫️

s101-portray-warm

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 277.16 283.08
Baseline MAD (ms) 15.68
Δ median +2.1%
z (Δ/MAD) +0.38

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 6651.09 6735.93 +1.3% ▫️
s100.lua.rule.invoke 5948.22 5954.91 +0.1% ▫️
s100.pipeline.vector.process 6978.31 7040.60 +0.9% ▫️
s100.pipeline.vector.stage.assemble 0.23 0.22 -5.8% ▫️
s100.pipeline.vector.stage.feature_xml 297.30 277.00 -6.8% ▫️
s100.pipeline.vector.stage.lua 6652.50 6737.43 +1.3% ▫️
s100.pipeline.vector.stage.rule_select 4.79 4.56 -4.9% ▫️
s100.pipeline.vector.stage.sort 17.12 15.43 -9.9% ▫️
s100.pipeline.vector.stage.viewing_groups 19.00 17.19 -9.5% ▫️
s100.pipeline.vector.stage.xslt 0.28 0.27 -3.7% ▫️
s100.render.frame 253.75 243.12 -4.2% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.lua.execute.duration 1809.66 1975.10 +9.1%
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 42.00 42.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 217.00 217.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 21.00 21.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.features.count 2478.00 2478.00 +0.0% ▫️
s100.lua.instructions.emitted.count 4004.00 4004.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 1626.40 1737.90 +6.9%
s100.lua.rule.invoke.duration 2.05 2.20 +7.0%
s100.lua.source.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.pattern.cache.hit.count 222.00 222.00 +0.0% ▫️
s100.pattern.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 4004.00 4004.00 +0.0% ▫️
s100.pipeline.duration 1885.08 2032.48 +7.8%
s100.pipeline.features.in 217.00 217.00 +0.0% ▫️
s100.pipeline.stage.duration 0.02 0.02 -2.7% ▫️
s100.pipeline.stage.duration 69.47 52.03 -25.1%
s100.pipeline.stage.duration 1809.96 1975.36 +9.1%
s100.pipeline.stage.duration 1.03 1.03 +0.1% ▫️
s100.pipeline.stage.duration 2.83 2.53 -10.4%
s100.pipeline.stage.duration 0.29 0.28 -2.4% ▫️
s100.pipeline.stage.duration 0.05 0.04 -22.8%
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.portrayal.cache.hit.count 2.00 2.00 +0.0% ▫️
s100.portrayal.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 16.00 16.00 +0.0% ▫️
s100.render.frame.duration 125.88 123.59 -1.8% ▫️
s100.render.instructions.processed.count 4004.00 4004.00 +0.0% ▫️
s100.render.styles.applied.count 4256.00 4256.00 +0.0% ▫️
s100.symbol.cache.hit.count 448.00 448.00 +0.0% ▫️
s100.symbol.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.symbol.resolve.duration 0.23 0.24 +7.1%
s100.symbol.resolve.duration 1.96 1.40 -28.4%

s101-real-cold

Iteration statistics

Stat Baseline Candidate
Samples 5 5
Median (ms) 0.24 0.27
Baseline MAD (ms) 0.01
Δ median +11.6%
z (Δ/MAD) +4.41

s101-real-warm

Iteration statistics

Stat Baseline Candidate
Samples 5 5
Median (ms) 0.28 0.28
Baseline MAD (ms) 0.04
Δ median -0.8%
z (Δ/MAD) -0.06

s101-render-warm

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 258.60 268.02
Baseline MAD (ms) 9.71
Δ median +3.6%
z (Δ/MAD) +0.97

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 6318.83 6741.87 +6.7%
s100.lua.rule.invoke 5648.87 6007.63 +6.4%
s100.pipeline.vector.process 6439.91 6839.34 +6.2%
s100.pipeline.vector.stage.assemble 0.18 0.18 +1.8% ▫️
s100.pipeline.vector.stage.feature_xml 103.81 79.24 -23.7%
s100.pipeline.vector.stage.lua 6319.87 6743.00 +6.7%
s100.pipeline.vector.stage.rule_select 3.11 2.90 -7.0% ▫️
s100.pipeline.vector.stage.sort 8.79 9.95 +13.1%
s100.pipeline.vector.stage.viewing_groups 10.22 11.54 +13.0%
s100.pipeline.vector.stage.xslt 0.19 0.18 -4.8% ▫️
s100.render.frame 140.65 161.72 +15.0%

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.lua.execute.duration 1724.98 1757.01 +1.9% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 63.00 63.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 42.00 42.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 217.00 217.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 21.00 21.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 49.00 49.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 840.00 840.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 7.00 7.00 +0.0% ▫️
s100.lua.feature.instructions.count 28.00 28.00 +0.0% ▫️
s100.lua.feature.instructions.count 14.00 14.00 +0.0% ▫️
s100.lua.features.count 2478.00 2478.00 +0.0% ▫️
s100.lua.instructions.emitted.count 4004.00 4004.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 1541.97 1575.45 +2.2% ▫️
s100.lua.rule.invoke.duration 1.79 2.04 +13.9%
s100.lua.source.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.pattern.cache.hit.count 222.00 222.00 +0.0% ▫️
s100.pattern.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 4004.00 4004.00 +0.0% ▫️
s100.pipeline.duration 1751.05 1785.61 +2.0% ▫️
s100.pipeline.features.in 217.00 217.00 +0.0% ▫️
s100.pipeline.stage.duration 0.02 0.02 -4.1% ▫️
s100.pipeline.stage.duration 21.25 24.05 +13.2%
s100.pipeline.stage.duration 1725.20 1757.28 +1.9% ▫️
s100.pipeline.stage.duration 0.86 0.76 -11.8%
s100.pipeline.stage.duration 2.62 2.31 -11.6%
s100.pipeline.stage.duration 0.25 0.26 +4.8% ▫️
s100.pipeline.stage.duration 0.03 0.03 -12.4%
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 4004.00 4004.00 +0.0% ▫️
s100.portrayal.cache.hit.count 2.00 2.00 +0.0% ▫️
s100.portrayal.cache.hit.count 595.00 595.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 16.00 16.00 +0.0% ▫️
s100.render.frame.duration 109.06 108.52 -0.5% ▫️
s100.render.instructions.processed.count 4004.00 4004.00 +0.0% ▫️
s100.render.styles.applied.count 4256.00 4256.00 +0.0% ▫️
s100.symbol.cache.hit.count 448.00 448.00 +0.0% ▫️
s100.symbol.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.symbol.resolve.duration 0.23 0.21 -11.4%
s100.symbol.resolve.duration 1.00 1.07 +7.2%

s102-coverage

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 1.36 1.27
Baseline MAD (ms) 0.19
Δ median -6.4%
z (Δ/MAD) -0.45

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.coverage.process 69.76 66.26 -5.0% ▫️
s100.pipeline.coverage.stage.read 5.02 5.32 +6.1%
s100.pipeline.coverage.stage.resolve 61.19 57.02 -6.8% ▫️
s100.render.coverage.build 82.76 83.66 +1.1% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.coverage.cells 4557.00 4557.00 +0.0% ▫️
s100.hdf5.read.bytes 5208.00 5208.00 +0.0% ▫️
s100.hdf5.read.duration 23.05 22.82 -1.0% ▫️
s100.hdf5.read.duration 30.98 31.43 +1.5% ▫️
s100.hdf5.read.duration 7.86 7.71 -2.0% ▫️
s100.pipeline.duration 11.63 12.37 +6.3%
s100.pipeline.stage.duration 0.93 1.02 +10.5%
s100.pipeline.stage.duration 10.20 10.70 +4.9% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️

s102-coverage-open

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 3.06 3.03
Baseline MAD (ms) 0.13
Δ median -0.8%
z (Δ/MAD) -0.18

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.dataset.open 85.67 84.75 -1.1% ▫️
s100.hdf5.dataset.read 14.10 14.03 -0.5% ▫️
s100.hdf5.file.open 20.74 20.43 -1.5% ▫️
s100.hdf5.open 21.28 20.77 -2.4% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.hdf5.read.bytes 36456.00 36456.00 +0.0% ▫️
s100.hdf5.read.duration 6.88 7.10 +3.2% ▫️
s100.hdf5.read.duration 2.72 2.91 +6.7%
s100.hdf5.read.duration 4.96 4.78 -3.6% ▫️

s102-coverage-render-large

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 131.71 132.71
Baseline MAD (ms) 1.13
Δ median +0.8%
z (Δ/MAD) +0.88

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.coverage.process 200.07 190.77 -4.6% ▫️
s100.pipeline.coverage.stage.read 150.09 141.87 -5.5% ▫️
s100.pipeline.coverage.stage.resolve 45.96 45.17 -1.7% ▫️
s100.render.coverage.build 4647.79 4861.57 +4.6% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.coverage.cells 7000000.00 7000000.00 +0.0% ▫️
s100.hdf5.read.bytes 8000000.00 8000000.00 +0.0% ▫️
s100.hdf5.read.duration 0.12 0.10 -19.4%
s100.hdf5.read.duration 2.75 2.67 -3.1% ▫️
s100.hdf5.read.duration 1.31 1.21 -7.9% ▫️
s100.pipeline.duration 39.05 35.03 -10.3%
s100.pipeline.stage.duration 30.29 25.93 -14.4%
s100.pipeline.stage.duration 7.99 8.35 +4.5% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️

s102-real-warm

Iteration statistics

Stat Baseline Candidate
Samples 5 5
Median (ms) 0.25 0.25
Baseline MAD (ms) 0.02
Δ median +2.8%
z (Δ/MAD) +0.38

s111-real-warm

Iteration statistics

Stat Baseline Candidate
Samples 5 5
Median (ms) 0.22 0.23
Baseline MAD (ms) 0.00
Δ median +5.6%
z (Δ/MAD) +13.44

s124-vector

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.31 0.32
Baseline MAD (ms) 0.03
Δ median +3.3%
z (Δ/MAD) +0.39

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.vector.process 7.71 7.58 -1.8% ▫️
s100.pipeline.vector.stage.assemble 0.28 0.30 +4.8% ▫️
s100.pipeline.vector.stage.feature_xml 0.95 1.00 +6.0%
s100.pipeline.vector.stage.rule_select 0.27 0.30 +11.4%
s100.pipeline.vector.stage.sort 0.22 0.21 -4.6% ▫️
s100.pipeline.vector.stage.viewing_groups 0.49 0.46 -7.6% ▫️
s100.pipeline.vector.stage.xslt 3.77 3.64 -3.4% ▫️
s100.render.frame 1.01 1.15 +13.8%
s100.xslt.transform 1.56 1.56 -0.1% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 14.00 14.00 +0.0% ▫️
s100.pipeline.duration 58.82 67.77 +15.2%
s100.pipeline.features.in 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.duration 1.11 1.14 +2.3% ▫️
s100.pipeline.stage.duration 3.32 3.53 +6.2%
s100.pipeline.stage.duration 1.17 1.25 +6.9%
s100.pipeline.stage.duration 0.07 0.07 -0.5% ▫️
s100.pipeline.stage.duration 0.09 0.11 +24.0%
s100.pipeline.stage.duration 52.59 61.26 +16.5%
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.portrayal.cache.hit.count 13.00 13.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.render.frame.duration 2.52 2.46 -2.4% ▫️
s100.render.instructions.processed.count 14.00 14.00 +0.0% ▫️
s100.render.styles.applied.count 14.00 14.00 +0.0% ▫️
s100.xslt.transform.duration 11.41 15.56 +36.4%

s201-vector

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.35 0.33
Baseline MAD (ms) 0.03
Δ median -3.7%
z (Δ/MAD) -0.40

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.vector.process 7.77 7.64 -1.6% ▫️
s100.pipeline.vector.stage.assemble 0.18 0.17 -4.1% ▫️
s100.pipeline.vector.stage.feature_xml 0.87 0.91 +4.5% ▫️
s100.pipeline.vector.stage.rule_select 0.24 0.25 +1.6% ▫️
s100.pipeline.vector.stage.sort 0.10 0.09 -4.2% ▫️
s100.pipeline.vector.stage.viewing_groups 0.38 0.35 -7.1% ▫️
s100.pipeline.vector.stage.xslt 3.31 3.30 -0.3% ▫️
s100.render.frame 0.76 0.72 -5.3% ▫️
s100.xslt.transform 2.22 2.20 -1.0% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 7.00 7.00 +0.0% ▫️
s100.pipeline.duration 161.03 195.92 +21.7%
s100.pipeline.features.in 21.00 21.00 +0.0% ▫️
s100.pipeline.stage.duration 0.59 0.62 +4.2% ▫️
s100.pipeline.stage.duration 7.31 11.99 +64.2%
s100.pipeline.stage.duration 0.13 0.24 +82.5%
s100.pipeline.stage.duration 0.02 0.02 -11.8%
s100.pipeline.stage.duration 0.05 0.05 -6.4% ▫️
s100.pipeline.stage.duration 152.52 182.65 +19.8%
s100.pipeline.stage.instructions.count 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.render.frame.duration 1.16 1.09 -5.6% ▫️
s100.render.instructions.processed.count 7.00 7.00 +0.0% ▫️
s100.render.styles.applied.count 14.00 14.00 +0.0% ▫️
s100.symbol.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.symbol.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.symbol.resolve.duration 0.01 0.01 -42.2%
s100.symbol.resolve.duration 0.86 0.82 -4.6% ▫️
s100.xslt.transform.duration 27.90 30.79 +10.4%

Generated by EncDotNet.S100.PerfReport gate command

@philliphoff philliphoff merged commit d3909e3 into main Jun 1, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant