diff --git a/Misc/NEWS.d/next/C API/2024-08-11-12-06-23.gh-issue-122901.B2Huom.rst b/Misc/NEWS.d/next/C API/2024-08-11-12-06-23.gh-issue-122901.B2Huom.rst new file mode 100644 index 000000000000000..9c3cb08f755837d --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-08-11-12-06-23.gh-issue-122901.B2Huom.rst @@ -0,0 +1 @@ +Improve performance of ``find_max_char``. diff --git a/Objects/stringlib/find_max_char.h b/Objects/stringlib/find_max_char.h index 7ab3fc88b331b14..10ba11769a3a7db 100644 --- a/Objects/stringlib/find_max_char.h +++ b/Objects/stringlib/find_max_char.h @@ -5,123 +5,130 @@ # error "find_max_char.h is specific to Unicode" #endif +#define MAX_CHAR_ASCII 0x7f +#define MAX_CHAR_UCS1 0xff +#define MAX_CHAR_UCS2 0xffff +#define MAX_CHAR_UCS4 0x10ffff + /* Mask to quickly check whether a C 'size_t' contains a non-ASCII, UTF8-encoded char. */ -#if (SIZEOF_SIZE_T == 8) -# define UCS1_ASCII_CHAR_MASK 0x8080808080808080ULL -#elif (SIZEOF_SIZE_T == 4) -# define UCS1_ASCII_CHAR_MASK 0x80808080U -#else -# error C 'size_t' size should be either 4 or 8! -#endif #if STRINGLIB_SIZEOF_CHAR == 1 +# if SIZEOF_SIZE_T == 8 +# define MASK_ASCII 0x8080808080808080ULL +# elif SIZEOF_SIZE_T == 4 +# define MASK_ASCII 0x80808080U +# else +# error C 'size_t' size should be either 4 or 8! +# endif +# define MASK_MAX_CHAR MASK_ASCII +# define MAX_CHAR MAX_CHAR_UCS1 +#elif STRINGLIB_SIZEOF_CHAR == 2 +# if SIZEOF_SIZE_T == 8 +# define MASK_ASCII 0xFF80FF80FF80FF80ULL +# define MASK_UCS1 0xFF00FF00FF00FF00ULL +# elif SIZEOF_SIZE_T == 4 +# define MASK_ASCII 0xFF80FF80U +# define MASK_UCS1 0xFF00FF00U +# else +# error C 'size_t' size should be either 4 or 8! +# endif +# define MASK_MAX_CHAR MASK_UCS1 +# define MAX_CHAR MAX_CHAR_UCS2 +#elif STRINGLIB_SIZEOF_CHAR == 4 +# if SIZEOF_SIZE_T == 8 +# define MASK_ASCII 0xFFFFFF80FFFFFF80ULL +# define MASK_UCS1 0xFFFFFF00FFFFFF00ULL +# define MASK_UCS2 0xFFFF0000FFFF0000ULL +# elif SIZEOF_SIZE_T == 4 +# define MASK_ASCII 0xFFFFFF80U +# define MASK_UCS1 0xFFFFFF00U +# define MASK_UCS2 0xFFFF0000U +# else +# error C 'size_t' size should be either 4 or 8! +# endif +# define MASK_MAX_CHAR MASK_UCS2 +# define MAX_CHAR MAX_CHAR_UCS4 +#else +#error Invalid STRINGLIB_SIZEOF_CHAR (must be 1, 2 or 4) +#endif Py_LOCAL_INLINE(Py_UCS4) STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end) { - const unsigned char *p = (const unsigned char *) begin; + const unsigned char *p = (const unsigned char *)begin; const unsigned char *_end = (const unsigned char *)end; - while (p < _end) { - if (_Py_IS_ALIGNED(p, ALIGNOF_SIZE_T)) { - /* Help register allocation */ - const unsigned char *_p = p; - while (_p + SIZEOF_SIZE_T <= _end) { - size_t value = *(const size_t *) _p; - if (value & UCS1_ASCII_CHAR_MASK) - return 255; - _p += SIZEOF_SIZE_T; - } - p = _p; - if (p == _end) - break; + size_t value = 0; + + if (!_Py_IS_ALIGNED(p, ALIGNOF_SIZE_T)) { +#if STRINGLIB_SIZEOF_CHAR <= 1 + if (!_Py_IS_ALIGNED(p, 1) && p + 1 <= _end) { + value |= *p++; } - if (*p++ & 0x80) - return 255; +#endif +#if STRINGLIB_SIZEOF_CHAR <= 2 + if (!_Py_IS_ALIGNED(p, sizeof(uint16_t)) && p + sizeof(uint16_t) <= _end) { + value |= *(const uint16_t*)p; + p += sizeof(uint16_t); + } +#endif +#if SIZEOF_SIZE_T == 8 + if (!_Py_IS_ALIGNED(p, sizeof(uint32_t)) && p + sizeof(uint32_t) <= _end) { + value |= *(const uint32_t*)p; + p += sizeof(uint32_t); + } +#endif } - return 127; -} -#undef ASCII_CHAR_MASK - -#else /* STRINGLIB_SIZEOF_CHAR == 1 */ - -#define MASK_ASCII 0xFFFFFF80 -#define MASK_UCS1 0xFFFFFF00 -#define MASK_UCS2 0xFFFF0000 - -#define MAX_CHAR_ASCII 0x7f -#define MAX_CHAR_UCS1 0xff -#define MAX_CHAR_UCS2 0xffff -#define MAX_CHAR_UCS4 0x10ffff + while (p + SIZEOF_SIZE_T * 32 <= _end) { + const size_t *pp = (const size_t *)p; + for(int i=0; i<32; i++) { + value |= pp[i]; + } + if (value & MASK_MAX_CHAR) { + return MAX_CHAR; + } + p += SIZEOF_SIZE_T * 32; + } -Py_LOCAL_INLINE(Py_UCS4) -STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end) -{ -#if STRINGLIB_SIZEOF_CHAR == 2 - const Py_UCS4 mask_limit = MASK_UCS1; - const Py_UCS4 max_char_limit = MAX_CHAR_UCS2; -#elif STRINGLIB_SIZEOF_CHAR == 4 - const Py_UCS4 mask_limit = MASK_UCS2; - const Py_UCS4 max_char_limit = MAX_CHAR_UCS4; -#else -#error Invalid STRINGLIB_SIZEOF_CHAR (must be 1, 2 or 4) + while (p + SIZEOF_SIZE_T <= _end) { + value |= *(const size_t *)p; + p += SIZEOF_SIZE_T; + } +#if SIZEOF_SIZE_T == 8 + if (p + sizeof(uint32_t) <= _end) { + value |= *(const uint32_t*)p; + p += sizeof(uint32_t); + } #endif - Py_UCS4 mask; - Py_ssize_t n = end - begin; - const STRINGLIB_CHAR *p = begin; - const STRINGLIB_CHAR *unrolled_end = begin + _Py_SIZE_ROUND_DOWN(n, 4); - Py_UCS4 max_char; - - max_char = MAX_CHAR_ASCII; - mask = MASK_ASCII; - while (p < unrolled_end) { - STRINGLIB_CHAR bits = p[0] | p[1] | p[2] | p[3]; - if (bits & mask) { - if (mask == mask_limit) { - /* Limit reached */ - return max_char_limit; - } - if (mask == MASK_ASCII) { - max_char = MAX_CHAR_UCS1; - mask = MASK_UCS1; - } - else { - /* mask can't be MASK_UCS2 because of mask_limit above */ - assert(mask == MASK_UCS1); - max_char = MAX_CHAR_UCS2; - mask = MASK_UCS2; - } - /* We check the new mask on the same chars in the next iteration */ - continue; - } - p += 4; +#if STRINGLIB_SIZEOF_CHAR <= 2 + if (p + sizeof(uint16_t) <= _end) { + value |= *(const uint16_t*)p; + p += sizeof(uint16_t); } - while (p < end) { - if (p[0] & mask) { - if (mask == mask_limit) { - /* Limit reached */ - return max_char_limit; - } - if (mask == MASK_ASCII) { - max_char = MAX_CHAR_UCS1; - mask = MASK_UCS1; - } - else { - /* mask can't be MASK_UCS2 because of mask_limit above */ - assert(mask == MASK_UCS1); - max_char = MAX_CHAR_UCS2; - mask = MASK_UCS2; - } - /* We check the new mask on the same chars in the next iteration */ - continue; - } - p++; +#endif +#if STRINGLIB_SIZEOF_CHAR <= 1 + if (p + 1 <= _end) { + value |= *p++; } - return max_char; +#endif + +#ifdef MASK_UCS2 + if (value & MASK_UCS2) + return MAX_CHAR_UCS4; +#endif +#ifdef MASK_UCS1 + if (value & MASK_UCS1) + return MAX_CHAR_UCS2; +#endif + if (value & MASK_ASCII) + return MAX_CHAR_UCS1; + return MAX_CHAR_ASCII; } +#undef MASK_MAX_CHAR +#undef MAX_CHAR #undef MASK_ASCII #undef MASK_UCS1 #undef MASK_UCS2 @@ -129,6 +136,3 @@ STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end) #undef MAX_CHAR_UCS1 #undef MAX_CHAR_UCS2 #undef MAX_CHAR_UCS4 - -#endif /* STRINGLIB_SIZEOF_CHAR == 1 */ -