Commit fc22fb8f authored by Evan Phoenix's avatar Evan Phoenix Committed by Dmitry Vyukov

expvar: Add benchmarks for perf sensitive operations

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/3680Reviewed-by: 's avatarDmitry Vyukov <dvyukov@google.com>
parent cb4b28e0
...@@ -7,8 +7,11 @@ package expvar ...@@ -7,8 +7,11 @@ package expvar
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"net"
"net/http/httptest" "net/http/httptest"
"runtime"
"strconv" "strconv"
"sync"
"testing" "testing"
) )
...@@ -47,6 +50,26 @@ func TestInt(t *testing.T) { ...@@ -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) { func TestFloat(t *testing.T) {
RemoveAll() RemoveAll()
reqs := NewFloat("requests-float") reqs := NewFloat("requests-float")
...@@ -73,6 +96,26 @@ func TestFloat(t *testing.T) { ...@@ -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) { func TestString(t *testing.T) {
RemoveAll() RemoveAll()
name := NewString("my-name") name := NewString("my-name")
...@@ -90,6 +133,16 @@ func TestString(t *testing.T) { ...@@ -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) { func TestMapCounter(t *testing.T) {
RemoveAll() RemoveAll()
colors := NewMap("bike-shed-colors") colors := NewMap("bike-shed-colors")
...@@ -130,6 +183,38 @@ func TestMapCounter(t *testing.T) { ...@@ -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) { func TestFunc(t *testing.T) {
RemoveAll() RemoveAll()
var x interface{} = []string{"a", "b"} var x interface{} = []string{"a", "b"}
...@@ -165,3 +250,135 @@ func TestHandler(t *testing.T) { ...@@ -165,3 +250,135 @@ func TestHandler(t *testing.T) {
t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want) 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()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment