diff --git a/.travis.yml b/.travis.yml index 70d6eb47..a476a44d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ install: - go get gopkg.in/bsm/ratelimit.v1 - go get github.com/onsi/ginkgo - go get github.com/onsi/gomega + - go get github.com/garyburd/redigo/redis - mkdir -p $HOME/gopath/src/gopkg.in - mv $HOME/gopath/src/github.com/go-redis/redis $HOME/gopath/src/gopkg.in/redis.v3 - cd $HOME/gopath/src/gopkg.in/redis.v3 diff --git a/README.md b/README.md index 13887132..bd9edfc9 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,27 @@ Some corner cases: EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, []string{"hello"}).Result() +## Benchmark + +``` +BenchmarkSetGoRedis10Conns64Bytes-4 200000 7184 ns/op 210 B/op 6 allocs/op +BenchmarkSetGoRedis100Conns64Bytes-4 200000 7174 ns/op 210 B/op 6 allocs/op +BenchmarkSetGoRedis10Conns1KB-4 200000 7341 ns/op 210 B/op 6 allocs/op +BenchmarkSetGoRedis100Conns1KB-4 200000 7425 ns/op 210 B/op 6 allocs/op +BenchmarkSetGoRedis10Conns10KB-4 200000 9480 ns/op 210 B/op 6 allocs/op +BenchmarkSetGoRedis100Conns10KB-4 200000 9301 ns/op 210 B/op 6 allocs/op +BenchmarkSetGoRedis10Conns1MB-4 2000 590321 ns/op 2337 B/op 6 allocs/op +BenchmarkSetGoRedis100Conns1MB-4 2000 588935 ns/op 2337 B/op 6 allocs/op +BenchmarkSetRedigo10Conns64Bytes-4 200000 7238 ns/op 208 B/op 7 allocs/op +BenchmarkSetRedigo100Conns64Bytes-4 200000 7435 ns/op 208 B/op 7 allocs/op +BenchmarkSetRedigo10Conns1KB-4 200000 7635 ns/op 208 B/op 7 allocs/op +BenchmarkSetRedigo100Conns1KB-4 200000 7597 ns/op 208 B/op 7 allocs/op +BenchmarkSetRedigo10Conns10KB-4 100000 17126 ns/op 208 B/op 7 allocs/op +BenchmarkSetRedigo100Conns10KB-4 100000 17030 ns/op 208 B/op 7 allocs/op +BenchmarkSetRedigo10Conns1MB-4 2000 675397 ns/op 226 B/op 7 allocs/op +BenchmarkSetRedigo100Conns1MB-4 2000 669053 ns/op 226 B/op 7 allocs/op +``` + ## Shameless plug Check my [PostgreSQL client for Go](https://github.com/go-pg/pg). diff --git a/bench_test.go b/bench_test.go new file mode 100644 index 00000000..dfd2bf3e --- /dev/null +++ b/bench_test.go @@ -0,0 +1,296 @@ +package redis_test + +import ( + "bytes" + "testing" + "time" + + redigo "github.com/garyburd/redigo/redis" + + "gopkg.in/redis.v3" +) + +func benchmarkRedisClient(poolSize int) *redis.Client { + client := redis.NewClient(&redis.Options{ + Addr: ":6379", + DialTimeout: time.Second, + ReadTimeout: time.Second, + WriteTimeout: time.Second, + PoolSize: poolSize, + }) + if err := client.FlushDb().Err(); err != nil { + panic(err) + } + return client +} + +func BenchmarkRedisPing(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.Ping().Err(); err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkRedisSet(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + value := string(bytes.Repeat([]byte{'1'}, 10000)) + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.Set("key", value, 0).Err(); err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkRedisGetNil(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.Get("key").Err(); err != redis.Nil { + b.Fatal(err) + } + } + }) +} + +func benchmarkSetGoRedis(b *testing.B, poolSize, payloadSize int) { + client := benchmarkRedisClient(poolSize) + defer client.Close() + + value := string(bytes.Repeat([]byte{'1'}, payloadSize)) + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.Set("key", value, 0).Err(); err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkSetGoRedis10Conns64Bytes(b *testing.B) { + benchmarkSetGoRedis(b, 10, 64) +} + +func BenchmarkSetGoRedis100Conns64Bytes(b *testing.B) { + benchmarkSetGoRedis(b, 100, 64) +} + +func BenchmarkSetGoRedis10Conns1KB(b *testing.B) { + benchmarkSetGoRedis(b, 10, 1024) +} + +func BenchmarkSetGoRedis100Conns1KB(b *testing.B) { + benchmarkSetGoRedis(b, 100, 1024) +} + +func BenchmarkSetGoRedis10Conns10KB(b *testing.B) { + benchmarkSetGoRedis(b, 10, 10*1024) +} + +func BenchmarkSetGoRedis100Conns10KB(b *testing.B) { + benchmarkSetGoRedis(b, 100, 10*1024) +} + +func BenchmarkSetGoRedis10Conns1MB(b *testing.B) { + benchmarkSetGoRedis(b, 10, 1024*1024) +} + +func BenchmarkSetGoRedis100Conns1MB(b *testing.B) { + benchmarkSetGoRedis(b, 100, 1024*1024) +} + +func benchmarkSetRedigo(b *testing.B, poolSize, payloadSize int) { + pool := &redigo.Pool{ + Dial: func() (redigo.Conn, error) { + return redigo.DialTimeout("tcp", ":6379", time.Second, time.Second, time.Second) + }, + MaxActive: poolSize, + MaxIdle: poolSize, + } + defer pool.Close() + + value := string(bytes.Repeat([]byte{'1'}, payloadSize)) + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + conn := pool.Get() + if _, err := conn.Do("SET", "key", value); err != nil { + b.Fatal(err) + } + conn.Close() + } + }) +} + +func BenchmarkSetRedigo10Conns64Bytes(b *testing.B) { + benchmarkSetRedigo(b, 10, 64) +} + +func BenchmarkSetRedigo100Conns64Bytes(b *testing.B) { + benchmarkSetRedigo(b, 100, 64) +} + +func BenchmarkSetRedigo10Conns1KB(b *testing.B) { + benchmarkSetRedigo(b, 10, 1024) +} + +func BenchmarkSetRedigo100Conns1KB(b *testing.B) { + benchmarkSetRedigo(b, 100, 1024) +} + +func BenchmarkSetRedigo10Conns10KB(b *testing.B) { + benchmarkSetRedigo(b, 10, 10*1024) +} + +func BenchmarkSetRedigo100Conns10KB(b *testing.B) { + benchmarkSetRedigo(b, 100, 10*1024) +} + +func BenchmarkSetRedigo10Conns1MB(b *testing.B) { + benchmarkSetRedigo(b, 10, 1024*1024) +} + +func BenchmarkSetRedigo100Conns1MB(b *testing.B) { + benchmarkSetRedigo(b, 100, 1024*1024) +} + +func BenchmarkRedisSetGetBytes(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + value := bytes.Repeat([]byte{'1'}, 10000) + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.Set("key", value, 0).Err(); err != nil { + b.Fatal(err) + } + + got, err := client.Get("key").Bytes() + if err != nil { + b.Fatal(err) + } + if !bytes.Equal(got, value) { + b.Fatalf("got != value") + } + } + }) +} + +func BenchmarkRedisMGet(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + if err := client.MSet("key1", "hello1", "key2", "hello2").Err(); err != nil { + b.Fatal(err) + } + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.MGet("key1", "key2").Err(); err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkSetExpire(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.Set("key", "hello", 0).Err(); err != nil { + b.Fatal(err) + } + if err := client.Expire("key", time.Second).Err(); err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkPipeline(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, err := client.Pipelined(func(pipe *redis.Pipeline) error { + pipe.Set("key", "hello", 0) + pipe.Expire("key", time.Second) + return nil + }) + if err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkZAdd(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := client.ZAdd("key", redis.Z{float64(1), "hello"}).Err(); err != nil { + b.Fatal(err) + } + } + }) +} + +func BenchmarkPool(b *testing.B) { + client := benchmarkRedisClient(10) + defer client.Close() + + pool := client.Pool() + + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + conn, _, err := pool.Get() + if err != nil { + b.Fatalf("no error expected on pool.Get but received: %s", err.Error()) + } + if err = pool.Put(conn); err != nil { + b.Fatalf("no error expected on pool.Put but received: %s", err.Error()) + } + } + }) +} diff --git a/conn.go b/conn.go index 62a8bdf1..e7306d94 100644 --- a/conn.go +++ b/conn.go @@ -65,16 +65,16 @@ func (cn *conn) init(opt *Options) error { } func (cn *conn) writeCmds(cmds ...Cmder) error { - buf := cn.buf[:0] + cn.buf = cn.buf[:0] for _, cmd := range cmds { var err error - buf, err = appendArgs(buf, cmd.args()) + cn.buf, err = appendArgs(cn.buf, cmd.args()) if err != nil { return err } } - _, err := cn.Write(buf) + _, err := cn.Write(cn.buf) return err } diff --git a/pool_test.go b/pool_test.go index bc88c5fc..2494e56e 100644 --- a/pool_test.go +++ b/pool_test.go @@ -3,7 +3,6 @@ package redis_test import ( "errors" "sync" - "testing" "time" . "github.com/onsi/ginkgo" @@ -210,24 +209,3 @@ var _ = Describe("pool", func() { Expect(rateErr).To(MatchError(`redis: you open connections too fast (last_error="test")`)) }) }) - -func BenchmarkPool(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - pool := client.Pool() - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - conn, _, err := pool.Get() - if err != nil { - b.Fatalf("no error expected on pool.Get but received: %s", err.Error()) - } - if err = pool.Put(conn); err != nil { - b.Fatalf("no error expected on pool.Put but received: %s", err.Error()) - } - } - }) -} diff --git a/redis_test.go b/redis_test.go index 8c846d00..7b5197bc 100644 --- a/redis_test.go +++ b/redis_test.go @@ -1,9 +1,7 @@ package redis_test import ( - "bytes" "net" - "testing" "time" . "github.com/onsi/ginkgo" @@ -192,200 +190,3 @@ var _ = Describe("Client", func() { Expect(cn.UsedAt.Equal(future)).To(BeTrue()) }) }) - -//------------------------------------------------------------------------------ - -func benchRedisClient() *redis.Client { - client := redis.NewClient(&redis.Options{ - Addr: ":6379", - }) - if err := client.FlushDb().Err(); err != nil { - panic(err) - } - return client -} - -func BenchmarkRedisPing(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.Ping().Err(); err != nil { - b.Fatal(err) - } - } - }) -} - -func BenchmarkRedisSet(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - value := string(bytes.Repeat([]byte{'1'}, 10000)) - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.Set("key", value, 0).Err(); err != nil { - b.Fatal(err) - } - } - }) -} - -func BenchmarkRedisGetNil(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.Get("key").Err(); err != redis.Nil { - b.Fatal(err) - } - } - }) -} - -func benchmarkRedisSetGet(b *testing.B, size int) { - client := benchRedisClient() - defer client.Close() - - value := string(bytes.Repeat([]byte{'1'}, size)) - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.Set("key", value, 0).Err(); err != nil { - b.Fatal(err) - } - - got, err := client.Get("key").Result() - if err != nil { - b.Fatal(err) - } - if got != value { - b.Fatalf("got != value") - } - } - }) -} - -func BenchmarkRedisSetGet64Bytes(b *testing.B) { - benchmarkRedisSetGet(b, 64) -} - -func BenchmarkRedisSetGet1KB(b *testing.B) { - benchmarkRedisSetGet(b, 1024) -} - -func BenchmarkRedisSetGet10KB(b *testing.B) { - benchmarkRedisSetGet(b, 10*1024) -} - -func BenchmarkRedisSetGet1MB(b *testing.B) { - benchmarkRedisSetGet(b, 1024*1024) -} - -func BenchmarkRedisSetGetBytes(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - value := bytes.Repeat([]byte{'1'}, 10000) - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.Set("key", value, 0).Err(); err != nil { - b.Fatal(err) - } - - got, err := client.Get("key").Bytes() - if err != nil { - b.Fatal(err) - } - if !bytes.Equal(got, value) { - b.Fatalf("got != value") - } - } - }) -} - -func BenchmarkRedisMGet(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - if err := client.MSet("key1", "hello1", "key2", "hello2").Err(); err != nil { - b.Fatal(err) - } - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.MGet("key1", "key2").Err(); err != nil { - b.Fatal(err) - } - } - }) -} - -func BenchmarkSetExpire(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.Set("key", "hello", 0).Err(); err != nil { - b.Fatal(err) - } - if err := client.Expire("key", time.Second).Err(); err != nil { - b.Fatal(err) - } - } - }) -} - -func BenchmarkPipeline(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - _, err := client.Pipelined(func(pipe *redis.Pipeline) error { - pipe.Set("key", "hello", 0) - pipe.Expire("key", time.Second) - return nil - }) - if err != nil { - b.Fatal(err) - } - } - }) -} - -func BenchmarkZAdd(b *testing.B) { - client := benchRedisClient() - defer client.Close() - - b.ResetTimer() - - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if err := client.ZAdd("key", redis.Z{float64(1), "hello"}).Err(); err != nil { - b.Fatal(err) - } - } - }) -}