Commit bcd9e232e2 for perl

commit bcd9e232e2cabb85c857899fbfdec11d1f28c5b5
Author: Richard Leach <rich+perl@hyphen-dash-hyphen.info>
Date:   Tue Sep 29 13:16:31 2026 +0000

    av_extend_guts: don't needlessly resize a shifted array

    For a workload that is repeatedly pushing and shifting array elements,
    this could cause the array to be reallocated many times over, without
    the peak array size ever actually growing.

    (The example case in https://github.com/Perl/perl5/issues/24865
    showed the array thrashing over the same few chunk allocations.)

    There are no code comments that explain why the check performed was
    for `(key > *maxp - 10)` rather than just `(key > *maxp)`. It
    harks back to Perl 3.0 and perhaps the original rationale no longer
    applies. (Or if it does, the rationale nonetheless seems lost to time.)

diff --git a/av.c b/av.c
index 697cda8203..caa88e5515 100644
--- a/av.c
+++ b/av.c
@@ -119,9 +119,7 @@ Perl_av_extend_guts(pTHX_ AV *av, SSize_t key, SSize_t *maxp, SV ***allocp,

             Move(*arrayp, *allocp, AvFILLp(av)+1, SV*);

-            if (key > *maxp - 10) {
-                newmax = key + *maxp;
-
+            if (key > *maxp) {
                 /* Zero everything above AvFILLp(av), which could be more
                  * elements than have actually been shifted. If we don't
                  * do this, trailing elements at the end of the resized
@@ -131,6 +129,7 @@ Perl_av_extend_guts(pTHX_ AV *av, SSize_t key, SSize_t *maxp, SV ***allocp,
                 goto resize;
             }
         } else if (*allocp) { /* a full SV* array exists */
+          resize:

 #ifdef Perl_safesysmalloc_size
             /* Whilst it would be quite possible to move this logic around
@@ -175,7 +174,6 @@ Perl_av_extend_guts(pTHX_ AV *av, SSize_t key, SSize_t *maxp, SV ***allocp,

             newmax = (key > SSize_t_MAX - newmax)
                         ? SSize_t_MAX : key + newmax;
-          resize:
         {
           /* it should really be newmax+1 here, but if newmax
            * happens to equal SSize_t_MAX, then newmax+1 is
diff --git a/pod/perldelta.pod b/pod/perldelta.pod
index 58c0d5f837..450725232d 100644
--- a/pod/perldelta.pod
+++ b/pod/perldelta.pod
@@ -387,6 +387,20 @@ succesfully inlined.

 =item *

+When a shifted array is unshifted, C<Perl_av_extend_guts> used to perform
+a C<(key > *maxp - 10)> check and always reallocate. That check dates from
+Perl 3.0 and the rationale for it seems to have been lost to time. (It may
+have been to encourage array growth, but the general levers for doing so
+have changed significantly over time.)
+
+Always reallocating when it is not strictly necessary could cause Perl
+applications that repeatedly perform a mixture of C<shift>/C<push> operations
+(or similar) to suffer from unnecessary allocator thrashing.
+C<Perl_av_extend_guts> will now only reallocate if the array really is out
+of space and must be extended.
+
+=item *
+
 XXX

 =back