Speed up some conversions - #9807
Conversation
|
Rebased following benchmark image update: #9831. Edit: or not, due to conflict :) Merging |
Merging this PR will improve performance by 76.97%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_convert[1237x811-P-RGBA] |
21.4 ms | 10.4 ms | ×2.1 |
| ⚡ | test_convert[1237x811-P-RGB] |
19.5 ms | 10.7 ms | +82.84% |
| ⚡ | test_convert[1237x811-PA-RGB] |
22 ms | 13.2 ms | +66.8% |
| ⚡ | test_convert[1237x811-PA-RGBA] |
23.9 ms | 15.3 ms | +56.38% |
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:faster-convert-x (f1d30a5) with main (6b5a7db)
Footnotes
-
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. ↩
|
|
||
| // Set the alpha channel of the UINT32 `v` in-place to the given value. | ||
| #ifdef WORDS_BIGENDIAN | ||
| #define SET_ALPHA_32(v, alpha) v = ((v & 0xFFFFFF00u) | (alpha)) |
There was a problem hiding this comment.
| #define SET_ALPHA_32(v, alpha) v = ((v & 0xFFFFFF00u) | (alpha)) | |
| #define SET_ALPHA_32(v, alpha) v = ((v & 0xFFFFFF00u) | alpha) |
There was a problem hiding this comment.
Same here - defensive parentheses if alpha expands to an expression.
EDIT: Prior art in e.g. the B16/L16/S16 macros in _imaging.c.
| #ifdef WORDS_BIGENDIAN | ||
| #define SET_ALPHA_32(v, alpha) v = ((v & 0xFFFFFF00u) | (alpha)) | ||
| #else | ||
| #define SET_ALPHA_32(v, alpha) v = ((v & 0x00FFFFFFu) | ((UINT32)(alpha) << 24)) |
There was a problem hiding this comment.
| #define SET_ALPHA_32(v, alpha) v = ((v & 0x00FFFFFFu) | ((UINT32)(alpha) << 24)) | |
| #define SET_ALPHA_32(v, alpha) v = ((v & 0x00FFFFFFu) | ((UINT32)alpha << 24)) |
There was a problem hiding this comment.
Rather not, because alpha could be a more complex expression.
EDIT: Prior art in e.g. the B16/L16/S16 macros in _imaging.c.
|
Could you mention here what your thinking was behind the new tests? |
|
You mean on diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c
index 4a35a9a1f..bab3e7bc0 100644
--- a/src/libImaging/Convert.c
+++ b/src/libImaging/Convert.c
@@ -594,7 +594,7 @@ i2l(UINT8 *out, const UINT8 *in_, int xsize) {
if (v <= 0) {
*out = 0;
} else if (v >= 255) {
- *out = 255;
+ *out = v & 255;
} else {
*out = (UINT8)v;
}? That would cause two already existing tests in
Guessing at what you think we might get wrong on this branch, diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c
index 3d0653139..72e2a93d6 100644
--- a/src/libImaging/Convert.c
+++ b/src/libImaging/Convert.c
@@ -979,9 +979,9 @@ pa2f(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) {
// Set the alpha channel of the UINT32 `v` in-place to the given value.
#ifdef WORDS_BIGENDIAN
-#define SET_ALPHA_32(v, alpha) v = ((v & 0xFFFFFF00u) | (alpha))
+#define SET_ALPHA_32(v, alpha) v = ((v & 0x00FFFFFFu) | (alpha))
#else
-#define SET_ALPHA_32(v, alpha) v = ((v & 0x00FFFFFFu) | ((UINT32)(alpha) << 24))
+#define SET_ALPHA_32(v, alpha) v = ((v & 0xFFFFFF00u) | ((UINT32)(alpha) << 24))
#endif
static voidcauses 36 tests to fail, including I don't naturally expect a performance PR to add more tests, since no functionality should be changed. You're adding tests not for coverage, basic correctness, or for regression checking, but to ensure that we don't make a hypothetical mistake in the future? I may be wrong, but that sounds like a step too far to me. |
248d723 to
f55d124
Compare
Fair enough. Removed.
Thanks for checking! Removed as well. The big-endian architecture Docker CI tests should then scream too. 👍
Somewhat the other way around, really: to check that I didn't make mistakes within the branch. The new tests were stacked first in the branch's commits, so I could verify that they were green before optimizing things and after optimizing things. |
f55d124 to
f1d30a5
Compare
| *out = (UINT8)v; | ||
| } | ||
| // Branchless saturation | ||
| *out = (UINT8)(v <= 0 ? 0 : v >= 255 ? 255 : v); |
There was a problem hiding this comment.
| *out = (UINT8)(v <= 0 ? 0 : v >= 255 ? 255 : v); | |
| *out = v <= 0 ? 0 : (v >= 255 ? 255 : (UINT8)v); |
Two ideas I'm suggesting here
- Why not only cast
vtoUINT8? - Rather than
a ? b : c ? d : e, usea ? b : (c ? d : e). ...yes, I find it easier to read.
Experiments to speed up the other conversions didn't really yield fruit.