|
56 | 56 | at the end, instead of comparing each character. The accumulation loop has |
57 | 57 | no branches, so compilers vectorize it, and the result does not depend on |
58 | 58 | 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 |
60 | 60 | exit early. */ |
61 | 61 | Py_LOCAL_INLINE(Py_UCS4) |
62 | 62 | 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) |
86 | 86 | #endif |
87 | 87 | } |
88 | 88 |
|
89 | | - while (p + SIZEOF_SIZE_T * 32 <= _end) { |
| 89 | + while (p + SIZEOF_SIZE_T * 16 <= _end) { |
90 | 90 | 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]; |
93 | 99 | } |
| 100 | + value |= v0 | v1 | v2 | v3; |
94 | 101 | if (value & MASK_MAX_CHAR) { |
95 | 102 | return MAX_CHAR; |
96 | 103 | } |
97 | | - p += SIZEOF_SIZE_T * 32; |
| 104 | + p += SIZEOF_SIZE_T * 16; |
98 | 105 | } |
99 | 106 |
|
100 | 107 | while (p + SIZEOF_SIZE_T <= _end) { |
|
0 commit comments