Firefox糟糕的RegEx性能

sua*_*kim 12 javascript regex performance firefox

我使用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);
});
Run Code Online (Sandbox Code Playgroud)

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

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

如果要测试的字符串变大,这种差异会变得更大.6000字符长的字符串将Firefox中的平均时间增加到~1.5秒(!),而其他浏览器仍然需要~0.5毫秒(!)(JSFiddle上的工作示例为6000个字符).

为什么Firefox和所有其他浏览器之间存在如此巨大的性能差异,无论如何我能改进它吗?

请注意,我无法调整执行的正则表达式本身,因为它们主要由解析器生成器生成,我不想手动更改生成的解析器代码.

nma*_*ier 2

正是RegExp捕获分组让你得到了:

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

我预计捕获组时它会稍微慢一些,但是慢得多让我感到惊讶。然而,慢速版本有可能创建大量捕获,而其他版本则不然,因此这似乎是一个重要的区别。

不科学的jsperf

您可能想要提交错误

  • 它要慢得多,因为 Firefox 和 Safari 使用的正则表达式 JIT (Yarr) 当前无法编译具有量化捕获的正则表达式(在上面的示例中,捕获括号后的“+”)。请参阅 https://bugs.webkit.org/show_bug.cgi?id=122891 了解跟踪此问题的错误。因此,正则表达式在 Yarr 正则表达式解释器中执行,这当然比运行 JIT 代码慢很多。 (4认同)