Closed chriso closed 1 year ago
It's been 5 versions of Go since we introduced this optimization and it's never been invalidated by the runtime. Maybe we would spend less engineering time if we were to remove the build tag and address if the build ever breaks in a future Go version?
I think it's still better to opt-in to the unsafe code.
If the internals do change, and we don't remember to check whether it still works each time a new version is released, do we risk exposing users to segfaults?
//go:linkname unsafe_NewArray reflect.unsafe_NewArray
func unsafe_NewArray(rtype unsafe.Pointer, length int) unsafe.Pointer
//go:linkname typedslicecopy reflect.typedslicecopy
//go:noescape
func typedslicecopy(elemType unsafe.Pointer, dst, src slice) int
func extendSlice(t reflect.Type, s *slice, n int) slice {
elemTypeRef := t.Elem()
elemTypePtr := ((*iface)(unsafe.Pointer(&elemTypeRef))).ptr
d := slice{
data: unsafe_NewArray(elemTypePtr, n),
len: s.len,
cap: n,
}
typedslicecopy(elemTypePtr, d, *s)
return d
}
On the other hand, if we forget to (re)enable it when a new version is released, our code only gets ~1.5% slower.
This PR enables the
extendSlice
optimization for go1.18 and go1.19, where it's still valid. The optimization will be disabled again in go1.20 in case internals change.I get the following improvement for
go test ./json -run=^$ -bench=CodeUnmarshal$ -benchtime=2000x -count 10
on arm64:The optimization was previously enabled for go1.17 in https://github.com/segmentio/encoding/pull/96.