http://qs321.pair.com?node_id=236537

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm doing some log parsing that will be using the same regular expressions hundreds of thousands of times over in a single session. I vaguely remember something about being able to compile regular expressions, though googling with thepen mirror didn't turn me up with much.

Help please?
Thanks

Replies are listed 'Best First'.
Re: Compiling Regular Expressions
by diotalevi (Canon) on Feb 19, 2003 at 05:40 UTC

    You've got two considerations - normal regexes which do not use interpolation are compiled exactly once without you doing anything special on your end. Zaxo's reference to the qr operator applies when you interpolate variables but only need to do that once or occassionally. And then from there its even slower to interpolate qr compiled expressions into other expressions (only because you're compiling more often than necessary).


    Seeking Green geeks in Minnesota

Re: Compiling Regular Expressions
by integral (Hermit) on Feb 19, 2003 at 06:07 UTC
    Zaxo has mentioned the qr operator, and diotalevi has said that regexps will be compiled and then cached by perl so that they are only compiled once. In addition to this there is the /o modifier (see perlop), which tells perl that the pattern should be compiled only once even if there is an interpolation in the pattern.

    --
    integral, resident of freenode's #perl
    

      /o is exactly like working with the qr operator except less flexible. I'm not convinced there are any real reasons to keep /o except for backwards compatibility. In any case, its not good to recommend this for new code - either use qr or normal regexes.


      Seeking Green geeks in Minnesota

        /o can still be useful from time to time. Firstly, you occasionally expect that the interpolated variable may change but still want to use the original value (but this is rare). Secondly, the "recompile only if changed" logic requires checking whether the pattern has changed, which takes time:

        perl -MBenchmark -w ($s, $t) = qw/ foo xfoox /; timethese(-1, { oful => q{ $t =~ /$s/o }, oless => q{ $t =~ /$s/ }, }) __END__ Benchmark: running oful, oless for at least 1 CPU seconds... oful: 2 wallclock secs ( 1.08 usr + 0.00 sys = 1.08 CPU) @ +2427258.33/s (n=2621439) oless: 2 wallclock secs ( 1.15 usr + 0.00 sys = 1.15 CPU) @ +1841144.35/s (n=2117316)

        Hugo
Re: Compiling Regular Expressions
by Zaxo (Archbishop) on Feb 19, 2003 at 05:33 UTC

    You want the qr quote-like operator.

    After Compline,
    Zaxo

Re: Compiling Regular Expressions
by hv (Prior) on Feb 19, 2003 at 13:38 UTC

    When perl compiles your program, any constant regular expressions in the code (ie any that don't interpolate any variables) are compiled. Any that do interpolate variables will be compiled once when they are first used, and on further uses are re-compiled only if they have changed since the last use.

    So in this code, the regular expression is compiled only once (at compile time):

    for (@words) { print if /foo/; }

    And also in this code (but at runtime):

    my $s = "foo"; for (@words) { print if /$s/; }

    In this code, the two regular expressions are each compiled only once:

    my($s1, $s2) = qw/ foo bar /; for (@words) { print if /$s1/; print if /$s2/; }

    But in this code, because the one regular expression is being used alternately interpolated with "foo" and "bar", it will be recompiled each time:

    my @s = qw/ foo bar /; for (@words) { for my $s (@s) { print if /$s/; } }

    You can get information about what perl is doing with your regexps using the -Dr switch if perl has been compiled for debugging, or with use re 'debug';. This produces a lot of scary-looking information about both compiling and running regular expressions, but if you ignore that and just look at the "Compiling ..." lines it will show you what is happening.

    The last case above is where qr{} comes in: if we fill @s with qr{}'d expressions instead, we can avoid the recompile:

    my @s = map qr{$_}, qw/ foo bar /; for (@words) { for my $s (@s) { print if /$s/; } }

    Note that this only helps if you are using exactly the compiled regexp: if you interpolate it into something more, even if only to add an anchor, it will go back to recompiling each time.

    Hugo

      I want to embed Perl code in my regex using (?{...}), so it's critical that the regex is compiled at compile time. I've gotten this to work without errors or warnings:

      my $kind; my $REGEX = qr/ [A-Za-z][\w]* (?{$kind = 'IDENT';}) | (?: ==? | != | <=? | >=? ) (?{$kind = 'OP';}) | -?\d+ (?{$kind = 'INT';}) | \x27 ( (?:[^\x27] | \x27{2})* ) \x27 (?{$kind = 'STRING';}) | \S (?{$kind = 'OTHER';}) /xs;

      However, I'd like to better organize the regex by splitting it into parts, then using those parts. This is what I'd like to do, but when I try, I get the error "Eval-group not allowed at runtime, use re 'eval' in regex". I think that the suggested workaround is bad practice, so I won't do it, but I don't understand why what I'm trying to do won't work since all the regex's involved use qr//. I've also tried using Readonly for the parts, but Perl still doesn't recognize that the parts I'm using will never change. Is there any other way to get $REGEX to compile at compile time while breaking it into parts?

      my $IDENT = qr/ [A-Za-z][\w]* /xs; my $STRING = qr/ \x27 ( (?:[^\x27] | \x27{2})* ) \x27 /xs; my $OP = qr/ (?: ==? | != | <=? | >=? ) /xs; my $INT = qr/ -?\d+ /xs; my $kind; my $REGEX = qr/ $IDENT (?{$kind = 'IDENT';}) | $OP (?{$kind = 'OP';}) | $INT (?{$kind = 'INT';}) | $STRING (?{$kind = 'STRING';}) | \S (?{$kind = 'OTHER';}) /xs;

        You must be using a version of perl that is less than v5.18, as this behaviour works for me on 5.18+, and is documented as such in v5.18 perldelta... search for "The use re 'eval' pragma".

Re: Compiling Regular Expressions
by Cody Pendant (Prior) on Feb 19, 2003 at 06:42 UTC
    This is kind of the opposite of what the poster is asking for, but what does the function study() actually do?

    I say it's the opposite, because the FAQ says it takes more time, and that it works on the scalar to be matched rather than the regex, but I might as well ask.

    In what circumstances would I study() a string before hitting it with a regex?
    --

    “Every bit of code is either naturally related to the problem at hand, or else it's an accidental side effect of the fact that you happened to solve the problem using a digital computer.”
    M-J D

      Cody Pendant,

      A few comments... (Monks, correct me if I'm right)

      You are correct that study is the opposite of what the OP wants, but only in your second point, in that it works on the scalar to be matched, and not on the regex itself. I have a feeling that your first point - that it 'takes more time' - is a mis-interpretation of the documentation: study takes extra time up-front to study the scalar in question, if you want to do lots of different regex's on it. Depending on your scalar and your regex's, peforming the study may save time on the regex's because you've already analyzed the heck out of that scalar.

      A possible scenario when this is useful: Let's say your scalar is a paragraph of text. You plan to run all sorts of matches on this text, to get some sort of statistics on it ('foo' was mentioned twice, 'bar' mentioned once, in the word 'barfly', 'um' appeared 3 times, always followed by '...', etc.). Thus you want to study() the same paragraph, to (possibly) save time on all those different regex's you're going to throw at it...

      This is the opposite of what the OP wants, since s/he wants to use the same regex on many different scalars... In this case, I think the previous comments are right on - the regex will be compiled only once if it contains no variable interpolation (i.e. /pattern/), so there's no cause for concern, and using the '/o' operator will force the pattern to be compiled only once if it is in a variable (i.e. /$pattern/o), with the caveat that you cannot change the value of $pattern (well, of course you can, but perl won't notice, and will continue to perform the regex with the value of $pattern when the '/o' was used...)

      Hope this helps (and is accurate)...

        I think your interpretation is correct, though when I've benchmarked it, now and previously, I have had variable results as you'll see below.

        It seems to consistantly slow the regex down (even excluding the study time), if you only match against a single regex.

        There can be some considerable speed up when matching against multiple regexes, but it's not consistant in how much you get. In the example below, matching against 2 regexes on a study'd string sometimes shows upto 50% improvement relative to an unstudy'd one, but less so when matching 3 regexes against the same two strings. It seems to be a function of the constant content of the regexs. Ie. what characters are involved. Those containing rare characters showing greater benefit than those not, but that's really educated speculation.

        That said, there always seems to be some benefit to studying the string if you intend to match against more than once with 2 or more different regexes.

        There doesn't seem to be any benefit (actually normally a small penalty) for multiple matches with a single regex, as with the /g modifier.

        perl> use Benchmark qw[cmpthese]; perl> $s = "I say it's the opposite, because the FAQ says it takes mo +re time, and that it works on the scalar to be matched rather than the regex, but I might as well ask. In what circumstances would I + study() a string before hitting it with a regex?" perl> $t = $s perl> study $s perl> cmpthese( -1, { studied=>'$_=()=$s=~m[FAQ]o', slacker=>'$_=()=$ +t=~m[FAQ]o' } ) Benchmark: running slacker, studied, each for at least 1 CPU seconds. +.. slacker: 1 wallclock secs ( 1.01 usr + 0.00 sys = 1.01 CPU) @ 56 +776.24/s (n=57344) studied: 2 wallclock secs ( 1.12 usr + 0.00 sys = 1.12 CPU) @ 54 +807.31/s (n=61439) Rate studied slacker studied 54807/s -- -3% slacker 56776/s 4% -- perl> cmpthese( -1,{\ studied=>'$a = ($s = ~m[FAQ]o && $s =~ m[regex]o)',\ slacker=>'$b = ($t =~ m[FAQ]o && $t =~ m[regex]o)',\ }) Benchmark: running slacker, studied, each for at least 1 CPU seconds. +.. slacker: 2 wallclock secs ( 1.08 usr + 0.00 sys = 1.08 CPU) @ 98 +754.16/s (n=106852) studied: 2 wallclock secs ( 1.20 usr + 0.00 sys = 1.20 CPU) @ 65 +054.91/s (n=78196) Rate studied slacker studied 65055/s -- -34% slacker 98754/s 52% -- perl> cmpthese( -1,{\ studied=>'$a = ($s =~ m[FAQ]o && $s =~ m[regex]o) && $s =~ m[circumsta +nces]o',\ slacker=>'$b = ($t =~ m[FAQ]o && $t =~ m[regex]o) && $t =~ m[circumsta +nces]o',\ }) Benchmark: running slacker, studied, each for at least 1 CPU seconds. +.. slacker: 2 wallclock secs ( 1.00 usr + 0.00 sys = 1.00 CPU) @ 71 +679.00/s (n=71679) studied: 2 wallclock secs ( 1.17 usr + 0.00 sys = 1.17 CPU) @ 56 +503.84/s (n=66166) Rate studied slacker studied 56504/s -- -21% slacker 71679/s 27% -- perl> cmpthese( -10,{\ studied=>'$a = ($s =~ m[FAQ]o && $s =~ m[regex]o)',\ slacker=>'$b = ($t =~ m[FAQ]o && $t =~ m[regex]o)',\ }) Benchmark: running slacker, studied, each for at least 10 CPU second +s... slacker: 10 wallclock secs (11.21 usr + 0.01 sys = 11.22 CPU) @ 1 +11296.69/s (n=1248415) studied: 11 wallclock secs (10.00 usr + 0.00 sys = 10.00 CPU) @ 1 +41637.98/s (n=1417088) Rate slacker studied slacker 111297/s -- -21% studied 141638/s 27% --

        Examine what is said, not who speaks.

        The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Re: Compiling Regular Expressions
by edan (Curate) on Feb 19, 2003 at 15:01 UTC

    I think meaning of /o in regexes is a good place for further reading - I came across it after my previous post.

    It looks like qr// is preferred over /o (according to tye over there) - much of my perl knowledge is from 5.004, and I don't think qr// was available then...

    sigh... I have a lot of catching-up to do. I guess that's a Good Thing.

    Now you'll all excuse me while I read up (in perlop under 'Quote and Quote-like Operators') on qr// ...