]> Cypherpunks.ru repositories - gostls13.git/commitdiff
expvar: Add benchmarks for perf sensitive operations
authorEvan Phoenix <evan@phx.io>
Fri, 30 Jan 2015 23:31:47 +0000 (15:31 -0800)
committerDmitry Vyukov <dvyukov@google.com>
Thu, 5 Feb 2015 13:32:25 +0000 (13:32 +0000)
These benchmarks are only for functions commonly used in loops. The
other functions are typically used for inspection or setup and thus are
not performance sensitive.

Change-Id: I8d0a0ba2d8234ecacb40fa3aa9077bf93c8fe89c
Reviewed-on: https://go-review.googlesource.com/3680
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
src/expvar/expvar_test.go

index 765e3b757e9a48a95ea216a34efc83baec8e836e..544e3fbc700c4b550bd6be4f3e506204f74fd479 100644 (file)
@@ -7,8 +7,11 @@ package expvar
 import (
        "bytes"
        "encoding/json"
+       "net"
        "net/http/httptest"
+       "runtime"
        "strconv"
+       "sync"
        "testing"
 )
 
@@ -47,6 +50,26 @@ func TestInt(t *testing.T) {
        }
 }
 
+func BenchmarkIntAdd(b *testing.B) {
+       var v Int
+
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       v.Add(1)
+               }
+       })
+}
+
+func BenchmarkIntSet(b *testing.B) {
+       var v Int
+
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       v.Set(1)
+               }
+       })
+}
+
 func TestFloat(t *testing.T) {
        RemoveAll()
        reqs := NewFloat("requests-float")
@@ -73,6 +96,26 @@ func TestFloat(t *testing.T) {
        }
 }
 
+func BenchmarkFloatAdd(b *testing.B) {
+       var f Float
+
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       f.Add(1.0)
+               }
+       })
+}
+
+func BenchmarkFloatSet(b *testing.B) {
+       var f Float
+
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       f.Set(1.0)
+               }
+       })
+}
+
 func TestString(t *testing.T) {
        RemoveAll()
        name := NewString("my-name")
@@ -90,6 +133,16 @@ func TestString(t *testing.T) {
        }
 }
 
+func BenchmarkStringSet(b *testing.B) {
+       var s String
+
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       s.Set("red")
+               }
+       })
+}
+
 func TestMapCounter(t *testing.T) {
        RemoveAll()
        colors := NewMap("bike-shed-colors")
@@ -130,6 +183,38 @@ func TestMapCounter(t *testing.T) {
        }
 }
 
+func BenchmarkMapSet(b *testing.B) {
+       m := new(Map).Init()
+
+       v := new(Int)
+
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       m.Set("red", v)
+               }
+       })
+}
+
+func BenchmarkMapAddSame(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               m := new(Map).Init()
+               m.Add("red", 1)
+               m.Add("red", 1)
+               m.Add("red", 1)
+               m.Add("red", 1)
+       }
+}
+
+func BenchmarkMapAddDifferent(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               m := new(Map).Init()
+               m.Add("red", 1)
+               m.Add("blue", 1)
+               m.Add("green", 1)
+               m.Add("yellow", 1)
+       }
+}
+
 func TestFunc(t *testing.T) {
        RemoveAll()
        var x interface{} = []string{"a", "b"}
@@ -165,3 +250,135 @@ func TestHandler(t *testing.T) {
                t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want)
        }
 }
+
+func BenchmarkRealworldExpvarUsage(b *testing.B) {
+       var (
+               bytesSent Int
+               bytesRead Int
+       )
+
+       // The benchmark creates GOMAXPROCS client/server pairs.
+       // Each pair creates 4 goroutines: client reader/writer and server reader/writer.
+       // The benchmark stresses concurrent reading and writing to the same connection.
+       // Such pattern is used in net/http and net/rpc.
+
+       b.StopTimer()
+
+       P := runtime.GOMAXPROCS(0)
+       N := b.N / P
+       W := 1000
+
+       // Setup P client/server connections.
+       clients := make([]net.Conn, P)
+       servers := make([]net.Conn, P)
+       ln, err := net.Listen("tcp", laddr)
+       if err != nil {
+               b.Fatalf("Listen failed: %v", err)
+       }
+       defer ln.Close()
+       done := make(chan bool)
+       go func() {
+               for p := 0; p < P; p++ {
+                       s, err := ln.Accept()
+                       if err != nil {
+                               b.Errorf("Accept failed: %v", err)
+                               return
+                       }
+                       servers[p] = s
+               }
+               done <- true
+       }()
+       for p := 0; p < P; p++ {
+               c, err := net.Dial("tcp", ln.Addr().String())
+               if err != nil {
+                       b.Fatalf("Dial failed: %v", err)
+               }
+               clients[p] = c
+       }
+       <-done
+
+       b.StartTimer()
+
+       var wg sync.WaitGroup
+       wg.Add(4 * P)
+       for p := 0; p < P; p++ {
+               // Client writer.
+               go func(c net.Conn) {
+                       defer wg.Done()
+                       var buf [1]byte
+                       for i := 0; i < N; i++ {
+                               v := byte(i)
+                               for w := 0; w < W; w++ {
+                                       v *= v
+                               }
+                               buf[0] = v
+                               n, err := c.Write(buf[:])
+                               if err != nil {
+                                       b.Errorf("Write failed: %v", err)
+                                       return
+                               }
+
+                               bytesSent.Add(int64(n))
+                       }
+               }(clients[p])
+
+               // Pipe between server reader and server writer.
+               pipe := make(chan byte, 128)
+
+               // Server reader.
+               go func(s net.Conn) {
+                       defer wg.Done()
+                       var buf [1]byte
+                       for i := 0; i < N; i++ {
+                               n, err := s.Read(buf[:])
+
+                               if err != nil {
+                                       b.Errorf("Read failed: %v", err)
+                                       return
+                               }
+
+                               bytesRead.Add(int64(n))
+                               pipe <- buf[0]
+                       }
+               }(servers[p])
+
+               // Server writer.
+               go func(s net.Conn) {
+                       defer wg.Done()
+                       var buf [1]byte
+                       for i := 0; i < N; i++ {
+                               v := <-pipe
+                               for w := 0; w < W; w++ {
+                                       v *= v
+                               }
+                               buf[0] = v
+                               n, err := s.Write(buf[:])
+                               if err != nil {
+                                       b.Errorf("Write failed: %v", err)
+                                       return
+                               }
+
+                               bytesSent.Add(int64(n))
+                       }
+                       s.Close()
+               }(servers[p])
+
+               // Client reader.
+               go func(c net.Conn) {
+                       defer wg.Done()
+                       var buf [1]byte
+                       for i := 0; i < N; i++ {
+                               n, err := c.Read(buf[:])
+
+                               if err != nil {
+                                       b.Errorf("Read failed: %v", err)
+                                       return
+                               }
+
+                               bytesRead.Add(int64(n))
+                       }
+                       c.Close()
+               }(clients[p])
+       }
+       wg.Wait()
+}