redigo icon indicating copy to clipboard operation
redigo copied to clipboard

use zero copy improve string and byte convert

Open imxyb opened this issue 4 years ago • 2 comments

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%

imxyb avatar Oct 13 '20 07:10 imxyb

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.

stevenh avatar Jul 01 '22 15:07 stevenh

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)
	})
}

stevenh avatar Feb 03 '24 17:02 stevenh