Skip to content

Comment TIFF tag debug messages - #10289

Open
simonhf wants to merge 4 commits into
python-pillow:mainfrom
simonhf:lazy-tiff-debug
Open

simonhf wants to merge 4 commits into
python-pillow:mainfrom
simonhf:lazy-tiff-debug

Conversation

@simonhf

@simonhf simonhf commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Changes proposed in this pull request:

  • ImageFileDirectory_v2.load() built a debug message for every IFD entry (a TiffTags.lookup(), several f-strings and a repr() of the value) and passed it to logger.debug(), even when debug logging was off. As suggested in review, those debug lines are now commented out, so they can be restored when debugging Pillow.

This makes opening TIFF images and reading EXIF noticeably cheaper, since both go through this loop. On an Apple M3 (best of 5), opening Tests/images/hopper.tif goes from 97 to 77 usec, and opening the 75 TIFFs in Tests/images that open cleanly goes from 7.6 to 5.9 ms.

For EXIF, with Tests/images/flower.jpg (40 tags), opening the image and reading its EXIF IFD:

python -m timeit -s "from PIL import Image" "with Image.open('Tests/images/flower.jpg') as im: im.getexif().get_ifd(0x8769)"
best of 5, three alternating runs main this PR
Linux x86_64, CPython 3.14.6 262-263 usec 209-211 usec
macOS arm64 (Apple M3), CPython 3.14.8 151-152 usec 123-126 usec

On a larger EXIF-reading workload (20 JPEGs with EXIF, 36,000 opens plus getexif() per run, 15 runs of each side alternated, Linux), the run time drops from 3.160 s to 2.949 s (median), 6.7% faster, with run-to-run variation of 0.7% on each side.

Before #4550 these lines were inside if DEBUG:, so the message was only built when it was printed. #10274 edits the neighbouring lines of the same loop; I'm happy to rebase whichever lands second.

The full test suite passes (5,655 passed; test_imagegrab.py::TestImageGrab::test_grab fails on my machine with or without this change, as screencapture is not permitted there).

I used an AI coding assistant to find and write this change; I reviewed and tested it, and the measurements are from my own runs.

ImageFileDirectory_v2.load() built a debug message for every IFD
entry, including a tag-name lookup and a repr() of the value, even
when debug logging was disabled. Check logger.isEnabledFor() once per
IFD and only build and log the message when it will be emitted.

Assisted-by: LLM
Signed-off-by: Simon Hardy-Francis <simon@linux.com>
@radarhere radarhere added the TIFF label Oct 22, 2026
logger.debug() already does nothing when debug logging is disabled, and these branches are not on the hot path.

Assisted-by: LLM
Signed-off-by: Simon Hardy-Francis <simon@linux.com>
@radarhere radarhere added the 🤖-assisted AI-assisted label Oct 22, 2026
@radarhere

Copy link
Copy Markdown
Member

This is a minor tweak for performance - I don't think it needs a test.

I find the second commit interesting. You reverted the change for empty or invalid tags. I expect you did this because you thought the extra code complexity wasn't worth the minor speed increase. But you think the extra code complexity in the other scenarios you've described here is? The performance test you've chosen isn't the generic opening of an image, but reading the EXIF IFD from an image. It's a bit specific, and I'm not sure where the line is here.

If it's a problem, I'd personally be willing to just comment out all the debugging within load(). We changed the debugging mechanism six years ago in #4550, and no one's said anything about it since.

@simonhf

simonhf commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for removing the test, and that's a fair question. To be honest, the second commit was prompted by codecov. Once the message was only built under if debug:, the guarded lines in those three branches weren't covered. Those branches only run for unusual or corrupt tags, so logging unconditionally there costs nothing measurable. The line I was drawing was "guard what runs for every tag", not a judgement about complexity.

On the benchmark, it isn't only EXIF: opening any TIFF goes through load() for its first IFD. On my M3 (best of 5), opening Tests/images/hopper.tif goes from 97 to 77 µs, and opening the 75 TIFFs in Tests/images that open cleanly goes from 7.6 to 5.9 ms, about 20% faster.

I also tried your suggestion of removing the debugging from load() entirely. It's as fast as this PR, within 1–2%, and simpler. I'm happy with either. If you'd prefer that, I'll update the PR to remove those lines instead.

@radarhere

Copy link
Copy Markdown
Member

Thanks for replying.

I think - update the PR to simplify comment out the lines, so that if people want to when debugging Pillow, they can restore them. I'll approve, but might leave this here for a little while to see if anyone else has thoughts to offer.

As suggested in review, so it can be restored when debugging Pillow.

Assisted-by: LLM
Signed-off-by: Simon Hardy-Francis <simon@linux.com>
@radarhere radarhere changed the title Only build TIFF tag debug messages when debug logging is enabled Comment TIFF tag debug messages Oct 23, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants