Skip to content

perf(writer): remove per-cell serializer allocations - #12

Merged
wolfiesch merged 2 commits into
mainfrom
perf/serializer-allocations-20260905
Sep 5, 2026
Merged

perf(writer): remove per-cell serializer allocations#12
wolfiesch merged 2 commits into
mainfrom
perf/serializer-allocations-20260905

Conversation

@wolfiesch

@wolfiesch wolfiesch commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Use stack-backed A1 references, direct numeric formatting and direct shared-string escaping. Preserve legacy floating-point formatting and XML whitespace/entity semantics.

Before / after evidence

Baseline 43d93c63fa6ecfd238134d2a76ba6f270b18fd19; candidate 922fe9af37d62d730603b1b514c37720a6fa17a7. Both use identical writer_hotpaths benchmark source (added by the serializer PR; copy it into the original baseline checkout before building). Linux x86_64 AMD EPYC 9V74, Rust 1.98.1, checked-in release fat LTO / one codegen unit. Native complete save (XML + ZIP), with workbook population excluded from timed and allocation-counted regions.

Workload Median ms base → candidate p95 ms base → candidate Allocations base → candidate Cumulative allocated MiB Peak process RSS MiB
numeric 17.609 → 12.932 19.033 → 14.533 300,303 → 303 13.95 → 12.06 17.85 → 17.91
repeated-strings 17.585 → 14.905 22.429 → 18.116 200,701 → 445 13.22 → 12.09 20.78 → 19.55
unique-strings 49.345 → 37.242 54.459 → 45.948 600,337 → 100,337 50.02 → 36.50 40.55 → 40.49
escaped-strings 54.506 → 42.727 60.437 → 51.091 700,337 → 100,337 58.03 → 36.88 41.80 → 41.75
large-numeric 203.563 → 146.759 203.563 → 146.759 3,000,310 → 310 103.53 → 83.71 130.26 → 130.29

Numeric saves remove exactly 300,000 allocation calls and improve 1.362× at 100k cells. Unique strings remove 500,000 calls and improve 1.325×. Peak RSS is largely unchanged; the benefit is latency and allocation traffic.

Ordinary rows are 100,000 total cells: two ABBA cycles, seven measured saves after a warmup per invocation, 28 samples per side/workload. The large-* row is 1,000,000 total cells with one measured save per side after warmup: a directional scale/RSS check, not a robust latency claim. One sheet uses 5,000×20 cells normally, 50,000×20 at scale. Peak RSS comes from an isolated child process and includes population, warmup and save; cumulative allocated bytes are allocation traffic, not peak live memory. No competing task builds or benchmarks ran during measurement.

Every uncompressed ZIP member matched SHA256 in every run. Entire ZIP archives were byte-identical. Output sizes and archive/member hashes are in the raw evidence.

Raw measurements, commands and binary hashes.

Correctness and reproduction

cargo test -p wolfxl-writer passes on the candidate. Added all-column address boundary equivalence plus 10,000 deterministic floating-point bit-pattern checks, including NaN, infinities and integer boundaries. Existing writer roundtrip, streaming and feature tests remain green. The baseline already increments shared-string reference counts on repeated saves; tests compare matching save ordinals rather than claim an unchanged string workbook is byte-stable across saves.

# In both pinned source checkouts, using identical benchmark helper:
cargo build --release -p wolfxl-writer --example writer_hotpaths
# Preserve the base and candidate binaries separately, then:
python scripts/performance/measure_writer_pair.py /path/base /path/candidate /tmp/evidence 

Native measurements do not imply the same percentage improvement for complete Python workloads.

CI follow-up

Workspace CI exposed pre-existing formatting in crates/wolfxl-cli/src/render.rs. A whitespace-only follow-up fixes its two expressions and synchronizes the stack. The measured writer code and benchmark binaries remain unchanged; the baseline/candidate SHAs above identify the measured revisions.

@wolfiesch
wolfiesch merged commit 4177ee8 into main Sep 5, 2026
4 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