My new post was inspired by the latest go quiz. Pay attention to benchmark [1]:
func BenchmarkSortStrings(b *testing.B) {
s := []string{"heart", "lungs", "brain", "kidneys", "pancreas"}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
sort.Strings(s)
}
}
As a convenient wrapper around sort.Sort(sort.StringSlice(s))
, it sort.Strings
modifies the data passed to it, sorting it, so that not everyone (at least, at least 43% of twitter subscribers) could assume that this would lead to allocations [allocations on the heap]. However, at least in recent versions of Go, this is the case and each iteration of this benchmark will cause one allocation. But why?
As many Go developers should know, interfaces are implemented as a two (machine) word structure . Each interface value contains two fields: one contains the type of the value stored by the interface, and the other contains a pointer to that value. [2]
In pseudocode, it would look like this:
type interface struct { // , type uintptr // () , data uintptr }
interface.data
, 8 . , , []string
24 : , ; ; (capacity). Go 24 8? , . , []string
24 , *[]string
β 8.
[Escaping]
, sort.Strings
:
func BenchmarkSortStrings(b *testing.B) { s := []string{"heart", "lungs", "brain", "kidneys", "pancreas"} b.ReportAllocs() for i := 0; i < b.N; i++ { var ss sort.StringSlice = s var si sort.Interface = ss // allocation sort.Sort(si) } }
, var si sort.Interface = ss
, var si sort.Interface = &ss
, ss
[3]. , ss
, ? ss
?
, ss
[heap], .
Total: 296.01MB 296.01MB (flat, cum) 99.66% 8 . . func BenchmarkSortStrings(b *testing.B) { 9 . . s := []string{"heart", "lungs", "brain", "kidneys", "pancreas"} 10 . . b.ReportAllocs() 11 . . for i := 0; i < b.N; i++ { 12 . . var ss sort.StringSlice = s 13 296.01MB 296.01MB var si sort.Interface = ss // allocation 14 . . sort.Sort(si) 15 . . } 16 . . }
, , ss
si
(, , , - ). , ss
. , : ? , .
% go test -bench=. sort_test.go goos: darwin goarch: amd64 cpu: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz BenchmarkSortStrings-4 12591951 91.36 ns/op 24 B/op 1 allocs/op PASS ok command-line-arguments 1.260s
Go 1.16beta1, amd64, 24 [4].
Go 32 .
% go1.15 test -bench=. sort_test.go goos: darwin goarch: amd64 BenchmarkSortStrings-4 11453016 96.4 ns/op 32 B/op 1 allocs/op PASS ok command-line-arguments 1.225s
: Go. , , [size classes].
, , , [] Go 24 . β , . , 24 , 24, . , 24 , , . , , .
, Go 24 , , β , . , . , . ? , .
, , , "", . , 24 , . ? , β [5].
24 , 8 , . 25% "" β , , . ? , 9 , ! - ?
, , . 24- , , , . , β , , - 24- . Go , ( , , , C++). , .
,
, : ? : . , (, ), . .
[6], ( ) . , [7].
, , 9 . , , , 9- . () , , 4. , β . 9 12 . , 3 β , , .
. Go 1.15 24 , ss
32 . Martin MΓΆhrmann Go 1.16 24 , .
[1] , . .
[2] Go. , Go 1.15 . -, .
[3] , sort.StringSlice
, *sort.StringSlice
.
[4] 32 , .
[5] 4G (, , 64 ), , [aligment] [padding] ( , , β . ).
[6] β .
[7] , , .