Skip to content

PI: Optimize retrieval of named destinations in reader - #3442

Merged
stefan6419846 merged 1 commit into
py-pdf:mainfrom
larsga:optimize-links
May 12, 2026
Merged

stefan6419846 merged 1 commit into
py-pdf:mainfrom
larsga:optimize-links

Conversation

@larsga

@larsga larsga commented Aug 21, 2025

Copy link
Copy Markdown
Contributor

This optimization solves the performance issue introduced by PR #3400.

Without this optimization test_merger.py takes 124 seconds on my laptop after PR 3400. With this optimization it takes 6.6 seconds.

Very likely this optimization will benefit other code as well.

@larsga
larsga force-pushed the optimize-links branch 2 times, most recently from 96cb457 to ae6835f Compare August 21, 2025 08:31
@larsga

larsga commented Aug 21, 2025

Copy link
Copy Markdown
Contributor Author

@stefan6419846 I need some help with this. ruff and Python both are complaining about Dict in my added code, claiming it's not declared. It is imported on line 39, though. And other code uses it in exactly the same way, without ruff or Python complaining. It works for me in both Python and ruff locally. What is going on?

@stefan6419846

Copy link
Copy Markdown
Collaborator

Please try to rebase on the latest main - your base appears to be outdated and the corresponding import is gone in main.

@larsga
larsga force-pushed the optimize-links branch 3 times, most recently from 2c3fb66 to 8465c0d Compare August 21, 2025 19:21
@codecov

codecov Bot commented Aug 21, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.64%. Comparing base (885049b) to head (5139db4).
⚠️ Report is 139 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3442   +/-   ##
=======================================
  Coverage   97.64%   97.64%           
=======================================
  Files          55       55           
  Lines       10227    10234    +7     
  Branches     1878     1880    +2     
=======================================
+ Hits         9986     9993    +7     
  Misses        137      137           
  Partials      104      104           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@larsga

larsga commented Aug 21, 2025

Copy link
Copy Markdown
Contributor Author

That worked. Thank you! The PR should be ready now.

@stefan6419846

Copy link
Copy Markdown
Collaborator

Thanks for the PR. This code seems to assume that the named destinations never change. Is this really a valid assumption or will this create unexpected behavior in specific cases?

@larsga

larsga commented Sep 8, 2025

Copy link
Copy Markdown
Contributor Author

It does assume the destinations never change. Originally I put this cache on the shared superclass for readers and writers, but then had the same realization as you. So now this code is only in the PdfReader class. That works for the purposes of the link rewriting code etc, but it does mean that getting named destinations from ´PdfWriter` is as slow as it used to be.

@stefan6419846

Copy link
Copy Markdown
Collaborator

Thanks for the explanation - indeed, in the reader it should not be an issue. Are we able to add a test for this?

@stefan6419846 stefan6419846 added the needs-test A test should be added before this PR is merged. label Sep 11, 2025
@larsga

larsga commented Sep 11, 2025

Copy link
Copy Markdown
Contributor Author

Hmmm. What sort of tests are you looking for? Tests of changing the named destinations in the reader? I guess not, since that's not supposed to be possible. Tests of changing the named destinations in the writer? It doesn't use this code, but I guess I could add. Or just tests of reading named destinations at all? Those might already exist for all I know, but I could check, once I know what you're thinking.

@stefan6419846

Copy link
Copy Markdown
Collaborator

Sorry for not being explicit enough - I am referring to the fact that these are cached. One possible approach would be to check the time for handling many destinations the first and the second time, although I am open for other tests as well.

@larsga

larsga commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

We really need this optimization. We just had a publishing job time out because of this issue. When I try that publishing job on my laptop, the job got to page 68 while I was having lunch. So in roughly half an hour. It's not really workable.

With this optimization it does the entire 1100-page document in 8 minutes and 24 seconds. If we assume 100% constant speed pr page then this is a speedup by a factor of 60.

What's holding this up is that I don't understand what tests you want. Please, can you explain so that I know what I need to do to get this code into the project?

Comment thread pypdf/_reader.py Outdated
@stefan6419846

Copy link
Copy Markdown
Collaborator

What's holding this up is that I don't understand what tests you want. Please, can you explain so that I know what I need to do to get this code into the project?

I want a test that shows the caching. This could be a speed-based test for both retrievals or one based upon mocking:

from unittest import mock


def test_get_named_destinations__caching():
    url = "..."  # TODO: Fill.
    name = "PDF32000_2008.pdf"
    reader = PdfReader(BytesIO(get_data_from_url(name=name)))

    destinations = list(reader.named_destinations)
    assert len(destinations) > 10

    with mock.patch("pypdf._doc_common.PdfDocCommon._get_named_destinations") as get_mock:
        destinations2 = list(reader.named_destinations)
    assert destinations2 == destinations
    get_mock.assert_not_called()

@larsga
larsga force-pushed the optimize-links branch 7 times, most recently from 3fbfac7 to 9aff1ab Compare May 11, 2026 16:31
@larsga

larsga commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

@stefan6419846 Please have another look now.

Comment thread tests/test_reader.py
@stefan6419846 stefan6419846 removed the needs-test A test should be added before this PR is merged. label May 12, 2026

@stefan6419846 stefan6419846 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Looks good for me now.

@stefan6419846 stefan6419846 changed the title ENH: Optimize getting named destinations PERF: Optimize retrieval of named destinations in reader May 12, 2026
@stefan6419846 stefan6419846 changed the title PERF: Optimize retrieval of named destinations in reader PI: Optimize retrieval of named destinations in reader May 12, 2026
@stefan6419846
stefan6419846 merged commit 4dc9494 into py-pdf:main May 12, 2026
19 of 20 checks passed
stefan6419846 added a commit that referenced this pull request May 21, 2026
## What's new

### Security (SEC)
- Disallow cross-reference streams with zero-only width values (#3791) by @stefan6419846
- Avoid excessive whitespace in layout mode text extraction (#3790) by @stefan6419846

### New Features (ENH)
- Implement SASLprep (RFC 4013) for AES-256 password normalization (#3780) by @adityamoolya
- CID font resource from font file to encode more characters (#3652) by @PJBrs

### Performance Improvements (PI)
- Optimize retrieval of named destinatinos in reader (#3442) by @larsga

### Bug Fixes (BUG)
- Fix TreeObject.insert_child KeyError on fresh children (#3786) by @Abzaek

### Robustness (ROB)
- AppearanceStream: Also honor user-set font name when not flattening annotations (#3781) by @PJBrs

### Documentation (DOC)
- Block encrypting writer in incremental mode (#3789) by @stefan6419846

[Full Changelog](6.11.0...6.12.0)
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