67
© 2015 NetCracker Technology Corporation Confidential Выражаемся регулярно Владимир Ситников JPoint 2015

Regular expressions

Embed Size (px)

Citation preview

Page 1: Regular expressions

© 2015 NetCracker Technology Corporation Confidential

Выражаемся регулярно

Владимир СитниковJPoint 2015

Page 2: Regular expressions

2© 2015 NetCracker Technology Corporation Confidential

О себе

• Владимир Ситников, @VladimirSitnikv

• Инженер по производительности в NetCracker

• 8 лет опыта с Java/SQL

• Мои предложения принимались в j.l.String, j.u.c.CopyOnWriteArrayList, jmh

Page 3: Regular expressions

3© 2015 NetCracker Technology Corporation Confidential

План

•Небольшое введение

•Разгоняем /.*/ и /.*?/

•Устраняем StackOverflowError

•Regexp vs XPath

•ANTLR, VTD-XML – не на этом докладе

Page 4: Regular expressions

4© 2015 NetCracker Technology Corporation Confidential

Regexp

he comes, hi s unholy radiance destro҉ying all enli ghtenment, HTML

tags leaking from yo ur eyes like liq uid pain, the song of regular exp ression parsing will exti nguish the voices of mor tal man from the sphere I can see it can you see

ît t it is beautiful t he final snuffing of the

lie s of Man ALL IS LOS T ALL I S LOST the pony he comes he c omes he

comes the ich or permeates all MY FACE MY FACE ᵒh god no NO NOOO O NΘ stop the an *

g

l e s a r

e n ot rè

al ZALGΌ IS ҉

TO Ɲȳ THE P O NY H

E

҉ C O M E S

Page 5: Regular expressions

5© 2015 NetCracker Technology Corporation Confidential

Случаи использования regexp

• Анализ success/fail в логах, xml ответа

• Поиск “<status>success</status>”

• Засекречивание паролей при логировании

• Замена “password=(.*)” на “password=****”

• По неосторожности

Page 6: Regular expressions

6© 2015 NetCracker Technology Corporation Confidential

Случаи использования regexp

• Анализ success/fail в логах, xml ответа

• Поиск “<status>success</status>”

• Засекречивание паролей при логировании

• Замена “password=(.*)” на “password=****”

• По неосторожности (String.replaceAll и т.п.)

Page 7: Regular expressions

7© 2015 NetCracker Technology Corporation Confidential

Две проблемы

•У вас проблема, и вы собрались использовать регулярные выражения для её решения

•Теперь у вас две проблемы

Page 8: Regular expressions

8© 2015 NetCracker Technology Corporation Confidential

Ужасы нашего городка

Page 9: Regular expressions

9© 2015 NetCracker Technology Corporation Confidential

Что не так с .* ?

•Обычно, regexp библиотеки работают «методом перебора»

•Если хвост «не подошёл», откатим попробуем Regexp ещё раз

Page 10: Regular expressions

10© 2015 NetCracker Technology Corporation Confidential

/^.*ab$/.match("ab")

|ab.*ab

a|b.*ab

ab|.*ab

ab|.*ab

a|b.*abоткатились

a|b.*abне помогло

|ab.*abоткатились

ab|.*abУра!

.* – жадная конструкция. Ест как не в себя

Page 11: Regular expressions

11© 2015 NetCracker Technology Corporation Confidential

Ленивые вычисления спешат на помощь

•Ленивый вариант помогает в конкретном случае/^.*?abc$/.match("abc")

•Но помогает далеко не всегда

Page 12: Regular expressions

12© 2015 NetCracker Technology Corporation Confidential

/^.*?b$/.match("aaa")

|aab.*?ab

a|ab.*?ab

a|ab.*?ab

|aab.*?abоткатились

a|ab.*?ab

aa|b.*?ab

aab|.*?abУра!

.*? – ленивая конструкция

Page 13: Regular expressions

13© 2015 NetCracker Technology Corporation Confidential

Ленивые вычисления и реальность

•Если regexp не подходит к строке, то всё равно придётся рассмотреть все случаи

•Был случай, что выражение вида <.*:item.*>(.*)</.*:item> работало 30 секунд на 100 КиБ строке

Page 14: Regular expressions

14© 2015 NetCracker Technology Corporation Confidential

Ленивые вычисления и реальность

<.*?:item.*?>(.*?)</.*?:item>

Уже лучше, regexp engine всё равно будет откатываться при выполнении .*

Page 15: Regular expressions

15© 2015 NetCracker Technology Corporation Confidential

Ужасы нашего городка

Page 16: Regular expressions

16© 2015 NetCracker Technology Corporation Confidential

Ленивые вычисления и реальность

<[^:>]*:item[^>]*>([^<]*)</...

[^>] не выйдет за границы XML тэга, вариантов будет намного меньше, и работать будет гораздо быстрее

Page 17: Regular expressions

17© 2015 NetCracker Technology Corporation Confidential

Сравним 3 варианта

• <ROW>.*<ACCNT>.*</ACCNT>.*</ROW>

• <ROW>.*?<ACCNT>.*?</ACCNT>.*?</ROW>

• <ROW>.*?<ACCNT>[^<]*</ACCNT>.*?</ROW>

Page 18: Regular expressions

18© 2015 NetCracker Technology Corporation Confidential

График скорости .*, .*?, [^>]

0

100

200

300

400

1 КиБ 2 КиБ 3 КиБ 4 КиБ

Быстрее,

оп

/мс

.*

.*?

[^>]*

Page 19: Regular expressions

19© 2015 NetCracker Technology Corporation Confidential

График скорости .*, .*?, [^>], шаблон не найден

0

10

20

30

40

50

1 КиБ 2 КиБ 3 КиБ 4 КиБ

Быстрее,

оп

/мс

.*

.*?

[^>]*

Поиск тормозил, т.к. шаблон не нашёлся

Page 20: Regular expressions

20© 2015 NetCracker Technology Corporation Confidential

JPoint или Joker?

Pattern.compile("(Joker|JPoint)+")

• На строке "JokerJPoint…700раз…Joker"падает с ошибкой StackOverflowError

• Как так?

Page 21: Regular expressions

21© 2015 NetCracker Technology Corporation Confidential

JPoint или Joker?

java.lang.StackOverflowError

.regex.Pattern$BranchConn.match(Pattern.java:4568

.regex.Pattern$Slice.match(Pattern.java:3972)

.regex.Pattern$Branch.match(Pattern.java:4604)

.regex.Pattern$GroupHead.match(Pattern.java:4658)

.regex.Pattern$Loop.match(Pattern.java:4785)

.regex.Pattern$GroupTail.match(Pattern.java:4717)

.regex.Pattern$BranchConn.match(Pattern.java:4568

Page 22: Regular expressions

22© 2015 NetCracker Technology Corporation Confidential

JPoint или Joker?

Pattern.compile("(Joker|JPoint)+?")

• Добавим "?". Помогло?

• Падать перестало, но теперь посещаем только первую конференцию из всех

Page 23: Regular expressions

23© 2015 NetCracker Technology Corporation Confidential

Однажды в NetBeans

NetBeans Bug 154894 - StackOverflowError at java.util.regex.Pattern$SliceI.match

support/JavadocAndSourceRootDetection.javaString whitespace = "…(?:[^*]|\\*[^/])*…"

Page 24: Regular expressions

24© 2015 NetCracker Technology Corporation Confidential

JPoint или Joker?

Pattern.compile("(Joker|JPoint)+")

• Regexp engine оставляет шанс отката для проверки альтернативной ветки

• В OpenJDK Pattern использует ООП java stack, при большой вложенности и получается StackOverflow

Page 25: Regular expressions

25© 2015 NetCracker Technology Corporation Confidential

Можно ли избавиться от откатов?

Без откатов крайне сложно (невозможно?) сделать

Page 26: Regular expressions

26© 2015 NetCracker Technology Corporation Confidential

Можно ли избавиться от откатов?

Без откатов крайне сложно (невозможно?) сделать back-references:

(.*)\1aa, abab, abcabc, …

Page 27: Regular expressions

27© 2015 NetCracker Technology Corporation Confidential

Как это выглядит в документации

Greedy Reluctant Posessive Значение

X? X?? X?+ X, один раз или ни единого

X* X*? X*+ X, ноль или более раз

X+ X+? X++ X, один или более раз

X{n} X{n}? X{n}+ X, ровно n раз

X{n,} X{n,}? X{n,}+ X, как минимум n раз

X{n,m} X{n,m}? X{n,m}+ X, от n до m раз

https://docs.oracle.com/javase/tutorial/essential/regex/quant.html

Page 28: Regular expressions

28© 2015 NetCracker Technology Corporation Confidential

Чего-чего?

Page 29: Regular expressions

29© 2015 NetCracker Technology Corporation Confidential

А на это мало кто обращает внимание

Greedy Reluctant Posessive Значение

X? X?? X?+ X, один раз или ни единого

X* X*? X*+ X, ноль или более раз

X+ X+? X++ X, один или более раз

Page 30: Regular expressions

30© 2015 NetCracker Technology Corporation Confidential

С++ спешит на помощь

Pattern.compile("(Joker|JPoint)++")

• ++ и *+ отключает откаты, и это как раз то, что нужно

Page 31: Regular expressions

31© 2015 NetCracker Technology Corporation Confidential

«Мне повезёт»

Pattern.compile("(?>Joker|JPoint)+")

?> – режим, при котором все откаты внутри скобок «забываются». Первый совпавший вариант принимается как окончательный

Page 32: Regular expressions

32© 2015 NetCracker Technology Corporation Confidential

График скорости .*, .*, [^>], шаблон не найден

0

20

40

60

80

100

1 КиБ 2 КиБ 3 КиБ 4 КиБ

Быстрее,

оп

/мс

.*

.*?

[^>]*

(?>

<ROW>(?>.*?<RSACCTNMBR>)(?>[^<]*</RSACCTNMBR>)…

Page 33: Regular expressions

33© 2015 NetCracker Technology Corporation Confidential

/(?>ab|a)b/.match("a")

|ab(?>ab|a)b

a|b(?>ab|a)b

ab|(?>ab|a)b

ab|(?>ab|a)b

ab|(?>ab|a)b

Шаблон не найден

(?>ab|a) не будет проверять второй вариант

Page 34: Regular expressions

34© 2015 NetCracker Technology Corporation Confidential

А давайте ускорим String?

• String.format("...") использует java.util.regex.Pattern для разбора формата

formatSpecifier = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";

Page 35: Regular expressions

35© 2015 NetCracker Technology Corporation Confidential

Откуда взять тестовые данные?

Возьмём шаблон из самого JMH:

(min, avg, max) = (%s, %s, %s), stdev = %s%n

CI (99.9%%): [%s, %s] (assumes normal distribution)

Page 36: Regular expressions

36© 2015 NetCracker Technology Corporation Confidential

А давайте ускорим String?

formatSpecifier = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";

fastSpecifier = "%(\\d++\\$)?+([-#+ 0,(\\<]+)?+(\\d++)?+(\\.\\d++)?+([tT])?+([a-zA-Z%])";

Page 37: Regular expressions

37© 2015 NetCracker Technology Corporation Confidential

Бесплатный сыр

Benchmark Cnt Score Error Units

jdk18u45 60 1,067 ± 0,025 us/op

optimized 60 0,917 ± 0,021 us/op

Page 38: Regular expressions

38© 2015 NetCracker Technology Corporation Confidential

NetBeans, NetBeans

(?>[^*]|\*[^/])

Работает?

Page 39: Regular expressions

39© 2015 NetCracker Technology Corporation Confidential

NetBeans, NetBeans

(?>[^*]|\*[^/])

StackOverflow не бросит, а на /***/ не сработает

Page 40: Regular expressions

40© 2015 NetCracker Technology Corporation Confidential

NetBeans, NetBeans

(?>[^*]|\*(?!/))

Вот это уже сработает!

(?!/) – negative lookahead

Page 41: Regular expressions

41© 2015 NetCracker Technology Corporation Confidential

Признаки плохого регулярного выражения

• Неспецифичные символы

• .*

• Внутри группировок

• Повторения: (a+)+, ([a-zA-Z]+)*=

• Альтернативы с пересечениями: (a|aa)+, (a|a?)+

Page 42: Regular expressions

42© 2015 NetCracker Technology Corporation Confidential

А можно ли совсем без откатов?

Если отказаться от back-references, то есть быстрые алгоритмы

Детерминированный конечный автомат для /(1|01*0)*/

Page 43: Regular expressions

43© 2015 NetCracker Technology Corporation Confidential

Современные regexp библиотеки в java

Название Год выпуска Алгоритм

java.util.regex.Pattern 2015 backtrack

com.basistech.tclre 2015 DFA

org.jruby.joni (Nashorn) 2014 backtrack

Apache Oro 2000 backtrack

JRegex 2013 backtrack

Page 44: Regular expressions

44© 2015 NetCracker Technology Corporation Confidential

Современные regexp библиотеки в смежных языках

Название Версия Алгоритм

Oracle 12с backtrack

PostgreSQL 9.3 DFA

MySQL 5.6 DFA

JavaScript/Chrome 43 backtrack

JavaScript/FireFox 48 backtrack

Python/Ruby/Perl/PHP backtrack

Page 45: Regular expressions

45© 2015 NetCracker Technology Corporation Confidential

Как проверить backtrack?

/(x+x+)+y/.test("xxxxx…20 раз…xxx")

Если regexp с откатами, то он будет очень долго подбирать x+

Page 46: Regular expressions

46© 2015 NetCracker Technology Corporation Confidential

/(x+x+)+y/.test("xxxx…xxx")

0

500

1000

1500

2000

5 6 7

Быстрее,

op

/ms

java

joni

tcl

jregex

Page 47: Regular expressions

47© 2015 NetCracker Technology Corporation Confidential

/(x+x+)+y/.test("xxxx…xxx")

50 1 112 4 1

1240 1185 1151

21 0.07 0.020

500

1000

1500

10 15 20

Быстрее, o

p/m

s

java

joni

tcl

jregex

Page 48: Regular expressions

48© 2015 NetCracker Technology Corporation Confidential

<ROW>(?>.*?<RSACCTNMBR>)(?>[^<]*</RSACCTNMBR>)

30

51

0.4

23

4 1 0.4

42

93 1

95

47

312426 26 25 25

0

20

40

60

80

100

1 КиБ 2 КиБ 3 КиБ 4 КиБ

Быстрее,

op

/ms

.*

.*?

[^>]*

(?>

tcl, [^>]*

Page 49: Regular expressions

49© 2015 NetCracker Technology Corporation Confidential

Page 50: Regular expressions

50© 2015 NetCracker Technology Corporation Confidential

CPU vs memory

• DFA подход потребляет больше памяти

• Патчим jmh, и там появляется allocation profiler

Page 51: Regular expressions

51© 2015 NetCracker Technology Corporation Confidential

CPU vs memory: <row>…

0.2 0.2 0.2 0.2

32 32 32 32

0

10

20

30

40

1 КиБ 2 КиБ 3 КиБ 4 КиБ

Пам

ять,

КиБ

/op

java

tcl

Pattern#matcher потребляет всего 200 байт

Page 52: Regular expressions

52© 2015 NetCracker Technology Corporation Confidential

CPU vs memory: <row>…

0 0 0 0

32 32 32 32

0

10

20

30

40

1 КиБ 2 КиБ 3 КиБ 4 КиБ

Пам

ять,

КиБ

/op

java

tcl

При Matcher#reset память вообще отдыхает

Page 53: Regular expressions

53© 2015 NetCracker Technology Corporation Confidential

j.l.r.Pattern

• Поддерживает Unicode (java 1.7+)

• Управляемый откат

• Малое потребление памяти

Page 54: Regular expressions

54© 2015 NetCracker Technology Corporation Confidential

org.jruby.Joni

• Поддерживает Unicode

• Управляемый откат

• Может работать с byte[] в UTF-8 кодировке

• Поддерживает Thread#interrupt()

• Малое потребление памяти

Page 55: Regular expressions

55© 2015 NetCracker Technology Corporation Confidential

Tcl-re

• DFA алгоритм, стабильное время работы

• Не самая быстрая на простых шаблонах

• Большое потребление памяти

Page 56: Regular expressions

56© 2015 NetCracker Technology Corporation Confidential

Они всё ещё разбирают XML regexp’ами

Page 57: Regular expressions

57© 2015 NetCracker Technology Corporation Confidential

Сравним с XPath

• Парсинг XML (20, 40, 60КиБ)

• Поиск тэга по точному пути

• Замена пароля звёздочками (поиск по имени атрибута)

Page 58: Regular expressions

58© 2015 NetCracker Technology Corporation Confidential

XPath

• Парсинг XML

• Поиск тэга по точному пути

• ICOMS/MAC00029/@ACNT

• Замена пароля звёздочками (поиск по имени атрибута)

• //self::node()[@__hPINNMBR]

Page 59: Regular expressions

59© 2015 NetCracker Technology Corporation Confidential

Regexp

• Парсинг XML

• Поиск тэга по точному пути, все вхождения

• <MAC00029(?:.(?! ACNT))*+ ACNTN="([^"]*+)"

• Замена пароля звёздочками (поиск по имени атрибута, все вхождения)

• __hPINNMBR="[^"]*+"

Page 60: Regular expressions

60© 2015 NetCracker Technology Corporation Confidential

Тестовая среда

• Java 1.8u45, JMH 1.9

• Встроенный XPath

• newXPath().compile

• xpath.evaluate(parsedXml, XPathConstants.NODESET)

• Intel Core i7-4960HQ CPU @ 2.60GHz

Page 61: Regular expressions

61© 2015 NetCracker Technology Corporation Confidential

CPU: XML vs Pattern

6 3 18 7 43 4 2

67

35

15

79

38

24

0

20

40

60

80

100

20 КиБ 40 КиБ 60 КиБ

Быстрее,

оп

/мс

xmlParse

xpExact

xpSearch

reExact

reSearch

j.u.r.Pattern

Page 62: Regular expressions

62© 2015 NetCracker Technology Corporation Confidential

Heap: XML vs Pattern

86

167

266299 310

358

500

319370

0.2 0.2 0.20.2 0.2 0.20

200

400

600

20 КиБ 40 КиБ 60 КиБ

Пам

ять,

КиБ

/oп

xmlParse

xpExact

xpSearch

reExact

reSearch

Page 63: Regular expressions

63© 2015 NetCracker Technology Corporation Confidential

Полмегабайта на xpath

• Где моя память, чувак?!

.jvmArgs("-XX:+UnlockCommercialFeatures","-XX:+FlightRecorder","-XX:StartFlightRecording=duration=60s,"

+ "settings=profile,"+ "filename=xpathExact_60k.jfr")

Page 64: Regular expressions

64© 2015 NetCracker Technology Corporation Confidential

JFR показывает кто виноват

Java FlightRecorder показывает, что виноват XPathContext

Page 65: Regular expressions

65© 2015 NetCracker Technology Corporation Confidential

One way ticket, one way ticket, …

JDK-6344064 Potential XPath performance issue in "new XPathContext()".

Fresh XPathContext is created for each call to evaluate(). However, the way the API is set up now, it is difficult not to do this. I'm assuming that the use case is having a W3C DOM and evaluating multiple XPath expressions on it.

Page 66: Regular expressions

66© 2015 NetCracker Technology Corporation Confidential

Заключение

• (?>…), ++, *+, ?+ отключают backtracking, что сильно ускоряет regexp

• В простых случаях java.util работает быстро

• Точный XPath работает быстрее Pattern, но аллоцирует уйму памяти

Page 67: Regular expressions

© 2015 NetCracker Technology Corporation Confidential

Вопросы?

Владимир Ситников,JPoint 2015