Issue #13134: optimize finding single-character strings using memchr

This commit is contained in:
Antoine Pitrou 2011-10-11 20:29:21 +02:00
parent 60dd7dc23c
commit 2c3b2302ad
4 changed files with 95 additions and 1 deletions

View file

@ -32,6 +32,60 @@
#define STRINGLIB_BLOOM(mask, ch) \
((mask & (1UL << ((ch) & (STRINGLIB_BLOOM_WIDTH -1)))))
Py_LOCAL_INLINE(Py_ssize_t)
STRINGLIB(fastsearch_memchr_1char)(const STRINGLIB_CHAR* s, Py_ssize_t n,
STRINGLIB_CHAR ch, unsigned char needle,
Py_ssize_t maxcount, int mode)
{
void *candidate;
const STRINGLIB_CHAR *found;
#define DO_MEMCHR(memchr, s, needle, nchars) do { \
candidate = memchr((const void *) (s), (needle), (nchars) * sizeof(STRINGLIB_CHAR)); \
found = (const STRINGLIB_CHAR *) \
((Py_ssize_t) candidate & (~ ((Py_ssize_t) sizeof(STRINGLIB_CHAR) - 1))); \
} while (0)
if (mode == FAST_SEARCH) {
const STRINGLIB_CHAR *_s = s;
const STRINGLIB_CHAR *e = s + n;
while (_s < e) {
DO_MEMCHR(memchr, _s, needle, e - _s);
if (found == NULL)
return -1;
if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
return (found - _s);
/* False positive */
_s = found + 1;
}
return -1;
}
#ifdef HAVE_MEMRCHR
/* memrchr() is a GNU extension, available since glibc 2.1.91.
it doesn't seem as optimized as memchr(), but is still quite
faster than our hand-written loop in FASTSEARCH below */
else if (mode == FAST_RSEARCH) {
while (n > 0) {
DO_MEMCHR(memrchr, s, needle, n);
if (found == NULL)
return -1;
n = found - s;
if (sizeof(STRINGLIB_CHAR) == 1 || *found == ch)
return n;
/* False positive */
}
return -1;
}
#endif
else {
assert(0); /* Should never get here */
return 0;
}
#undef DO_MEMCHR
}
Py_LOCAL_INLINE(Py_ssize_t)
FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
const STRINGLIB_CHAR* p, Py_ssize_t m,
@ -51,6 +105,25 @@ FASTSEARCH(const STRINGLIB_CHAR* s, Py_ssize_t n,
if (m <= 0)
return -1;
/* use special case for 1-character strings */
if (n > 10 && (mode == FAST_SEARCH
#ifdef HAVE_MEMRCHR
|| mode == FAST_RSEARCH
#endif
)) {
/* use memchr if we can choose a needle without two many likely
false positives */
unsigned char needle;
int use_needle = 1;
needle = p[0] & 0xff;
if (needle == 0 && sizeof(STRINGLIB_CHAR) > 1) {
needle = (p[0] >> 8) & 0xff;
if (needle >= 32)
use_needle = 0;
}
if (use_needle)
return STRINGLIB(fastsearch_memchr_1char)
(s, n, p[0], needle, maxcount, mode);
}
if (mode == FAST_COUNT) {
for (i = 0; i < n; i++)
if (s[i] == p[0]) {