Repository navigation
Documents: read long .docx files about a third faster when opening - #140
Merged
Merged
Conversation
Reading a 3 MB document with 1,500 paragraphs, 176 lists and 300 hyperlinks spent most of its time repeating work. Looking up an element's children copied the live children collection into an array (tens of thousands of times: every property of every paragraph, run, style and list level); the readers now walk siblings, several times faster. The main document's relationships (60 KB) were parsed again by the theme colors, hyperlinks, mail merge and header/footer readers, and each hyperlink scanned every relationship; they are now read once and indexed. Each list level read its paragraph properties twice, and the document declares 1,584 levels with only 36 distinct ones: a level's properties are now read once and each distinct level once, every level still getting its own copy. Serializing paragraph sources indexes attributes rather than copying them. DocxPackage.open on that document in Chrome: 505 ms -> 367 ms (median of 9). What is read and saved is unchanged: the opened package and its saved and edited exports are identical for all 2,257 corpus documents.
Opening kept the XML of every body paragraph, so that a save after other edits writes it unchanged. Each one is serialized with the two dozen namespace declarations it inherits from the document, about 3 MB of text for a 1,500-paragraph document, and most of it is never used: an unedited save writes the original part, and a read-only view saves nothing. Opening now copies each paragraph and the declarations it inherits, which later changes to the tree cannot reach, and serializes the copy when the source is first asked for. DocxPackage.open on that document in Chrome: 367 ms -> 325 ms (median of 9). The first save after an edit serializes the paragraphs instead. The opened package and its saved and edited exports are identical for all 2,257 corpus documents.
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.
Follow-up to #139, stacked on it (base:
docs-latency/a-docx). This one speeds up reading a .docx when it opens.Results (Chrome, same 28-page, 1,500-paragraph test document as #139)
DocxPackage.open(median of 9)The JS reading work dropped from ~315 ms to ~135 ms. What remains is mostly native XML parsing and inflate.
Causes and fixes
child()/children()call copied the livechildrencollection into an array, tens of thousands of times per open. They now walk siblings through helpers indocx-xml.jsthat keep the same results and exceptions, used across the reader modules.serializeFragmentindexes attributes instead of copying them.Verification
main.docx-read-reuse.test.js, built from synthetic markup:[...node.children].Not done (would change behavior or need a broader audit)
TextDocument's re-resolution of already-resolved paragraphs: one preview-recovered fixture differs.DecompressionStreaminstead of fflate: its errors on corrupt archives differ.🤖 Generated with Claude Code