Created
September 8, 2022 12:51
-
-
Save Deleplace/0544f0681912348666ee48e2d55bc58d to your computer and use it in GitHub Desktop.
Benchmark heavy use of slices.Insert vs. heavy use of a dynamic-array-like insert func
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package insert | |
import ( | |
"fmt" | |
"testing" | |
"golang.org/x/exp/slices" | |
) | |
func BenchmarkGrow(b *testing.B) { | |
for _, M := range []int{ | |
10, | |
1_000, | |
100_000, | |
} { | |
{ | |
name := fmt.Sprintf("insert_%d", M) | |
b.Run(name, func(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
var a []int | |
for j := 0; j < M; j++ { | |
a = slices.Insert(a, 0, 0) | |
} | |
Sink = a | |
} | |
}) | |
} | |
{ | |
name := fmt.Sprintf("insertDynamic_%d", M) | |
b.Run(name, func(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
var a []int | |
for j := 0; j < M; j++ { | |
a = insertDynamic(a, 0, 0) | |
} | |
Sink = a | |
} | |
}) | |
} | |
} | |
} | |
// insertDynamic is like slices.Insert, except it allocates much less | |
// frequently, by allocating 25% extra memory whenever it needs to grow s. | |
func insertDynamic[S ~[]E, E any](s S, i int, v ...E) S { | |
tot := len(s) + len(v) | |
if tot <= cap(s) { | |
s2 := s[:tot] | |
copy(s2[i+len(v):], s[i:]) | |
copy(s2[i:], v) | |
return s2 | |
} | |
return append(v, s...) | |
} | |
// Prevent optimizing things away | |
var Sink []int |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Results on my workstation:
Both funcs are O(M^2), when inserting M elements one by one in a loop. However,
insertDynamic
tends to be 3x as fast because it allocates less often, and memory allocation tends to dominate the cost ofslices.Insert
.