Repository navigation
Speed up 8-bit horizontal resampling loops - #10293
Conversation
Step a pointer through the input pixels in the 2, 3 and 4 band loops of _ImagingResampleHorizontal_8bpc(), which compilers vectorise better for long filters. For 3 bands with short filters, keep the indexed loop, which is faster with gcc on aarch64. The output is unchanged. Assisted-by: LLM Signed-off-by: Simon Hardy-Francis <simon@linux.com>
Merging this PR will improve performance by 47.58%1 warning
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
dec923d to
058bdba
Compare
Changes proposed in this pull request:
_ImagingResampleHorizontal_8bpc(), the 2, 3 and 4 band loops readlineIn[(x + xmin) * 4 + c]. They now step a pointer through the input pixels instead, which compilers vectorise better when the filter is long (strong downscaling, or BICUBIC and LANCZOS).mainfor those, while on the x86 machines below it was at most about 5% faster than the indexed loop there.box) before and after, on all four machines below; they are byte-identical.Timings use the image from
Tests/benchmarks.py(make_pillow_image(), 1237x811), modes RGB, RGBA, LA and L, the 5 filters and scales 0.01, 0.125, 0.5, 0.8 and 2.14 (thetest_scalescales plus 0.5), so 25 cases per mode. Each cell is the range of the change in wall time againstmainover those 25 cases, negative is faster, with how many of the 25 got faster:The largest gains are for strong downscaling, for example RGB LANCZOS 1237x811 to 12x8 is 59% faster on Graviton4 and 63% faster on the M3.
A note on method, because the effect sizes are small in places: on the M3, the Graviton4 and the Xeon, where a function lands in memory moved some of these cases by 5-14% on its own, so a single build per side was not reliable. On Linux, each side was built at 5 or 6 code placements (padding inserted before the code), and the table shows the mean over placements of the best of 2 runs; on the M3 it is one build per side, best of 3 runs. L is a useful check: its code is not changed by this PR, and its spread above is that layout effect, not the patch. On the Xeon (a Skylake-family core) it comes from the JCC erratum (jumps crossing 32-byte boundaries); building both sides with
-Wa,-mbranches-within-32B-boundariesbrings L to within 1% there.CodSpeed measures this in simulation mode on x86 only, so it will show the change in instruction count, not these wall times.
The full test suite passes (5,659 passed;
test_imagegrab.py::TestImageGrab::test_grabfails on my machine with or without this change, asscreencaptureis 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.