Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve performance of ``find_max_char``.
206 changes: 105 additions & 101 deletions Objects/stringlib/find_max_char.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,130 +5,134 @@
# 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
#undef MAX_CHAR_ASCII
#undef MAX_CHAR_UCS1
#undef MAX_CHAR_UCS2
#undef MAX_CHAR_UCS4

#endif /* STRINGLIB_SIZEOF_CHAR == 1 */