redigo
redigo copied to clipboard
use zero copy improve string and byte convert
bench result:
BenchmarkIntForByte-12 34.3 19.8 -42.27%
benchmark old allocs new allocs delta
BenchmarkIntForByte-12 1 0 -100.00%
benchmark old bytes new bytes delta
BenchmarkIntForByte-12 3 0 -100.00%
benchmark old ns/op new ns/op delta
BenchmarkString-12 14.9 2.55 -82.89%
benchmark old allocs new allocs delta
BenchmarkString-12 1 0 -100.00%
benchmark old bytes new bytes delta
BenchmarkString-12 4 0 -100.00%
Looks like the proposal https://github.com/golang/go/issues/53003 has been accepted recently so we can look to use those when they are implemented.
I believe the compiler in go 1.21 is doing this optimisation by default as there no difference between string(reply)
and unsafe.String(unsafe.SliceData(reply), len(reply))
when running the following benchmark.
func BenchmarkReplyHelpers(b *testing.B) {
c, err := dial()
require.NoError(b, err)
defer c.Close()
_, err = c.Do("SET", "k1", "1")
require.NoError(b, err)
b.Run("String", func(b *testing.B) {
reply, err := c.Do("GET", "k1")
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = redis.String(reply, err)
}
b.StopTimer()
// Outside the loop, so the compiler cannot optimize the function call away.
require.NoError(b, err)
})
b.Run("Int", func(b *testing.B) {
reply, err := c.Do("GET", "k1")
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = redis.String(reply, err)
}
b.StopTimer()
// Outside the loop, so the compiler cannot optimize the function call away.
require.NoError(b, err)
})
b.Run("Int64", func(b *testing.B) {
reply, err := c.Do("GET", "k1")
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = redis.Int64(reply, err)
}
b.StopTimer()
// Outside the loop, so the compiler cannot optimize the function call away.
require.NoError(b, err)
})
b.Run("Uint64", func(b *testing.B) {
reply, err := c.Do("GET", "k1")
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = redis.Uint64(reply, err)
}
b.StopTimer()
// Outside the loop, so the compiler cannot optimize the function call away.
require.NoError(b, err)
})
b.Run("Float64", func(b *testing.B) {
reply, err := c.Do("GET", "k1")
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = redis.Float64(reply, err)
}
b.StopTimer()
// Outside the loop, so the compiler cannot optimize the function call away.
require.NoError(b, err)
})
b.Run("Bool", func(b *testing.B) {
reply, err := c.Do("GET", "k1")
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = redis.Bool(reply, err)
}
b.StopTimer()
// Outside the loop, so the compiler cannot optimize the function call away.
require.NoError(b, err)
})
}