как регекспом убить производительность
От: Кодт Россия  
Дата: 27.07.16 09:14
Оценка: 52 (7) :)
Это, конечно, тема обще-алгоритмическая, но в первую очередь, важна именно для скриптовых языков.

Недавно стековерфлоу лёг на простенькой функции, которая чистила строчку от концевых пробелов.
Мы же понимаем, как легко это делается регекспами: s/\s+$//
Беда подкралась внезапно: строка с 20тыс пробелов в середине.
Движок регекспа запнулся и устроил квадратичный забег с откатами.

http://stackstatus.net/post/147710624694/outage-postmortem-july-20-2016

"_____x__"
 +++++X    - неудача, откат
  ++++X    - неудача, откат
   +++X    - неудача, откат
    ++X    - неудача, откат
     +X    - неудача, откат
      X    - сразу неудача, идём дальше
       +++ - удача, конец
Перекуём баги на фичи!
Re: как регекспом убить производительность
От: VTT http://vtt.to
Дата: 27.07.16 10:17
Оценка:
Вообще-то там проблема не столько с регуляркой, сколько с ее применением для одного и того же (не изменяющегося) поста каждый раз при запросе их домашней страницы.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re: как регекспом убить производительность
От: flаt  
Дата: 27.07.16 10:57
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Это, конечно, тема обще-алгоритмическая, но в первую очередь, важна именно для скриптовых языков.


Вот ещё весёлые примеры: https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
Re: как регекспом убить производительность
От: vsb Казахстан  
Дата: 27.07.16 11:10
Оценка: +2
А всё потому, что регэкспы надо разделить на две части — простые и сложные и явно отразить это в API. Простые регэкспы должны работать за O(N) (через DFA, например), а сложные пусть хоть заоткатываются.

А сейчас регэкспы это просто мессиво. Во-первых куча похожих, но отличающихся синтаксисов, во-вторых реализации без каких-то гарантий.
Re[2]: как регекспом убить производительность
От: Кодт Россия  
Дата: 27.07.16 11:11
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Вообще-то там проблема не столько с регуляркой, сколько с ее применением для одного и того же (не изменяющегося) поста каждый раз при запросе их домашней страницы.


И это тоже.
Но про грабли с регулярками надо знать.

А то вдруг найдутся добрые люди и сделают дос-бомбу для какого-нибудь блога или форума.
Перекуём баги на фичи!
Re[2]: как регекспом убить производительность
От: Кодт Россия  
Дата: 27.07.16 11:20
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>А всё потому, что регэкспы надо разделить на две части — простые и сложные и явно отразить это в API. Простые регэкспы должны работать за O(N) (через DFA, например), а сложные пусть хоть заоткатываются.

vsb>А сейчас регэкспы это просто мессиво. Во-первых куча похожих, но отличающихся синтаксисов, во-вторых реализации без каких-то гарантий.

Как отличать простые регекспы от сложных? Ещё один синтаксис?
Перекуём баги на фичи!
Re[3]: как регекспом убить производительность
От: vsb Казахстан  
Дата: 27.07.16 12:32
Оценка:
Здравствуйте, Кодт, Вы писали:

vsb>>А всё потому, что регэкспы надо разделить на две части — простые и сложные и явно отразить это в API. Простые регэкспы должны работать за O(N) (через DFA, например), а сложные пусть хоть заоткатываются.

vsb>>А сейчас регэкспы это просто мессиво. Во-первых куча похожих, но отличающихся синтаксисов, во-вторых реализации без каких-то гарантий.

К>Как отличать простые регекспы от сложных? Ещё один синтаксис?


Простые это подмножество сложных, без тех фич, которые невозможно реализовать в рамках конечных автоматов. Ну и ещё раз повторю — отдельное API для них, чтобы сразу было видно, какую производительность можно ожидать.
Re[4]: как регекспом убить производительность
От: Кодт Россия  
Дата: 27.07.16 13:10
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Простые это подмножество сложных, без тех фич, которые невозможно реализовать в рамках конечных автоматов. Ну и ещё раз повторю — отдельное API для них, чтобы сразу было видно, какую производительность можно ожидать.


Исходный регексп ^(.*?)(\s+)$ вполне себе реализуется на недетерминированном конечном автомате. И в детерминированный его переделать несложно.
Проблема в том, что похожий на него ^(.*?)(\s\s)+$ — находящий чётное количество концевых пробелов (если концевое количество нечётное — первый пробел проигнорирует) — рождает уже довольно громоздкий детерминированный автомат.
И ведь нам интересно не только получить ответ "подходит — не подходит", но и координаты групп найти.
Перекуём баги на фичи!
Re: как регекспом убить производительность
От: Tonal- Россия www.promsoft.ru
Дата: 27.07.16 15:49
Оценка: 126 (4) +2
К>Недавно стековерфлоу лёг на простенькой функции, которая чистила строчку от концевых пробелов.
К>Мы же понимаем, как легко это делается регекспами: s/\s+$//
К>Беда подкралась внезапно: строка с 20тыс пробелов в середине.
А вот не нужно использовать регулярки там, где есть специализированные функции.
В любом современном языке есть функция для отрезания концевых пробелов, которая гарантированно работает быстрее, экономнее и главное корректно в любых ситуациях.

К>Движок регекспа запнулся и устроил квадратичный забег с откатами.

К>http://stackstatus.net/post/147710624694/outage-postmortem-july-20-2016
К>
К>"_____x__"
К> +++++X    - неудача, откат
К>  ++++X    - неудача, откат
К>   +++X    - неудача, откат
К>    ++X    - неудача, откат
К>     +X    - неудача, откат
К>      X    - сразу неудача, идём дальше
К>       +++ - удача, конец
К>

Простейший диалект языка регулярных выражений (РЕГ) эквивалентен детерминированному конечному автомату (ДКА).
И преобразовав РЕГ в ДКА можно проверить совпадение за линейное время.
Но, уже возможность группировки и получения значений сопоставленных групп ломает эту идилию.
Не говоря уже о ссылках на группы.

Поэтому большинство популярных реализаций использует реализацию через интерпретацию — типа расширенный НКА (недетерминированный конечный автомат).
Которая сильно более гибка (например реализации от MS, Lua, PCRE умеют описывать вложенные структуры), но улетает в экспоненту на некоторых типах регулярок и строк.
Что мы и видим на данном примере.

Есть интересный результат от 2000г — тегированный ДКА.
Описан здесь: http://laurikari.net/ville/spire2000-tnfa.pdf
Известные мне реализации на Haskell https://wiki.haskell.org/Regular_expressions#regex-tdfa и С http://laurikari.net/tre/about/
Как можно увидеть они пока что не дотягивают по плюшкам до стандартных реализаций.

Да, о реализации движков очень хорошо изложено в классической книжке «Регулярные выражения» Дж. Фридла, глава 4.
Там же описаны гибридные реализации. Например можно строить для регулярки ДКА (игнорируя некоторые навороты) и НКА. Далее ищем по строке с помощью ДКА, а имея совпадение прогоняем на нём НКА.
К сожалению, о ТДКА Фридл ни во втором, ни в 3-ем русском издании не упоминает.
Re[3]: как регекспом убить производительность
От: flаt  
Дата: 27.07.16 16:53
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Как отличать простые регекспы от сложных? Ещё один синтаксис?


Например: https://github.com/google/re2
Re: как регекспом убить производительность
От: Vain Россия google.ru
Дата: 27.07.16 21:14
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Это, конечно, тема обще-алгоритмическая, но в первую очередь, важна именно для скриптовых языков.

Какая же попаболь у перловцев.. (первый же пример пропагандирует использование г-нищи)

PS Кстати, в перле есть такие встроенные переменные как $&. Помниться долго искал причину почему производительность регулярки была в районе плинтуса, хотя групинга никакого не было..
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Отредактировано 28.07.2016 0:23 Vain . Предыдущая версия . Еще …
Отредактировано 27.07.2016 21:22 Vain . Предыдущая версия .
Re[2]: как регекспом убить производительность
От: anonymous Россия http://denis.ibaev.name/
Дата: 28.07.16 07:43
Оценка: 3 (1) :)
Здравствуйте, Vain, Вы писали:

К>>Это, конечно, тема обще-алгоритмическая, но в первую очередь, важна именно для скриптовых языков.

V>Какая же попаболь у перловцев.. (первый же пример пропагандирует использование г-нищи)

В Perl стандартный способ сделать trim, это код:
$str =~ s/^\s+//;
$str =~ s/\s+$//;

И он не спотыкается на указанной в начальном сообщении строке.

V>PS Кстати, в перле есть такие встроенные переменные как $&. Помниться долго искал причину почему производительность регулярки была в районе плинтуса, хотя групинга никакого не было..


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

Сочувствую боли, которую ты причинил себе.
Re[3]: как регекспом убить производительность
От: Sharov Россия  
Дата: 28.07.16 08:59
Оценка:
Здравствуйте, anonymous, Вы писали:

A>Здравствуйте, Vain, Вы писали:


К>>>Это, конечно, тема обще-алгоритмическая, но в первую очередь, важна именно для скриптовых языков.

V>>Какая же попаболь у перловцев.. (первый же пример пропагандирует использование г-нищи)

A>В Perl стандартный способ сделать trim, это код:

A>
A>$str =~ s/^\s+//;
A>$str =~ s/\s+$//;
A>

A>И он не спотыкается на указанной в начальном сообщении строке.

Почему? Движок, стандарт другой?
Кодом людям нужно помогать!
Re[3]: как регекспом убить производительность
От: VTT http://vtt.to
Дата: 28.07.16 09:52
Оценка:
Здравствуйте, anonymous, Вы писали:

A>В Perl стандартный способ сделать trim, это код:

$str =~ s/^\s+//;
$str =~ s/\s+$//;


Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[4]: как регекспом убить производительность
От: anonymous Россия http://denis.ibaev.name/
Дата: 28.07.16 10:25
Оценка:
Здравствуйте, Sharov, Вы писали:

A>>В Perl стандартный способ сделать trim, это код:

A>>
A>>$str =~ s/^\s+//;
A>>$str =~ s/\s+$//;
A>>

A>>И он не спотыкается на указанной в начальном сообщении строке.
S>Почему? Движок, стандарт другой?

Не знаю. Могу только предположить, что оптимизация именно этих выражений, как часто используемых. Точнее второго: движок сразу понимает, что смотреть нужно конце, а не проверять, тянется ли найденное совпадение до конца.

Вот это уже не будет так работать:
$str =~ s/(a|\s)+$//;

А это будет:
$str =~ s/[a\s]+$//;
Re[4]: как регекспом убить производительность
От: anonymous Россия http://denis.ibaev.name/
Дата: 28.07.16 10:32
Оценка:
Здравствуйте, VTT, Вы писали:

A>>В Perl стандартный способ сделать trim, это код:

VTT>
VTT>$str =~ s/^\s+//;
VTT>$str =~ s/\s+$//;
VTT>

VTT>Image: how_programmers_see_perl_programmers.jpg


s,^\s+,,,s,\s+$,, for$str;
Re[3]: как регекспом убить производительность
От: Vain Россия google.ru
Дата: 28.07.16 21:21
Оценка:
Здравствуйте, anonymous, Вы писали:

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

Чтобы что-то прочитать из документации надо сначало хотя бы знать что искать и где. А когда хер знает из-за чего тормозит это уже претензии к перлу будут в целом.

A>Сочувствую боли, которую ты причинил себе.

У меня не было боли. Отладил и забыл. А вот что-то серьёзное на перле писать я уже не буду. Благо есть языки получше.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[4]: как регекспом убить производительность
От: anonymous Россия http://denis.ibaev.name/
Дата: 28.07.16 21:57
Оценка:
Здравствуйте, Vain, Вы писали:

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

V>Чтобы что-то прочитать из документации надо сначало хотя бы знать что искать и где. А когда хер знает из-за чего тормозит это уже претензии к перлу будут в целом.

perldoc perlvar → Variables related to regular expressions → Performance issues

A>>Сочувствую боли, которую ты причинил себе.

V>У меня не было боли.

Откуда ж ты о ней знаешь?

V>Отладил и забыл. А вот что-то серьёзное на перле писать я уже не буду. Благо есть языки получше.


Ага, нахватаются где-то вершков про встроенные переменные, а потом язык у них виноват. Другие потом читают написанный первыми код и думают: «Капец язык, ничего не понять!»
Re: как регекспом убить производительность
От: Слава  
Дата: 28.07.16 22:10
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Это, конечно, тема обще-алгоритмическая, но в первую очередь, важна именно для скриптовых языков.


К>Недавно стековерфлоу лёг на простенькой функции, которая чистила строчку от концевых пробелов.

К>Мы же понимаем, как легко это делается регекспами: s/\s+$//

Если я правильно помню, подобный поиск первого не-пробельного символа делался на ассемблере с помощью одной инструкции вроде repnz scas <образец>, <указатель на строку>, или что-то вроде. Для C-подобных языков это делалось бы обычным for i--, а в C# вообще есть стандартный TrimEnd у строки. Зачем там потребовались регэкспы? Кто-то очень хорошо их знал, ценил, любил и применял?
Re[2]: как регекспом убить производительность
От: мыщъх США http://nezumi-lab.org
Дата: 29.07.16 05:28
Оценка: +1
Здравствуйте, Слава, Вы писали:

С>Здравствуйте, Кодт, Вы писали:


К>>Мы же понимаем, как легко это делается регекспами: s/\s+$//

С>Зачем там потребовались регэкспы? Кто-то очень хорошо их знал, ценил, любил и применял?
начнем с того, что \s это не только пробел, но и табуляция. жду рабочего примера на асме или си. да, кодировки могут быть разные. при этом желательно чтобы по времени разработки и по наглядности кода вы не сильно отличались от регулярки. ну а производительность регулярок -- гуглим статьи Russ Cox'a. так что проблема не в регулярках, а в реализациях. почему в популярных языках используются такие реализации -- это другой вопрос.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.