[php-src] Issue #7847: stripos has very bad performance
| From: | cmb69 | Date: | Tue, 28 Dec 2021 23:55:39 +0000 |
| Subject: | [php-src] Issue #7847: stripos has very bad performance | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-238620@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/7847
Comment Author: cmb69
I'd rather treat this as feature request; we probably don't want to replace the current
implementation of
stripos() in stable releases (given that it works this way since
"forever"), and the recent changes to https://wiki.php.net/rfc/strtolower-ascii may
have already improved the performance, and at least should make it easier to avoid using
zend_string_tolower(). Furthermore, I somewhat doubt that the given test script is
representative for real world applications, where case-insensitive searches for long ASCII strings
appear to be rather rare (and especially the case where we only have to match the first character of
needle to recognize that we do not have to compare the rest of needle, and
can procede to the next character of haystack).
That said, consider the following drastically simplified but naive patch for master:
````.diff
ext/standard/string.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/ext/standard/string.c b/ext/standard/string.c
index 7c2dce4595..c350ebdd0a 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -1911,19 +1911,20 @@ PHP_FUNCTION(stripos)
RETURN_FALSE;
}
- haystack_dup = zend_string_tolower(haystack);
- needle_dup = zend_string_tolower(needle);
- found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
- ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
+ char *pn = ZSTR_VAL(needle);
+ char *eh = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - ZSTR_LEN(needle);
+ for (char *ph = ZSTR_VAL(haystack); ph <= eh; ++ph) {
+ if (*ph == *pn || *ph & ~0x40 == *pn & ~0x40) {
+ found = ph;
+ break;
+ }
+ }
if (found) {
- RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
+ RETVAL_LONG(found - ZSTR_VAL(haystack));
} else {
RETVAL_FALSE;
}
-
- zend_string_release_ex(haystack_dup, 0);
- zend_string_release_ex(needle_dup, 0);
}
/* }}} */
````
The given test script outputs here:
````
strpos: 1032 / second
stripos: 157 / second
preg_match: 1472 / second
````
This is even slightly slower than the unpatched variant, so the zend_string_tolower()
and php_memnstr() approach doesn't appear that bad, especially given that
preg_match() even rivals the performance of strpos().