Ruby 2.4: Как ускорить динамическую инициализацию регулярных выражений для использования с .match? - PullRequest
0 голосов
/ 22 мая 2018

Я только что прочитал о Regexp.match?('string') для Ruby 2.4 и был очень рад видеть результаты!Но когда я попробовал это в своем приложении, я едва увидел какой-либо выгоды.

str = 's'
Benchmark.bm do |b|
  b.report(".match         ") { 100000.times { 'string'.match /s/ } }
  b.report(".match?        ") { 100000.times { 'string'.match? /s/ } }
  b.report(".match dynamic ") { 100000.times { 'string'.match /#{str}/ } }
  b.report(".match? dynamic") { 100000.times { 'string'.match? /#{str}/ } }
end
                 user     system      total        real
.match           0.140000   0.000000   0.140000 (  0.143658)
.match?          0.020000   0.000000   0.020000 (  0.029628)
.match dynamic   0.370000   0.010000   0.380000 (  0.371935)
.match? dynamic  0.260000   0.010000   0.270000 (  0.278614)

Из эталонного теста мы видим огромный выигрыш от .match до .match?, но как только я начинаю динамически создавать сложныеРегулярное выражение в соответствии с требованиями моего приложения, я теряю большую часть прибыли.

Мой вопрос: почему существует такая радикальная разница и можно ли каким-то образом создать динамическое регулярное выражение, чтобы использовать производительность .matches? впример ниже?Я проверил свои тесты, используя ruby 2.4.2p198

str = 'my text with words'
reg_str = '((^|[\s\"“])(cherry pie|cherry pies)($|[\s\"”\.\,\:\?\!])|(\#(cherrypie|cherrypies)($|\s|\#|\.|\,|\:|\?|\!)))'
puts Benchmark.measure {
  100000.times { str.match? /#{reg_str}/i }
}
9.380000   0.010000   9.390000 (  9.403821)

puts Benchmark.measure {
  100000.times { str.match? /((^|[\s\"“])(cherry pie|cherry pies)($|[\s\"”\.\,\:\?\!])|(\#(cherrypie|cherrypies)($|\s|\#|\.|\,|\:|\?|\!)))/i }
}  
0.020000   0.000000   0.020000 (  0.017900)

Ответы [ 3 ]

0 голосов
/ 22 мая 2018

Вы в основном измеряете интерполяцию строк / регулярных выражений по сравнению с буквальной реализацией.Время match? само по себе никак не влияет на результат измерения.

Чтобы сравнить match? с match, необходимо создать экземпляр регулярного выражения:

str = 'my text with words'
reg_str = '...'
reg = /#{reg_str}/i
puts Benchmark.measure {
  100000.times { str.match? reg }
}

Результат выше будет примерно таким же, как во втором тесте.

Тем не менее, интерполяция строки / регулярного выражения - это зверь, который занимает большую часть времени.Если вам нужна сложная интерполяция в регулярном выражении, разница между match? и match не будет заметна, поскольку интерполяция является узким местом, а не совпадением.

0 голосов
/ 23 мая 2018

Повышение скорости на match? обусловлено отсутствием выделения объектов MatchData и глобальных переменных, таких как $1.Он просто возвращает true или false.Вы не можете использовать match?, если вам нужно что-то вернуть из регулярного выражения.

match? не будет быстрее при компиляции строк регулярного выражения в Regexp объектах.

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

# bad:
lines.each { |line| puts "Found a match!" if line.match?(/abcd/) }

# good:
regex = /abcd/
lines.each { |line| puts "Found a match!" if line.match?(regex) }
0 голосов
/ 22 мая 2018

Используйте модификатор / o , поэтому интерполяция выполняется только один раз:

str = 's'
Benchmark.bm do |b|
  b.report(".match         ") { 100000.times { 'string'.match /s/ } }
  b.report(".match?        ") { 100000.times { 'string'.match? /s/ } }
  b.report(".match dynamic ") { 100000.times { 'string'.match /#{str}/o } }
  b.report(".match? dynamic") { 100000.times { 'string'.match? /#{str}/o } }
end
       user     system      total        real
.match           0.120000   0.010000   0.130000 (  0.117889)
.match?          0.020000   0.000000   0.020000 (  0.027255)
.match dynamic   0.110000   0.000000   0.110000 (  0.113300)
.match? dynamic  0.030000   0.000000   0.030000 (  0.034755)
...