Skip to content

Speed up Image.getextrema() - #9762

Open
akx wants to merge 3 commits into
python-pillow:mainfrom
akx:extremaly-fast
Open

Speed up Image.getextrema()#9762
akx wants to merge 3 commits into
python-pillow:mainfrom
akx:extremaly-fast

Conversation

@akx

@akx akx commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Speeds up getextrema() -- with more than just hoist and restrict this time, by adding a separate function for the common case of 2-4 band 8bpc images. The single-channel case speeds up pleasantly too by autovectorisation, though.

------------------- benchmark 'extrema': 4 tests, 2 sources --------------------
Name (time in us)                   0001_3a36c3a OPS  0003_796ae94 OPS      ΔOPS
--------------------------------------------------------------------------------
test_getextrema[1024x1024-RGB]            1,790.4542       13,099.6057   +631.6%
test_getextrema[1024x1024-L]              1,410.9957       17,641.2525  +1150.3%
test_getextrema[1024x1024-RGBA]             687.5769       13,102.1316  +1805.6%
test_getextrema[1024x1024-LA]               494.9731       11,851.6516  +2294.4%
--------------------------------------------------------------------------------

akx

This comment was marked as outdated.

@akx
akx marked this pull request as ready for review July 4, 2026 10:54
@akx
akx force-pushed the extremaly-fast branch from 12b239a to 03355c9 Compare July 20, 2026 07:37
@akx akx mentioned this pull request Jul 29, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 10, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by ×3

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 6 improved benchmarks
✅ 597 untouched benchmarks
⏩ 335 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
test_getextrema[1237x811-CMYK] 20.3 ms 4.7 ms ×4.3
test_getextrema[1237x811-RGBA] 20.3 ms 4.7 ms ×4.3
test_getextrema[1237x811-LA] 15.7 ms 4.7 ms ×3.3
test_getextrema[1237x811-RGB] 14.2 ms 4.7 ms ×3
test_getextrema[1237x811-L] 3.6 ms 1.4 ms ×2.5
test_getextrema[1237x811-I] 6.7 ms 4.3 ms +55.06%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing akx:extremaly-fast (6002bd5) with main (0d20a00)

Open in CodSpeed

Footnotes

  1. 335 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Comment thread src/_imaging.c
static PyObject *
_getextrema(ImagingObject *self, PyObject *args) {
if (self->image->type == IMAGING_TYPE_UINT8 && self->image->bands > 1 &&
self->image->bands <= 4) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Considering that we don't have any images with more than 4 bands, I'm not sold that we need to check for it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If this check is elided, in the future when someone decides to add such support, the following code will crash in unfortunate and non-obvious ways when it writes over the UINT8 mb[2 * 4]; array, where 4 stands for "maximum number of bands supported by this code".

I can remove the check if you like, but I don't think it costs much here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Couldn't you just allocate mb to match the number of bands?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Using a VLA (or heap allocation) becomes a little complex, since due to the data layout of 2-band (PA/LA/La) images (pixelsize 4, XX....AA in image32), the actual valid sizes for mb (and ImagingGetExtremaMultiband's vmin and vmax) are actually only 3 or 4. IOW, the required accumulator array size is not directly related to bands.

I'm not sure it's worth the complexity, what do you think?

Comment thread src/_imaging.c
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