Skip to content

Fix O(N²) page list update in pages setter (freezes UI on huge documents) - #694

Merged
espresso3389 merged 1 commit into
espresso3389:masterfrom
Guesub:fix/pages-setter-linear-scan
Aug 19, 2026
Merged

Fix O(N²) page list update in pages setter (freezes UI on huge documents)#694
espresso3389 merged 1 commit into
espresso3389:masterfrom
Guesub:fix/pages-setter-linear-scan

Conversation

@Guesub

@Guesub Guesub commented Aug 19, 2026

Copy link
Copy Markdown

Problem

_PdfDocumentPdfium.pages setter looks up each page's previous index with _pages.indexWhere((p) => identical(p, newPage)). During progressive page loading (loadPagesProgressively_loadPagesInLimitedTime), every batch recreates all trailing (not-yet-loaded) page instances, so none of them hit the identical fast path and each one triggers a full O(N) scan — making every batch O(N²) on the caller's isolate (the UI isolate in Flutter apps).

For a 47,352-page document (a 1.9 GB aircraft maintenance manual), that is roughly 2.2 billion identity comparisons per batch: measured ~5.5 s of main-isolate freeze after every ~250 ms load batch, repeating for the entire progressive-load run. The viewer is effectively frozen the whole time (0.3–10 fps while scrolling). Small documents don't show it because the cost is quadratic in page count.

Fix

Build an identity map of the previous pages once per call and look up old indexes in O(1). HashMap.identity() preserves the exact identical() semantics of the original lookup, and _pages never contains duplicate identities (both internal producers create fresh instances per position), so behavior is unchanged.

Results (iPhone, 1.9 GB / 47,352-page PDF)

before after
main-isolate stall per load batch 5.1–5.9 s none observed
scroll fps 0.3–10 46–113
max frame gap ~5,700 ms 19–274 ms

The pages setter ran _pages.indexWhere(identical) for every non-identical
page. During progressive page loading every batch recreates all unloaded
trailing pages, so each batch cost O(N^2) identity comparisons on the main
isolate -- about 5.5s per batch for a 47,352-page document (measured on an
iPhone with a 1.9GB manual), repeating until all pages are loaded and
freezing the UI the whole time.

Build an identity map of the previous pages once per call and look up old
indexes in O(1), preserving the exact identical() semantics.
@espresso3389
espresso3389 merged commit d3729a1 into espresso3389:master Aug 19, 2026
10 of 12 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.

2 participants