Re: [Perl/perl5] 502da7: make /p a no-op
[email protected] (demerphq) Thu, 12 Feb 2026 17:34:40 +0100
| Newsgroups | perl.perl5.porters |
|---|---|
| Message-ID | <CANgJU+WiKYtfuRLc8UefH1vZ836KXuqJwKSMYTE4sSbC28Xd=Q@mail.gmail.com> |
On Thu, 12 Feb 2026 at 16:36, Philippe Bruhat (BooK) <[email protected]> wrote: > Hi, > Hey man! Hope you are good. > On Thu, Feb 12, 2026 at 06:00:35PM +0800, demerphq wrote: > > On my phone so forgive the double reply. The code i mean is something > like > > this: > > > > while (<>) { > > while (/(.)/g) { $count++} > > } > > > > Try that on a large file. If it appears to hang then /p should not be > > removed. > > Thanks Yves for bringing up this issue. > > I tried this (also reported on the GitHub ticket): > > $ time perl -E 'say $^V;while(<>){while(/(.)/g){$count++}}say$count' > /tmp/big > v5.36.0 > 101876091 > > real 0m16.232s > user 0m16.208s > sys 0m0.020s > > # this is the Perl built from https://github.com/Perl/perl5/pull/24191 > > $ time ./perl -Ilib -E 'say > $^V;while(<>){while(/(.)/g){$count++}}say$count' /tmp/big > v5.43.8 > 101876091 > > real 0m48.173s > user 0m48.110s > sys 0m0.060s > > So there's definitely a degradation > Did you double check you were using the same build options? I did something similar and ended up comparing a DEBUGGING and release build. My other post about COW and $_ and /p modifier is related to this. I havent had time yet to dig more deeply to see what the story is. > > > Every time people proposed removing /p when I have checked while /g it > > > has been broken. > > I'm a little bit confused about the whole $& vs ${^MATCH} situation. > The docs keep saying there was horrible degradation before 5.20, but > that everything is fixed since 5.20 and COW. And still things don't > appear as simple. > As always this is a bit of a twisty set of circumstances... And involves/involved multiple bugs/issues/design flaws. Let me just summarize the situation and history a bit as I understand it just for the record. So, the core problem is that when we match and have capture buffers in the pattern we lazily populate the buffers based on offsets into the matched string, and is $1 is a magic variable which behaves like a tied scalar which reads the appropriate segment of the matched string. This means we have to manage the string being updated in between the match and the use of $1 etc. In order to manage this in most cases when there is a capture buffer we copy the string before the match, and store the copy in the current match context object. Similarly the use of $& *anywhere in the running perl context* would force this copy behavior for all matches, even when the pattern did not contain capture buffers. (PL_sawampersand). This meant that use English was avoided by most people because it mapped $MATCH to $&. (The same rules applied to $` and $'.) At some point we changed use English to only support $& on request and it became a little more commonly used, but was still generally avoided by CPAN code that needed to work on older perl with the older use English implementation. This had to be a global var because it is hard (maybe Turing Complete hard, but definitely harder than was reasonable for the perl internals) to determine which match a $& is going to access. There was one place where this copying was disabled (i'm not sure if it was always disabled, or just when PL_sawampersand was not used), and that is for scalar context /g matches, and this was because we don't want while (/(.)/g) { } to be quadratic. It should be linear. But this meant that if you messed with the target of the pattern, you could end up messing up capture buffers. So in older perls code like this: $str = "abcdef"; $str =~ /(\w+)/g; $str = "123456"; print $1; will output "123456" and not "abcdef" and if you go far enough back, if the modified string was shorter than the original you could even trigger a SEGV. At some point I added the support for /p and created ${^PREMATCH}, ${^MATCH} and ${^POSTMATCH}. The idea of this was that these vars are only populated if the /p modifier was used on a match. So if you really wanted to use $& without paying a global performance penalty you could use ${^MATCH} instead, and it was up to you make sure that when you did was after a successful match which used /p. Even later on COW was added. COW in theory is a solution to the quadratic problem, as it makes copying the string in the match relatively efficient. (O(1) performance for the copy instead of O(length_of_string). So then support for /p was removed from various places. Unfortunately the COW stuff didn't work very well with readline(). We do some gnarly speed optimizations to make readline fast, which disabled COW as well. So the two changes cancelled each other out and scalar /g matches were again quadratic. We eventually fixed this somewhat by changing how readline works. (I fixed it, but i dont rememebr exactly how, i wrote a huge comment about it at the time if you care to read it. However, this fix wasnt a complete fix. It didn't fix the performance problem of scalar /g matches, it just avoided the quadratic behavior by making sure readline returned COWable strings. The old quadratic behavior still existed and could be produced by using a non COW-able string, which are rare and but occasionally encountered beasts in various contexts. So I essentially reverted the NO-OP of the /p modifier. I guess when I did it I missed some docs so it continued to be documented as a no-op even though it was not. > > Is there a difference between the punctuation variables ($`, $& and $') > and the long name versions (${^PREMATCH}, ${^MATCH} and ${^POSTMATCH})? > What is it? And what is the relation with PL_sawampersand, which has > apparently been disabled since 1a904fc88069e249a4bd0ef196a3f1a7f549e0fe > in 2012? > I thought I reverted the removal. Maybe i only did a partial revert which didnt affect $& directly but did affect scalar /g matches. > Should we open an issue to completely remove the code related to > PL_sawampersand? > It depends. I dont think we should until we eliminate the quadratic vector or alternatively as I posted earlier, when we try to make the copy if the string is not COWable we can warn. If we warn when people are doing scalar /g matches on an unCOWable string at least the user will know and have some guidance on how to avoid the problem: typically simply copying the value into a "normal" sv before the scalar /g match will do the trick. > > I've tried these in the hope of understanding the relationship > between the puncation variables and the long name ones: > > # Using $& doesn't seem to make a difference > > $ time perl -E 'say > $^V;while(<>){while(/(.)/g){$&;$count++}}say$count' /tmp/big > v5.36.0 > 101876091 > > real 0m15.719s > user 0m15.705s > sys 0m0.013s > > # Using /p and ${^MATCH} doesn't seem to make a difference either > > $ time perl -E 'say > $^V;while(<>){while(/(.)/gp){${^MATCH};$count++}}say$count' /tmp/big > v5.36.0 > 101876091 > > real 0m15.739s > user 0m15.723s > sys 0m0.017s > > (Both of those suck like the first example with my patch bleadperl, > of course.) > > In my limited understanding, the change I made (removing a conditional > goto) was equivalent to having /p always enabled. That doesn't seem to > be the actual result. > > I'm clearly out of my depth here. > > I started looking at this, because I believe that for PPC0014 (English > name aliases) to be complete, we want the caret long name match variables > to be simple aliases to the punctuation ones. Which I think means /p > must be a true no-op. > I dont see why resolving the other vars should be blocked by these particular vars. They have always been special, even with 'use English' in operation. Having said that, these days probably the best is to warn when we match against an unCOWable string, and then let the user deal with it. Yves -- perl -Mre=debug -e "/just|another|perl|hacker/"