[php-src] Issue #7847: stripos has very bad performance

From: 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().

« previous php.bugs (#238620) next »