diff --git a/pod/perldelta.pod b/pod/perldelta.pod index 37f2fd23163f..4e87d9d6228b 100644 --- a/pod/perldelta.pod +++ b/pod/perldelta.pod @@ -348,6 +348,19 @@ well. =item * +C, which undoes the OOK string offset mechanism now only +moves the string buffer contents to the start of the buffer if `SvOK(sv)` +is true. Historically, the buffer contents were always moved, but if the +buffer contents are defunct and no longer required, doing that incurs an +unnecessary cost proportional to the size of the shifted contents. + +(The assumption in this change is that `SvOK(sv)` is a valid indicator of +whether the string buffer contents are "live" or not.) + +See GH#23967 for an example of where such a copy was noticeable. + +=item * + XXX =back diff --git a/scope.c b/scope.c index b8063c27760b..dddd1018bf7c 100644 --- a/scope.c +++ b/scope.c @@ -1440,8 +1440,20 @@ Perl_leave_scope(pTHX_ I32 base) if (SvTYPE(sv) == SVt_PVHV && HvHasAUX(sv)) Perl_hv_kill_backrefs(aTHX_ MUTABLE_HV(sv)); - else if(SvOOK(sv)) - sv_backoff(sv); + else if(SvOOK(sv)) { + /* Inlined sv_backoff() - the buffer contents are + * defunct and there's no need to copy them. All that + * is needed is resetting SvLEN and the SvPVX pointer. */ + assert(SvTYPE(sv) != SVt_PVHV); /* the branch above */ + assert(SvTYPE(sv) != SVt_PVAV); + + STRLEN delta; + SvOOK_offset(sv, delta); + + SvLEN_set(sv, SvLEN(sv) + delta); + SvPV_set(sv, SvPVX(sv) - delta); + SvFLAGS(sv) &= ~SVf_OOK; + } if (SvMAGICAL(sv)) { /* note that backrefs (either in HvAUX or magic) diff --git a/sv.c b/sv.c index 146e7ac61d8b..4e161322646f 100644 --- a/sv.c +++ b/sv.c @@ -1336,6 +1336,8 @@ wrapper instead. /* prior to 5.000 stable, this function returned the new OOK-less SvFLAGS prior to 5.23.4 this function always returned 0 + prior to 5.43.x, the contents of the string buffer were always copied + down to the start of the buffer. Now, this only happens if SvOK(sv) */ void @@ -1355,7 +1357,11 @@ Perl_sv_backoff(SV *const sv) SvLEN_set(sv, SvLEN(sv) + delta); SvPV_set(sv, SvPVX(sv) - delta); SvFLAGS(sv) &= ~SVf_OOK; - Move(s, SvPVX(sv), SvCUR(sv)+1, char); + + /* Don't copy a buffer if the contents are already defunct. */ + if (SvOK(sv)) + Move(s, SvPVX(sv), SvCUR(sv)+1, char); + return; }