PCRE_UTF8修改器非常慢


PCRE_UTF8 Modifier Extremely Slow

出于某种原因,即使不使用多字节字符,只需将 PCRE_UTF8 修饰符添加到正则表达式preg_match()输入中大致减少 (x10) 执行时间。我不知道为什么会这样以及如何最好地减少时间。用于测试的脚本是:

$s = microtime(true);
for ($i = 0; $i < 1000; $i++) {
    preg_match('/ /u', str_repeat(' ', 50000), $match);
}
$e = microtime(true);
echo "u Modifier:'t".(($e-$s)/$i)."'n";
$s = microtime(true);
for ($i = 0; $i < 1000; $i++) {
    preg_match('/ /', str_repeat(' ', 50000), $match);
}
$e = microtime(true);
echo "No Modifier:'t".(($e-$s)/$i)."'n";

在这里在线试用。

结果是:

u Modifier: 2.5037050247192E-5
No Modifier:    2.4969577789307E-6

我试图看看这是否是网上已知的错误,但据说这不是 PHP 的问题。

这是由什么引起的,更快地执行匹配1 的最佳方法是什么?


1"匹配"是指任何匹配。使用的示例只是一个最小示例,显然可以以更好的方式匹配。

PCRE 在进行任何其他处理之前检查 UTF 有效性。

来自 PCRE 文档:

设置 PCRE2_UTF 选项后,作为模式和主题传递的字符串(默认情况下)在进入相关函数时将检查其有效性。如果传递了无效的 UTF 字符串,则返回负错误代码。可以通过调用 pcre2_get_startchar() 从匹配数据块中提取违规字符的代码单元偏移量,在 UTF 错误后用于此目的。

在进行任何其他处理之前检查整个字符串。除了检查字符串的格式外,还有一个检查以确保所有代码点都在 U+0 到 U+10FFFF 范围内,不包括代理项区域。所谓的"非字符"代码点没有被排除在外,因为 Unicode 更正 #9 明确指出它们不应该被排除在外。

在某些情况下,您可能已经知道字符串有效,因此希望跳过这些检查以提高性能,例如,在重复扫描长主题字符串的情况下。如果在编译时或匹配时设置PCRE2_NO_UTF_CHECK选项,PCRE2 假定给定的模式或主题(分别)仅包含有效的 UTF 代码单元序列。

(注意:这些文档引用自 PCRE2,但 PCRE 行为是相同的)

不幸的是,我认为没有办法从 PHP 设置PCRE2_NO_UTF_CHECK选项。

无论如何,您的基准测试应该经过更多的迭代才能有意义。您应该测量几秒钟的计算时间,以便更好地了解此功能的影响。