From 5dc744a3247747598ac688d8e509d1341e70e6d0 Mon Sep 17 00:00:00 2001 From: rajat315315 Date: Sat, 11 Jul 2026 16:50:19 +0530 Subject: [PATCH 1/2] feat: add unicode_memeq() to compare either directly or SSE2 vector --- Objects/stringlib/eq.h | 58 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/Objects/stringlib/eq.h b/Objects/stringlib/eq.h index 821b692f26b8308..9729b2464034fc2 100644 --- a/Objects/stringlib/eq.h +++ b/Objects/stringlib/eq.h @@ -1,5 +1,61 @@ /* Fast unicode equal function optimized for dictobject.c and setobject.c */ +/* Return 1 if two unicode objects are equal, 0 if not. + * unicode_eq() is called when the hash of two unicode objects is equal. + */ +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) +# include +# define USE_SSE2 1 +#endif + +static inline int +unicode_memeq(const void *p1, const void *p2, size_t len) { + if (len == 0) return 1; + + const uint8_t *u1 = (const uint8_t *)p1; + const uint8_t *u2 = (const uint8_t *)p2; + + switch (len) { + case 1: + return *u1 == *u2; + case 2: + return *(const uint16_t *)u1 == *(const uint16_t *)u2; + case 3: + return (*(const uint16_t *)u1 == *(const uint16_t *)u2) && + (*(const uint16_t *)(u1 + 1) == *(const uint16_t *)(u2 + 1)); + case 4: + return *(const uint32_t *)u1 == *(const uint32_t *)u2; + case 5: + case 6: + case 7: + return (*(const uint32_t *)u1 == *(const uint32_t *)u2) && + (*(const uint32_t *)(u1 + len - 4) == *(const uint32_t *)(u2 + len - 4)); + case 8: + return *(const uint64_t *)u1 == *(const uint64_t *)u2; + default: + if (len <= 16) { + return (*(const uint64_t *)u1 == *(const uint64_t *)u2) && + (*(const uint64_t *)(u1 + len - 8) == *(const uint64_t *)(u2 + len - 8)); + } +#ifdef USE_SSE2 + if (len <= 32) { + __m128i v1_a = _mm_loadu_si128((const __m128i *)u1); + __m128i v2_a = _mm_loadu_si128((const __m128i *)u2); + __m128i cmp_a = _mm_cmpeq_epi8(v1_a, v2_a); + int mask_a = _mm_movemask_epi8(cmp_a); + + __m128i v1_b = _mm_loadu_si128((const __m128i *)(u1 + len - 16)); + __m128i v2_b = _mm_loadu_si128((const __m128i *)(u2 + len - 16)); + __m128i cmp_b = _mm_cmpeq_epi8(v1_b, v2_b); + int mask_b = _mm_movemask_epi8(cmp_b); + + return (mask_a == 0xFFFF) && (mask_b == 0xFFFF); + } +#endif + return memcmp(p1, p2, len) == 0; + } +} + /* Return 1 if two unicode objects are equal, 0 if not. * unicode_eq() is called when the hash of two unicode objects is equal. */ @@ -18,5 +74,5 @@ unicode_eq(PyObject *str1, PyObject *str2) const void *data1 = PyUnicode_DATA(str1); const void *data2 = PyUnicode_DATA(str2); - return (memcmp(data1, data2, len * kind) == 0); + return unicode_memeq(data1, data2, len * kind); } From adf960590ce004e941e9c6e84dcdf8c1a23bdf54 Mon Sep 17 00:00:00 2001 From: rajat315315 Date: Sat, 11 Jul 2026 17:16:59 +0530 Subject: [PATCH 2/2] feat: add rst comment using blurb --- .../2026-07-11-17-15-55.gh-issue-153198.dmBbIm.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-17-15-55.gh-issue-153198.dmBbIm.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-17-15-55.gh-issue-153198.dmBbIm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-17-15-55.gh-issue-153198.dmBbIm.rst new file mode 100644 index 000000000000000..74161aee0d873f5 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-17-15-55.gh-issue-153198.dmBbIm.rst @@ -0,0 +1,2 @@ +Optimize unicode_eq string equality comparisons for short strings by using +inline integer casts and SSE2 vector instructions.