Подтвердить что ты не робот

Плохая производительность RegEx от 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);
});

Ниже приведены результаты тестирования на моей машине:

Firefox 24: среднее время между 370 и 450 мс для выполнения 10000 регулярных выражений
Chrome 30, Opera 17, IE 10: среднее время между 0,3 и 0,6 мс

Эта разница становится еще больше, если строка для теста становится больше. Строка длиной 6000 символов увеличивает среднее время в Firefox до ~ 1,5 секунд (!), в то время как другим браузерам по-прежнему требуется ~ 0,5 миллисекунды (!) (Рабочий пример на JSFiddle с 6000 символами).

Почему существует такое большое различие в производительности между Firefox и всеми другими браузерами, и могу ли я его улучшить?

Обратите внимание, что я не могу настроить исполняемые регулярные выражения, потому что они в основном генерируются генератором синтаксического анализатора, и я не хочу вручную изменять встроенный код парсера.

4b9b3361

Ответ 1

Это группа захвата RegExp, которая вас достала:

/^[0-9]+/ и/или /^(?:[0-9])+/ и/или /^([0-9]+)/ на порядок быстрее, чем /^([0-9])+/. И они должны быть жизнеспособными альтернативами.

Я ожидал бы, что он будет немного медленнее с захватом групп, но что это намного медленнее меня удивляет. Однако медленная версия имеет потенциал для создания много-много захватов, в то время как другие версии этого не делают, так что это кажется важным отличием.

Ненаучный jsperf.

Вы можете удалить файл.