Repository navigation
perf(streaming): speed up iterRows on wide sheets of styled empty cells (#302) - #303
Merged
Merged
Conversation
…ls (#302) A sheet whose rows carry hundreds of `<c r="X1" s="n"/>` cells is nearly all SAX events and almost no values, so the per-event cost of the row walk dominated: each event was allocated as an object with Clark-notation names and attributes and handed through an async generator step. The walk now registers its handlers on saxes directly and only crosses an async boundary once per 64K feed, which brings it under loadWorkbook's time for the same bytes instead of 1.5–3x over it. The saxes driver moves to an internal module so no public declaration imports saxes' types, which do not type-check under skipLibCheck: false. coordinateToTuple also reads the plain "AB12" shape off char codes ahead of the regex, which both readers call once per cell. Closes #302 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0142ArZjzXh8UP4dVS623qG3
The corpus case that guards CDATA reads through loadWorkbookStream, which no longer goes through iterParse, so the mutant has to sit on the handler the row walk registers itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0142ArZjzXh8UP4dVS623qG3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
loadWorkbookStream'siterRowswas several times slower than it needed to be on wide sheets where almost every cell is empty but styled (<c r="X1" s="n"/>). The row walk now drives saxes directly instead of consuming a per-event async iterator, so it runs faster thanloadWorkbookon the same bytes instead of 1.5–3x slower.Motivation
#302 reports ~46 µs per cell on a 30,000 × 227 sheet with 0.4% of cells holding a value. A profile showed saxes itself was a small part of the time. Most of it went to the
iterParselayer: one event object per SAX event, Clark-notation names and attribute maps, and an async generator step for each event.Closes #302
Changes
iterSheetRows(behindReadOnlyWorksheet.iterRows/iterValues) registers its handlers on aSaxesParserand collects finished rows synchronously. It crosses an async boundary once per 64K feed instead of once per event. Rows completed before a content-limit error or a corrupt cell are still yielded before the error.src/xml/iterparse.tsto the internalsrc/xml/sax-feed.ts, anditerParseis now built on it. It lives in its own module so no public.d.tsimports saxes types. saxes' own declarations fail underskipLibCheck: false, andcheck:consumercaught this.coordinateToTupleparses the plain"AB12"form from char codes before falling back to the regex. Both readers call it once per cell.Testing
pnpm test: 3761 tests).tests/perf/iter-rows-throughput.test.ts: 5,000 × 227 styled cells, comparingiterRowstime againstloadWorkbookon the same bytes. Onmainit fails (ratio 1.12). On this branch it passes (0.81–0.84).main4.2–19.9 s, this branch 0.9–3.2 s.Breaking changes
None
Checklist
flagged it above.
represents real work that warrants a maintainer's review, and I am willing to
defend each line in review.
🤖 Generated with Claude Code
https://claude.ai/code/session_0142ArZjzXh8UP4dVS623qG3