diff --git a/src/slices/slices.go b/src/slices/slices.go index ea1dea573c9..4a35ec5c23f 100644 --- a/src/slices/slices.go +++ b/src/slices/slices.go @@ -88,7 +88,11 @@ func Insert[S ~[]E, E any](s S, i int, v ...E) S { copy(s2[i:], v) return s2 } - s2 := make(S, tot) + // Use append rather than make so that we bump the size of + // the slice up to the next storage class. + // This is what Grow does but we don't call Grow because + // that might copy the values twice. + s2 := append(S(nil), make(S, tot)...) copy(s2, s[:i]) copy(s2[i:], v) copy(s2[i+len(v):], s[i:]) diff --git a/src/slices/slices_test.go b/src/slices/slices_test.go index 720e731ddf2..0f3df43e061 100644 --- a/src/slices/slices_test.go +++ b/src/slices/slices_test.go @@ -256,6 +256,20 @@ func TestInsert(t *testing.T) { t.Errorf("Insert(%v, %d, %v...) = %v, want %v", test.s, test.i, test.add, got, test.want) } } + + if !testenv.OptimizationOff() && !race.Enabled { + // Allocations should be amortized. + const count = 50 + n := testing.AllocsPerRun(10, func() { + s := []int{1, 2, 3} + for i := 0; i < count; i++ { + s = Insert(s, 0, 1) + } + }) + if n > count/2 { + t.Errorf("too many allocations inserting %d elements: got %v, want less than %d", count, n, count/2) + } + } } var deleteTests = []struct {