Чередование в регулярных выражениях, кажется, очень медленно в больших файлах - PullRequest
6 голосов
/ 10 апреля 2020

Я пытаюсь использовать это регулярное выражение:

my @vulnerabilities = ($g ~~ m:g/\s+("Low"||"Medium"||"High")\s+/);

На чанах файлов, таких как этот , чанках, которые go от одного "отсортированы" к другому. Каждый из них должен быть несколько сотен килобайт, и все они вместе занимают от 1 до 3 секунд все вместе (делится на 32 за одну итерацию).

Как это можно ускорить?

Ответы [ 3 ]

8 голосов
/ 10 апреля 2020

Проверка файла примера показывает, что строки встречаются только как целая строка, начиная с табуляции и пробела. Из ваших ответов я также понял, что вы действительно заинтересованы только в подсчете. Если это так, то я бы предложил что-то вроде этого решения:

my %targets = "\t Low", "Low", "\t Medium", "Medium", "\t High", "High";
my %vulnerabilities is Bag = $g.lines.map: {
    %targets{$_} // Empty
}
dd %vulnerabilities;  # ("Low"=>2877,"Medium"=>54).Bag

На моем компьютере это занимает около 0,25 секунды.

Всегда стоит посмотреть на проблемную область. тщательно!

3 голосов
/ 10 апреля 2020

Это можно немного упростить. Вы используете \s+ до и после, но нужно ли это? Я думаю, что вам нужно просто обеспечить границу слова или только один пробел, таким образом, вы можете использовать

\s("Low"||"Medium"||"High")\s

или вы можете использовать \b вместо \s.

Второй шаг чтобы не использовать группу захвата, вместо этого используйте не захватывая grous, потому что механизм регулярных выражений тратит время и память на «запоминание» групп, поэтому вы можете попробовать:

\s(?:"Low"||"Medium"||"High")\s
2 голосов
/ 10 апреля 2020

TL; DR Я сравнивал решения на недавнем раскудо, используя ваши данные выборки. Уродливое решение для грубой силы, которое я представляю здесь, примерно в два раза быстрее, чем восхитительно элегантное решение, представленное Лиз. Вероятно, вы могли бы улучшить в разы другой порядок или больше, разбивая ваши данные и параллельно обрабатывая их. Я также обсуждаю другие варианты, если этого недостаточно.

Чередование выглядит как красная сельдь

Когда я устранил чередование (оставив только "Low") и запустил код на недавнем раскудо, Время было примерно таким же. Так что я думаю, что это красная сельдь, и я не изучал этот аспект более подробно.

Параллельная обработка выглядит многообещающе

Из ваших данных видно, что вы можете разбить их, разделив на какую-то произвольную строку, и затем шаблон сопоставляет каждую часть параллельно, а затем объединяет результаты.

Это может net принести вам существенный выигрыш, в зависимости от различных факторов, связанных с вашей системой и данными, которые вы обрабатываете.

Но Я не исследовал эту опцию.

Самые быстрые результаты, которые я видел

Самые быстрые результаты, которые я видел, были с этим кодом:

my %counts;
$g ~~ m:g / "\t " [ 'Low' || 'Medium' || 'High' ] \n { %counts{$/}++ } /;
say %counts.map: { .key.trim, .value }

Это отображает :

((Low 2877) (Medium 54))

Этот подход включает в себя изменения, аналогичные тем, о которых говорил Михал Турчин, но настойчивее:

  • Я выбросил все захват не только не удосужившись захватить 'Low' или что-либо еще, но и отбросить все результаты матча.

  • Я заменил \s+ узоры с конкретными персонажами а не классы персонажей. Я сделал это на основании моих случайных тестов с недавним rakudo, которые предположили, что это немного быстрее.

Выход за пределы регулярных выражений raku

Raku разработан для полной универсальности Unicode , И его двигатель регулярных выражений является чрезвычайно мощным. Но похоже, что ваши данные - это просто ASCII, а ваш шаблон - типичное очень простое регулярное выражение. Таким образом, вы используете кувалду, чтобы сломать орех. Это не должно иметь большого значения - кувалда, как предполагается, тоже хороша, как щелкунчик, - но механизм регулярных выражений raku до сих пор очень плохо оптимизирован.

Возможно, этот орех - простой пример, и вы Нам просто интересно использовать встроенные в Raku возможности регулярных выражений до их максимальной текущей производительности.

Но если нет, вам нужно еще больше скорости и ускорения от этого или других более эффективных решений в raku в сочетании с параллельной обработкой, go недостаточно, чтобы найти нужное вам место, стоит подумать о том, чтобы не использовать raku или использовать его с другим инструментом.

Один идиоматический c способ использовать raku с другим инструментом - это использовать Inline , причем в данном случае очевидным является Inline::Perl5. Используя это, вы можете попробовать быстрый встроенный движок регулярных выражений perl или даже использовать его плагин-возможность для подключения действительно быстрого движка регулярных выражений.

И, учитывая простоту сопоставляемого вами шаблона Вы могли бы даже полностью отказаться от регулярных выражений, написав небольшой кусочек к некоторому низкоуровневому инструменту поиска необработанного текста (возможно, сохранив смещения символов и затем сгенерировав соответствующие объекты соответствия raku из результатов).

...