Skip to content

Commit

Permalink
expvar: Add benchmarks for perf sensitive operations
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
evanphx authored and dvyukov committed Feb 5, 2015
1 parent cb4b28e commit fc22fb8
Showing 1 changed file with 217 additions and 0 deletions.
217 changes: 217 additions & 0 deletions src/expvar/expvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ package expvar
import (
"bytes"
"encoding/json"
"net"
"net/http/httptest"
"runtime"
"strconv"
"sync"
"testing"
)

Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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"}
Expand Down Expand Up @@ -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()
}

0 comments on commit fc22fb8

Please sign in to comment.