Skip to content

perf: optimize XML hot-part deserialization with streaming and zero-copy#50

Merged
Nebu1eto merged 2 commits into
mainfrom
perf/phase2-xml-hotpart
Feb 11, 2026
Merged

perf: optimize XML hot-part deserialization with streaming and zero-copy#50
Nebu1eto merged 2 commits into
mainfrom
perf/phase2-xml-hotpart

Conversation

@Nebu1eto

Copy link
Copy Markdown
Owner

Summary

Three targeted optimizations eliminating the primary allocation hotspots in the XML read path:

  1. Streaming XML deserialization: Replace read_to_string + from_str with from_reader(BufReader) for all XML parts, eliminating multi-MB intermediate String allocations
  2. Zero-allocation cell deserialization: Custom serde Visitors for CompactCellRef and CellTypeTag using visit_str(&str) instead of String::deserialize, eliminating 2 heap allocations per cell
  3. Zero-copy SST construction: Use std::mem::take to move owned strings from parsed SST directly into Arc<str>, avoiding clone for plain-text entries

Performance Impact (estimated for 100k x 20 workbook)

  • ~100MB less peak memory (eliminated worksheet XML String buffer)
  • ~2M fewer heap allocations (cell-level deserialization)
  • ~10k fewer String clones (SST construction)
  • Target: Read Scale 100k >= 8% additional improvement

Changes

  • crates/sheetkit-core/src/workbook/io.rs: from_reader(BufReader) with adaptive capacity (8KB-64KB)
  • crates/sheetkit-xml/src/worksheet.rs: Custom CompactCellRefVisitor + CellTypeTagVisitor
  • crates/sheetkit-core/src/sst.rs: std::mem::take for plain-text SST entries

Test plan

  • 6 new tests (from_reader worksheet/SST/large, zero-copy plain/mixed/empty)
  • 1,978 Rust tests pass
  • clippy clean, fmt clean
  • Stacked on Phase 1 (ReadFast mode)

🤖 Generated with Claude Code

@claude

claude Bot commented Feb 11, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@Nebu1eto
Nebu1eto force-pushed the perf/phase2-xml-hotpart branch from 8250ff8 to 82ae33b Compare February 11, 2026 16:01
Base automatically changed from perf/phase1-open-readfast to main February 11, 2026 16:03
Nebu1eto and others added 2 commits February 12, 2026 01:08
Switch read_xml_part from read_to_string + from_str to
quick_xml::de::from_reader with a size-aware BufReader (up to 64 KB).
This eliminates the intermediate String allocation for all XML parts.

Optimize per-cell deserialization: CompactCellRef and CellTypeTag now
use serde Visitor implementations that accept &str directly, avoiding
a heap String allocation per cell reference and type attribute.

Optimize SharedStringTable::from_sst to take ownership of plain-text
String values via std::mem::take instead of cloning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Nebu1eto
Nebu1eto force-pushed the perf/phase2-xml-hotpart branch from 82ae33b to c8e1571 Compare February 11, 2026 16:09
@Nebu1eto
Nebu1eto merged commit 54103b5 into main Feb 11, 2026
7 checks passed
@Nebu1eto
Nebu1eto deleted the perf/phase2-xml-hotpart branch February 11, 2026 16:16
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