Skip to content

Commit 5dc744a

Browse files
committed
feat: add unicode_memeq() to compare either directly or SSE2 vector
1 parent 564c58c commit 5dc744a

1 file changed

Lines changed: 57 additions & 1 deletion

File tree

  • Objects/stringlib

Objects/stringlib/eq.h

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
11
/* Fast unicode equal function optimized for dictobject.c and setobject.c */
22

3+
/* Return 1 if two unicode objects are equal, 0 if not.
4+
* unicode_eq() is called when the hash of two unicode objects is equal.
5+
*/
6+
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
7+
# include <emmintrin.h>
8+
# define USE_SSE2 1
9+
#endif
10+
11+
static inline int
12+
unicode_memeq(const void *p1, const void *p2, size_t len) {
13+
if (len == 0) return 1;
14+
15+
const uint8_t *u1 = (const uint8_t *)p1;
16+
const uint8_t *u2 = (const uint8_t *)p2;
17+
18+
switch (len) {
19+
case 1:
20+
return *u1 == *u2;
21+
case 2:
22+
return *(const uint16_t *)u1 == *(const uint16_t *)u2;
23+
case 3:
24+
return (*(const uint16_t *)u1 == *(const uint16_t *)u2) &&
25+
(*(const uint16_t *)(u1 + 1) == *(const uint16_t *)(u2 + 1));
26+
case 4:
27+
return *(const uint32_t *)u1 == *(const uint32_t *)u2;
28+
case 5:
29+
case 6:
30+
case 7:
31+
return (*(const uint32_t *)u1 == *(const uint32_t *)u2) &&
32+
(*(const uint32_t *)(u1 + len - 4) == *(const uint32_t *)(u2 + len - 4));
33+
case 8:
34+
return *(const uint64_t *)u1 == *(const uint64_t *)u2;
35+
default:
36+
if (len <= 16) {
37+
return (*(const uint64_t *)u1 == *(const uint64_t *)u2) &&
38+
(*(const uint64_t *)(u1 + len - 8) == *(const uint64_t *)(u2 + len - 8));
39+
}
40+
#ifdef USE_SSE2
41+
if (len <= 32) {
42+
__m128i v1_a = _mm_loadu_si128((const __m128i *)u1);
43+
__m128i v2_a = _mm_loadu_si128((const __m128i *)u2);
44+
__m128i cmp_a = _mm_cmpeq_epi8(v1_a, v2_a);
45+
int mask_a = _mm_movemask_epi8(cmp_a);
46+
47+
__m128i v1_b = _mm_loadu_si128((const __m128i *)(u1 + len - 16));
48+
__m128i v2_b = _mm_loadu_si128((const __m128i *)(u2 + len - 16));
49+
__m128i cmp_b = _mm_cmpeq_epi8(v1_b, v2_b);
50+
int mask_b = _mm_movemask_epi8(cmp_b);
51+
52+
return (mask_a == 0xFFFF) && (mask_b == 0xFFFF);
53+
}
54+
#endif
55+
return memcmp(p1, p2, len) == 0;
56+
}
57+
}
58+
359
/* Return 1 if two unicode objects are equal, 0 if not.
460
* unicode_eq() is called when the hash of two unicode objects is equal.
561
*/
@@ -18,5 +74,5 @@ unicode_eq(PyObject *str1, PyObject *str2)
1874

1975
const void *data1 = PyUnicode_DATA(str1);
2076
const void *data2 = PyUnicode_DATA(str2);
21-
return (memcmp(data1, data2, len * kind) == 0);
77+
return unicode_memeq(data1, data2, len * kind);
2278
}

0 commit comments

Comments
 (0)