Qual é a maneira mais rápida de verificar se uma string corresponde a uma expressão regular em Ruby?
Meu problema é que preciso "egrep" por meio de uma lista enorme de strings para descobrir quais são as que correspondem a um regexp fornecido em tempo de execução. Eu só me preocupo se a string corresponde ao regexp, não onde ela corresponde, nem qual é o conteúdo dos grupos correspondentes. Espero que essa suposição possa ser usada para reduzir a quantidade de tempo que meu código gasta combinando expressões regulares.
Eu carrego o regexp com
pattern = Regexp.new(ptx).freeze
Eu descobri que string =~ pattern
é um pouco mais rápido do que string.match(pattern)
.
Existem outros truques ou atalhos que podem ser usados para tornar este teste ainda mais rápido?
ruby
regex
performance
gioele
fonte
fonte
Respostas:
A partir do Ruby 2.4.0, você pode usar
RegExp#match?
:Regexp#match?
está explicitamente listado como um aprimoramento de desempenho nas notas de versão do 2.4.0 , pois evita alocações de objetos realizadas por outros métodos, comoRegexp#match
e=~
:fonte
Regexp#match?
é de fato pelo menos 50% mais rápido do que as outras alternativas.Esta é uma referência simples:
Portanto,
=~
é mais rápido, mas depende do que você deseja ter como valor de retorno. Se você deseja apenas verificar se o texto contém um regex ou não use=~
fonte
=~
é mais rápido do quematch
, com um aumento de desempenho menos dramático ao operar em regexps maiores. O que estou pensando é se existe alguma maneira estranha de fazer essa verificação ainda mais rápida, talvez explorando algum método estranho no Regexp ou alguma construção estranha.!("test123" !~ /1/)
?"test123" =~ /1/
/1/.match?("test123")
é mais rápido do"test123" =~ /1/
que apenas verificar se o texto contém um regex ou não.Este é o benchmark que fiz depois de encontrar alguns artigos na rede.
Com 2.4.0 o vencedor é
re.match?(str)
(como sugerido por @ wiktor-stribiżew), nas versões anteriores,re =~ str
parece ser o mais rápido, emborastr =~ re
seja quase tão rápido.Resultados MRI 1.9.3-o551:
Resultados MRI 2.1.5:
Resultados MRI 2.3.3 (há uma regressão na correspondência de regex, ao que parece):
Resultados MRI 2.4.0:
fonte
/a+b/ =~ str
estr =~ /a+b/
. É válido mesmo ao iterá-los por meio de funções e vejo isso válido o suficiente para ser considerado melhor do que armazenar e congelar expressões regulares em uma variável. Testei meu script com ruby 1.9.3p547, ruby 2.0.0p481 e ruby 2.1.4p265. É possível que essas melhorias tenham sido feitas em patches posteriores, mas ainda não tenho planos de testá-lo com versões / patches anteriores.!(re !~ str)
que fosse mais rápido, mas não é.E sobre
re === str
(comparação de caso)?Uma vez que é avaliado como verdadeiro ou falso e não há necessidade de armazenar correspondências, retornar o índice de correspondência e outras coisas, me pergunto se seria uma maneira ainda mais rápida de correspondência do que
=~
.Ok, eu testei isso.
=~
ainda é mais rápido, mesmo se você tiver vários grupos de captura, no entanto, é mais rápido do que as outras opções.BTW, o que é bom
freeze
? Não consegui medir nenhum aumento de desempenho com isso.fonte
freeze
não aparecerão nos resultados porque ocorre antes dos loops de referência e atua no próprio padrão.Dependendo de quão complicada é sua expressão regular, você poderia usar apenas o fatiamento de string simples. Não tenho certeza sobre a praticidade disso para sua aplicação ou se ele realmente ofereceria melhorias de velocidade.
fonte
Os motores Regexp variam em como implementam pesquisas, mas, em geral, ancoram seus padrões para velocidade e evitam correspondências gananciosas, especialmente ao pesquisar strings longas.
A melhor coisa a fazer, até que você esteja familiarizado com o funcionamento de um determinado mecanismo, é fazer benchmarks e adicionar / remover âncoras, tentar limitar as pesquisas, usar curingas vs. correspondências explícitas, etc.
A gema Fruity é muito útil para avaliar rapidamente as coisas, porque é inteligente. O código Benchmark embutido do Ruby também é útil, embora você possa escrever testes que o enganam por não serem cuidadosos.
Usei ambos em muitas respostas aqui no Stack Overflow, então você pode pesquisar minhas respostas e verá muitos pequenos truques e resultados para lhe dar ideias de como escrever código mais rápido.
A coisa mais importante a lembrar é que é ruim otimizar prematuramente seu código antes de saber onde ocorrem as lentidões.
fonte
Para completar as respostas de Wiktor Stribiżew e Dougui, eu diria isso
/regex/.match?("string")
tão rápido quanto"string".match?(/regex/)
.Ruby 2.4.0 (10.000.000 ~ 2 seg)
Ruby 2.6.2 (100.000.000 ~ 20 seg)
Obs: o tempo varia, às vezes
/regex/.match?("string")
é mais rápido e às vezes"string".match?(/regex/)
, as diferenças podem ser apenas devido à atividade da máquina.fonte