Skip to content

Speed up image comparison - #10030

Open
akx wants to merge 6 commits into
python-pillow:mainfrom
akx:image-equals-new
Open

akx wants to merge 6 commits into
python-pillow:mainfrom
akx:image-equals-new

Conversation

@akx

@akx akx commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Closes #7260 (supersedes it).

I added a benchmark, and stacked the tests from the original (adjusted to pass lints, etc.) to be before the reimplementation in the PR, then added some more corner case tests.

I verified, with interactive rebase, that the tests pass just before the new handler is added, and then separately afterwards.

Local benchmark score improvement (unsurprising, and this isn't measuring peak memory usage improvements!):

------------------- benchmark 'compare': 15 tests, 2 sources -------------------
Name (time in ns)                          0001_main Min  0002_new Min      ΔMin
--------------------------------------------------------------------------------
test_equality[1237x811-L-inverted]          122,875.0916      470.7952    -99.6%
test_equality[1237x811-L-equal]             125,959.0499   24,207.9841    -80.8%
test_equality[1237x811-L-one-pixel]         129,332.9988   12,415.9269    -90.4%
test_equality[1237x811-I;16-one-pixel]      343,374.9080   27,542.0025    -92.0%
test_equality[1237x811-LA-inverted]         343,417.0503      479.1538    -99.9%
test_equality[1237x811-I;16-equal]          380,958.0812   55,041.9791    -85.6%
test_equality[1237x811-I;16-inverted]       387,958.9494      479.0956    -99.9%
test_equality[1237x811-LA-one-pixel]        451,375.0318  189,000.0422    -58.1%
test_equality[1237x811-LA-equal]            463,792.0065  378,541.9976    -18.4%
test_equality[1237x811-RGBA-inverted]       727,667.0076      483.2982    -99.9%
test_equality[1237x811-RGB-inverted]        754,792.0104      479.2004    -99.9%
test_equality[1237x811-RGB-equal]           813,374.9943  378,374.9416    -53.5%
test_equality[1237x811-RGB-one-pixel]       823,125.0104  189,290.9640    -77.0%
test_equality[1237x811-RGBA-equal]          874,208.0536  105,833.0527    -87.9%
test_equality[1237x811-RGBA-one-pixel]      899,208.0111   52,874.9079    -94.1%
--------------------------------------------------------------------------------

EDIT: added one more commit for autovectorisation to make RGB (likely very common!) and LA even faster (at least on my hardware):


--------------------- benchmark 'compare': 15 tests, 2 sources ---------------------
Name (time in ns)                          0001_main Min  0005_equalve Min      ΔMin
------------------------------------------------------------------------------------
test_equality[1237x811-L-inverted]          122,875.0916          479.0956    -99.6%
test_equality[1237x811-L-equal]             125,959.0499       24,333.0142    -80.7%
test_equality[1237x811-L-one-pixel]         129,332.9988       12,416.9746    -90.4%
test_equality[1237x811-I;16-one-pixel]      343,374.9080       27,625.0066    -92.0%
test_equality[1237x811-LA-inverted]         343,417.0503          479.2004    -99.9%
test_equality[1237x811-I;16-equal]          380,958.0812       55,207.9873    -85.5%
test_equality[1237x811-I;16-inverted]       387,958.9494          479.2004    -99.9%
test_equality[1237x811-LA-one-pixel]        451,375.0318       43,249.9219    -90.4%
test_equality[1237x811-LA-equal]            463,792.0065       86,874.9339    -81.3%
test_equality[1237x811-RGBA-inverted]       727,667.0076          485.3995    -99.9%
test_equality[1237x811-RGB-inverted]        754,792.0104          483.2982    -99.9%
test_equality[1237x811-RGB-equal]           813,374.9943       86,790.9985    -89.3%
test_equality[1237x811-RGB-one-pixel]       823,125.0104       43,459.0038    -94.7%
test_equality[1237x811-RGBA-equal]          874,208.0536      105,957.9663    -87.9%
test_equality[1237x811-RGBA-one-pixel]      899,208.0111       52,916.9338    -94.1%
------------------------------------------------------------------------------------

@akx
akx marked this pull request as ready for review September 21, 2026 05:20
@akx akx changed the title Add tp_richcompare handler for ImagingCore Speed up image comparison Sep 21, 2026
@akx

akx commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

CodSpeed isn't showing a report here because all the benchmarks are new, but you can see them in the report here.

The varied range of speeds suggests that these optimizations do take, but I'm not sure if the last commit, the autovectorisation, works as well as it does on clang/ARM.

@hugovk

hugovk commented Sep 21, 2026

Copy link
Copy Markdown
Member

Want to split the benchmarks to their own PR?

@akx

akx commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

@hugovk Done: #10033

Comment thread src/PIL/Image.py Outdated
Comment thread src/PIL/Image.py Outdated
Comment thread src/PIL/Image.py Outdated
Yay295 and others added 5 commits September 21, 2026 15:49
* de-parametrize test_not_equal_mode_1
* add type annotations
* test the Image's behavior, not the Core's
Co-authored-by: Yay295 <Yay295@gmail.com>
@codspeed

codspeed Bot commented Sep 21, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by ×32

⚠️ 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

⚡ 15 improved benchmarks
✅ 609 untouched benchmarks
⏩ 338 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
test_equality[1237x811-RGB-inverted] 49,211 µs 67.4 µs ×730
test_equality[1237x811-RGBA-inverted] 52,269.3 µs 73.7 µs ×710
test_equality[1237x811-LA-inverted] 30,972.3 µs 67.6 µs ×460
test_equality[1237x811-I;16-inverted] 25,080.7 µs 67.5 µs ×370
test_equality[1237x811-L-inverted] 12,659.3 µs 67.1 µs ×190
test_equality[1237x811-RGBA-one-pixel] 54.6 ms 3.7 ms ×15
test_equality[1237x811-I;16-one-pixel] 25.7 ms 1.9 ms ×13
test_equality[1237x811-L-one-pixel] 12,877.2 µs 985.3 µs ×13
test_equality[1237x811-RGB-one-pixel] 51.3 ms 4 ms ×13
test_equality[1237x811-LA-one-pixel] 32.1 ms 4 ms ×8.1
test_equality[1237x811-RGBA-equal] 57.7 ms 7.4 ms ×7.8
test_equality[1237x811-I;16-equal] 26.3 ms 3.7 ms ×7
test_equality[1237x811-L-equal] 13.1 ms 1.9 ms ×6.9
test_equality[1237x811-RGB-equal] 52.7 ms 7.9 ms ×6.7
test_equality[1237x811-LA-equal] 33.2 ms 7.9 ms ×4.2

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:image-equals-new (8602567) with main (d13f288)

Open in CodSpeed

Footnotes

  1. 338 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.

@akx

akx commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

And the memory usage comparison that the benchmark doesn't capture shows that comparisons now require no extra allocations:

On a 2053x2039 image:

Mode main == (MiB) new == (MiB) main peak (MiB) new peak (MiB)
1 3.7 0.0 11.8 8.1
CMYK 57.7 0.0 89.8 32.0
F 56.8 0.0 88.8 32.0
HSV 42.0 0.0 74.0 32.0
I 56.8 0.0 88.8 32.0
I;16 28.7 0.0 44.8 16.0
I;16B 39.9 0.0 55.9 16.0
I;16L 29.5 0.0 45.5 16.0
I;16N 28.7 0.0 44.8 16.0
L 15.2 0.0 23.3 8.1
LA 28.7 0.0 60.7 32.0
LAB 53.8 0.0 85.9 32.1
La 28.7 0.0 60.7 32.0
P 16.9 0.0 25.2 8.2
PA 28.8 0.0 61.0 32.1
RGB 42.0 0.0 74.0 32.0
RGBA 56.8 0.0 88.8 32.0
RGBX 56.8 0.0 88.8 32.0
RGBa 56.8 0.0 88.8 32.0
YCbCr 42.0 0.0 74.0 32.0

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.

4 participants