火狐浏览器正则表达式性能差

12

我使用JavaScript解析器生成器JISON为我的用户创建的一些脚本创建解析器。最近,我注意到Firefox上的解析过程比我页面支持的任何其他浏览器(IE10、最新版Chrome和Opera)都要慢得多。

在深入挖掘生成的解析器源代码后,我将问题缩小到了一行代码,该代码执行一些正则表达式来标记要解析的代码。当然,这行代码经常被执行。

我创建了一个小的测试用例,其中包含一些随机字符串(大约1300个字符长)和一个非常通用的正则表达式。这个测试用例测量了执行10000次正则表达式的平均时间(JSFiddle上的工作示例):

$(document).ready(function() {
    var str = 'asdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj ölkasjd flöaksjdf löask dfjkasdfasdfa asdfasdf asdf asdf asdfasödlfkja asldfkj asdölkfj aslödkjf aösldkfj',
        regex = new RegExp('^([0-9])+'),
        durations = [],
        resHtml = 'Durations:',
        totalDuration = 0,
        matches, start;

    // Perform "timing test" 10 times to get some average duration
    for (var i = 0; i < 10; i++) {
        // Execute regex 10000 times and see how long it takes
        start = window.performance.now();
        for (var j = 0; j < 10000; j++) {
            regex.exec(str);
        }
        durations.push(window.performance.now() - start);
    }

    // Create output string and update DIV
    for (var i = 0; i < durations.length; i++) {
        totalDuration += durations[i];
        resHtml += '<br>' + i + ': ' + (parseInt(durations[i] * 100, 10) / 100) + ' ms';
    }
    resHtml += '<br>==========';
    resHtml += '<br>Avg: ' + (parseInt((totalDuration / durations.length) * 100, 10) / 100) + ' ms';

    $('#result').html(resHtml);
});

以下是我的机器上的测试结果:

Firefox 24:10000个正则表达式执行的平均时间在370和450毫秒之间
Chrome 30、Opera 17、IE 10:平均时间在0.3到0.6毫秒之间

如果要测试的字符串更大,这种差异会变得更加显著。一个6000个字符长的字符串会使Firefox的平均时间增加到约1.5秒!而其他浏览器仍然只需要约0.5毫秒!在JSFiddle上有6000个字符的工作示例

为什么Firefox与所有其他浏览器之间存在如此巨大的性能差异?我能否以任何方式改进它?

请注意,我不能调整执行的正则表达式本身,因为它们大多是由解析器生成器生成的,我不想手动更改构建解析器的代码。

1个回答

2

问题出在使用了RegExp捕获分组:

/^[0-9]+/或者/^(?:[0-9])+/或者/^([0-9]+)/的运行速度比/^([0-9])+/快几个数量级,它们应该是可行的替代方案。

我本来以为使用捕获分组会稍微慢一点,但是它这么慢真让我惊讶。然而,慢版本有可能会创建很多捕获,而其他版本则不会,所以这似乎是一个重要的区别。

非科学的性能测试

您可能需要提交错误报告


4
由于Firefox和Safari使用的正则表达式JIT(Yarr)无法编译具有量化捕获的正则表达式(在上面的示例中,捕获圆括号后面的“+”),因此速度要慢得多。请参见https://bugs.webkit.org/show_bug.cgi?id=122891以跟踪此错误。因此,正则表达式在Yarr正则表达式解释器中执行,这当然比运行JIT代码要慢得多。 - Boris Zbarsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接