Tom*_*ica 19 php regex performance strpos
我曾批评一个答案,暗示preg_match
了===
,为了避免类型不匹配查找字符串偏移时.
不过,后来对答案的作者已经发现,preg_match
实际上是显著快于多字节操作mb_strpos
.Normal strpos
比两个函数都快,但当然不能处理多字节字符串.
据我了解,mb_strpos
需要做更多的东西比strpos
.然而,如果正则表达式可以做到这一点几乎一样快的strpos
,它是什么,mb_strpos
这是否需要这么多的时间?
我怀疑这是一个优化错误.例如,PHP扩展可能比其原生函数慢吗?
mb_strpos($str, "??", 0 ,"GBK"): 15.988190889 (89%)
preg_match("/??/", $str): 1.022506952 (6%)
strpos($str, "dh"): 0.934401989 (5%)
Run Code Online (Sandbox Code Playgroud)
功能运行10 次6次.绝对时间(s)计算函数的10 6次运行的时间总和,而不是一次的平均值.
测试字符串是$str = "??dhgd????";
.该测试可以在这里看到(向下滚动跳过测试类).
注意:根据评论员之一(和常识),preg_match
比较时也不使用多字节,受到同样的错误风险strpos
.
Gum*_*mbo 18
要理解函数具有不同运行时的原因,您需要了解它们实际执行的操作.因为总结起来为"他们寻找针在干草堆里 "是不够的.
strpos
如果你看的实现strpos
,它使用zend_memstr
在内部,它实现了一个非常天真的算法用于搜索针在干草堆:基本上,它使用了memchr
找到的第一个字节针在干草堆里,然后使用memcmp
检查是否整体针开始在那位置.如果不是,则重复从第一个字节的前一个匹配位置搜索针的第一个字节.
知道这一点,我们可以说strpos
只使用朴素搜索算法搜索字节序列中的字节序列.
mb_strpos
这个函数是多字节对应的strpos
.这使得搜索变得更加复杂,因为您无法在不知道它们属于哪个字符的情况下查看字节.
mb_strpos
使用mbfl_strpos
,与简单算法相比,它做得更多,zend_memstr
就像200行复杂代码(mbfl_strpos
)与30行光滑代码(zend_memstr
)相比.
我们可以跳过其中两个部分的针和干草堆在必要时转换为UTF-8 ,和前来的代码主要块.
首先我们有两个设置循环,然后有循环根据给定的偏移量继续指针,你可以看到它们知道实际字符以及它们如何跳过整个编码的UTF-8字符:因为UTF-8是一个变量 -宽度字符编码,其中每个编码字符的第一个字节表示编码字符的整个长度.此信息存储在u8_tbl
数组中.
最后,实际搜索发生的循环.在这里,我们有一些有趣的事情,因为测试针在一定的位置草垛被反向尝试.如果一个字节不匹配,跳转表jtbl
是用来寻找下一个可能的位置针在大海捞针.这实际上是Boyer-Moore字符串搜索算法的实现.
所以现在我们知道mb_strpos
......
preg_match
至于preg_match
它,它使用PCRE库.其标准匹配算法使用非确定性有限自动机(NFA)来找到进行模式树的深度优先搜索的匹配.这基本上是一种天真的搜索方法.
hak*_*kre 13
我要离开preg_match
,让分析更加突出.
mb_strpos
相比较你的观察相对较慢strpos
,它会让你假设 - 因为消耗的时间 - mb_strpos
不仅仅是strpos
.
我认为这种观察是正确的.
然后你问什么是导致时差的"更多".
我试着给出一个简单的答案:"更多"是因为strpos
对二进制字符串进行操作(一个字符= 8位= 1个八位字节= 1个字节).mb_strpos
对编码的字符序列(几乎所有mb_*
函数都可以)进行操作,这些字符序列 可以是X位,甚至可能是每个字符的可变长度.
由于这总是关于特定的字符编码,因此需要首先针对该编码验证haystack以及needle字符串(可能),然后需要在该特定字符编码中完成查找字符串位置的整个操作.
这是翻译工作,并且 - 取决于编码 - 还需要特定的搜索算法.
接下来,mb扩展还需要在内存中具有一些结构来组织不同的字符编码,无论是转换表和/或特定算法.请参阅您注入的额外参数 - 例如编码的名称.
这比仅进行简单的逐字节比较要多得多.
例如,当您需要对某个字符进行编码或解码时,GBK字符编码非常有趣.在这种情况下,mb字符串函数需要考虑所有这些细节,以确定字符是否以及在哪个位置.由于PHP在用户空间中只有二进制字符串,您可以从中调用该函数,因此需要在每个函数调用上完成整个工作.
为了进一步说明这一点,如果查看支持的编码列表(mb_list_encodings
),您还可以找到一些类似BASE64,UUENCODE,HTML-ENTITIES和Quoted-Printable的内容.正如您可能想象的那样,所有这些都有不同的处
例如,单个数字HTML实体最大可达1024字节(如果不是更大).我知道和喜爱的一个极端例子就是这个.但是,对于该编码,它必须由mb_strpos
算法处理.
在5.5.6 PHP源文件以一看,延迟似乎出现在大部分mbfilter.c,其中- 作为hakre猜测 - 既草垛和针需要进行验证和转换,每一次 mb_strpos
(或者,我猜测,大多数mb_*
家庭)被召唤:
除非haystack采用默认格式,否则将其编码为默认格式:
if (haystack->no_encoding != mbfl_no_encoding_utf8) {
mbfl_string_init(&_haystack_u8);
haystack_u8 = mbfl_convert_encoding(haystack, &_haystack_u8, mbfl_no_encoding_utf8);
if (haystack_u8 == NULL) {
result = -4;
goto out;
}
} else {
haystack_u8 = haystack;
}
Run Code Online (Sandbox Code Playgroud)
除非needle是默认格式,否则将其编码为默认格式:
if (needle->no_encoding != mbfl_no_encoding_utf8) {
mbfl_string_init(&_needle_u8);
needle_u8 = mbfl_convert_encoding(needle, &_needle_u8, mbfl_no_encoding_utf8);
if (needle_u8 == NULL) {
result = -4;
goto out;
}
} else {
needle_u8 = needle;
}
Run Code Online (Sandbox Code Playgroud)
根据快速检查valgrind
,编码转换占mb_strpos
运行时间的很大一部分,约占总数的84%,或五分之六:
218,552,085 ext/mbstring/libmbfl/mbfl/mbfilter.c:mbfl_strpos [/usr/src/php-5.5.6/sapi/cli/php]
183,812,085 ext/mbstring/libmbfl/mbfl/mbfilter.c:mbfl_convert_encoding [/usr/src/php-5.5.6/sapi/cli/php]
Run Code Online (Sandbox Code Playgroud)
这似乎与OP的mb_strpos
对战时间一致strpos
.
编码不考虑,mb_strpos
"荷兰国际集团字符串是完全一样的strpos
"荷兰国际集团一个略长的字符串.好吧,如果你有一些非常笨拙的字符串,那么这个字符串的长度可以达到四倍,但即使这样,你也会得到一个四倍的延迟,而不是二十倍.编码时间增加了5-6倍的减速.
mb_strpos
......所以,你可以做什么?您可以跳过这两个步骤,确保您已在内部使用"基本"格式的字符串mbfl*
进行转换和比较,即mbfl_no_encoding_utf8
(UTF-8):
那你的伪代码:
$haystack = "...";
$needle = "...";
$res = mb_strpos($haystack, $needle, 0, $Encoding);
Run Code Online (Sandbox Code Playgroud)
变为:
$haystack = "...";
$needle = "...";
mb_internal_encoding('UTF-8') or die("Cannot set encoding");
$haystack = mb_convert_encoding($haystack, 'UTF-8' [, $SourceEncoding]);
$needle = mb_convert_encoding($needle, 'UTF-8', [, $SourceEncoding]);
$res = mb_strpos($haystack, $needle, 0);
Run Code Online (Sandbox Code Playgroud)
当然,如果整个UTF-8基础的"设置时间"和维护明显小于在每个mb_*
功能中隐式进行转换的"运行时间",这是很方便的.