Skip to content

Commit 1daa7ee

Browse files
Use four accumulators and a 16-word block so the loop is fast when not vectorized
1 parent f524f84 commit 1daa7ee

1 file changed

Lines changed: 12 additions & 5 deletions

File tree

‎Objects/stringlib/find_max_char.h‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
at the end, instead of comparing each character. The accumulation loop has
5757
no branches, so compilers vectorize it, and the result does not depend on
5858
where each byte sits in the word, so unaligned ends can be read in smaller
59-
pieces. Every 32 words, check whether the widest kind is already known and
59+
pieces. Every 16 words, check whether the widest kind is already known and
6060
exit early. */
6161
Py_LOCAL_INLINE(Py_UCS4)
6262
STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end)
@@ -86,15 +86,22 @@ STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end)
8686
#endif
8787
}
8888

89-
while (p + SIZEOF_SIZE_T * 32 <= _end) {
89+
while (p + SIZEOF_SIZE_T * 16 <= _end) {
9090
const size_t *pp = (const size_t *)p;
91-
for (int i = 0; i < 32; i++) {
92-
value |= pp[i];
91+
/* Four independent accumulators so the loop does not serialize on
92+
one register when the compiler does not vectorize it. */
93+
size_t v0 = 0, v1 = 0, v2 = 0, v3 = 0;
94+
for (int i = 0; i < 16; i += 4) {
95+
v0 |= pp[i];
96+
v1 |= pp[i + 1];
97+
v2 |= pp[i + 2];
98+
v3 |= pp[i + 3];
9399
}
100+
value |= v0 | v1 | v2 | v3;
94101
if (value & MASK_MAX_CHAR) {
95102
return MAX_CHAR;
96103
}
97-
p += SIZEOF_SIZE_T * 32;
104+
p += SIZEOF_SIZE_T * 16;
98105
}
99106

100107
while (p + SIZEOF_SIZE_T <= _end) {

0 commit comments

Comments
 (0)