Add Microbenchmarks for VIF/VC creations.
BenchmarkVIF_Dial:
Histogram (unit: us)
Count: 100000 Min: 4 Max: 7292 Avg: 14.39
------------------------------------------------------------
[ 4, 5) 1 0.0% 0.0%
[ 5, 6) 53395 53.4% 53.4% #####
[ 6, 9) 32558 32.6% 86.0% ###
[ 9, 14) 9596 9.6% 95.5% #
[ 14, 24) 2444 2.4% 98.0%
[ 24, 43) 1182 1.2% 99.2%
[ 43, 78) 400 0.4% 99.6%
[ 78, 141) 88 0.1% 99.7%
[ 141, 255) 52 0.1% 99.7%
[ 255, 462) 1 0.0% 99.7%
[ 462, 837) 17 0.0% 99.7%
[ 837, 1517) 77 0.1% 99.8%
[ 1517, 2747) 19 0.0% 99.8%
[ 2747, 4973) 157 0.2% 100.0%
[ 4973, 9001) 13 0.0% 100.0%
[ 9001, 16288) 0 0.0% 100.0%
[16288, inf) 0 0.0% 100.0%
100000 17919 ns/op
BenchmarkVIF_DialTLS
Histogram (unit: ms)
Count: 100 Min: 13 Max: 26 Avg: 14.35
------------------------------------------------------------
[ 13, 14) 15 15.0% 15.0% ##
[ 14, 15) 57 57.0% 72.0% ######
[ 15, 16) 24 24.0% 96.0% ##
[ 16, 17) 1 1.0% 97.0%
[ 17, 18) 1 1.0% 98.0%
[ 18, 20) 0 0.0% 98.0%
[ 20, 22) 0 0.0% 98.0%
[ 22, 25) 1 1.0% 99.0%
[ 25, 28) 1 1.0% 100.0%
[ 28, 32) 0 0.0% 100.0%
[ 32, 37) 0 0.0% 100.0%
[ 37, 43) 0 0.0% 100.0%
[ 43, 50) 0 0.0% 100.0%
[ 50, 59) 0 0.0% 100.0%
[ 59, 69) 0 0.0% 100.0%
[ 69, 81) 0 0.0% 100.0%
[ 81, inf) 0 0.0% 100.0%
100 14755394 ns/op
BenchmarkVC_DialTLS
Histogram (unit: ms)
Count: 100 Min: 19 Max: 32 Avg: 19.96
------------------------------------------------------------
[ 19, 20) 53 53.0% 53.0% #####
[ 20, 21) 23 23.0% 76.0% ##
[ 21, 22) 15 15.0% 91.0% ##
[ 22, 23) 6 6.0% 97.0% #
[ 23, 24) 0 0.0% 97.0%
[ 24, 26) 1 1.0% 98.0%
[ 26, 28) 1 1.0% 99.0%
[ 28, 31) 0 0.0% 99.0%
[ 31, 34) 1 1.0% 100.0%
[ 34, 38) 0 0.0% 100.0%
[ 38, 43) 0 0.0% 100.0%
[ 43, 49) 0 0.0% 100.0%
[ 49, 56) 0 0.0% 100.0%
[ 56, 65) 0 0.0% 100.0%
[ 65, 75) 0 0.0% 100.0%
[ 75, 87) 0 0.0% 100.0%
[ 87, inf) 0 0.0% 100.0%
100 20544962 ns/op
Change-Id: I5293af2dc52f1a04478d6ac8fcd8cfc79201abea
diff --git a/lib/testutil/stats.go b/lib/testutil/stats.go
new file mode 100644
index 0000000..976696a
--- /dev/null
+++ b/lib/testutil/stats.go
@@ -0,0 +1,104 @@
+package testutil
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math"
+ "time"
+
+ "veyron.io/veyron/veyron/lib/stats/histogram"
+)
+
+// BenchStats is a simple helper for gathering additional statistics
+// like histogram during benchmarks. This is not thread safe.
+type BenchStats struct {
+ numBuckets int
+ unit time.Duration
+ min, max int64
+ histogram *histogram.Histogram
+
+ durations durationSlice
+ dirty bool
+}
+
+type durationSlice []time.Duration
+
+// NewBenchStats creates a new BenchStats instance. If numBuckets is not
+// positive, the default value (16) will be used.
+func NewBenchStats(numBuckets int) *BenchStats {
+ if numBuckets <= 0 {
+ numBuckets = 16
+ }
+ return &BenchStats{
+ // Use one more bucket for the last unbounded bucket.
+ numBuckets: numBuckets + 1,
+ durations: make(durationSlice, 0, 100000),
+ }
+}
+
+// Add adds an elapsed time per operation to the BenchStats.
+func (stats *BenchStats) Add(d time.Duration) {
+ stats.durations = append(stats.durations, d)
+ stats.dirty = true
+}
+
+// maybeUpdate updates internal stat data if there was any newly added
+// stats since this was updated.
+func (stats *BenchStats) maybeUpdate() {
+ if !stats.dirty || len(stats.durations) == 0 {
+ return
+ }
+
+ stats.min = math.MaxInt64
+ stats.max = 0
+ for _, d := range stats.durations {
+ if stats.min > int64(d) {
+ stats.min = int64(d)
+ }
+ if stats.max < int64(d) {
+ stats.max = int64(d)
+ }
+ }
+
+ // Use the largest unit that can represent the minimum time duration.
+ stats.unit = time.Nanosecond
+ for _, u := range []time.Duration{time.Microsecond, time.Millisecond, time.Second} {
+ if stats.min <= int64(u) {
+ break
+ }
+ stats.unit = u
+ }
+
+ // Adjust the min/max according to the new unit.
+ stats.min /= int64(stats.unit)
+ stats.max /= int64(stats.unit)
+
+ stats.histogram = histogram.New(histogram.Options{
+ NumBuckets: stats.numBuckets,
+ // max(i.e., Nth lower bound) = min + (1 + growthFactor)^(numBuckets-2).
+ GrowthFactor: math.Pow(float64(stats.max-stats.min), 1/float64(stats.numBuckets-2)) - 1,
+ SmallestBucketSize: 1.0,
+ MinValue: stats.min})
+
+ for _, d := range stats.durations {
+ stats.histogram.Add(int64(d / stats.unit))
+ }
+
+ stats.dirty = false
+}
+
+// Print writes textual output of the BenchStats.
+func (stats *BenchStats) Print(w io.Writer) {
+ stats.maybeUpdate()
+
+ fmt.Fprintf(w, "Histogram (unit: %s)\n", fmt.Sprintf("%v", stats.unit)[1:])
+ stats.histogram.Value().Print(w)
+}
+
+// String returns the textual output of the BenchStats as string.
+func (stats *BenchStats) String() string {
+ var b bytes.Buffer
+ stats.Print(&b)
+ return b.String()
+}
diff --git a/runtimes/google/ipc/stream/benchmark/dial_test.go b/runtimes/google/ipc/stream/benchmark/dial_test.go
new file mode 100644
index 0000000..ee87feb
--- /dev/null
+++ b/runtimes/google/ipc/stream/benchmark/dial_test.go
@@ -0,0 +1,10 @@
+package benchmark
+
+import "testing"
+
+func BenchmarkVIF_Dial(b *testing.B) { benchmarkVIFDial(b, securityNone) }
+func BenchmarkVIF_DialTLS(b *testing.B) { benchmarkVIFDial(b, securityTLS) }
+
+// Note: We don't benchmark Non-TLC VC Dial for now since it doesn't wait ack
+// from the server after sending "OpenVC".
+func BenchmarkVC_DialTLS(b *testing.B) { benchmarkVCDial(b, securityTLS) }
diff --git a/runtimes/google/ipc/stream/benchmark/dial_vc.go b/runtimes/google/ipc/stream/benchmark/dial_vc.go
new file mode 100644
index 0000000..e0e397d
--- /dev/null
+++ b/runtimes/google/ipc/stream/benchmark/dial_vc.go
@@ -0,0 +1,58 @@
+package benchmark
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "veyron.io/veyron/veyron/lib/testutil"
+ "veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
+
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/options"
+)
+
+// benchmarkVCDial measures VC creation time over the underlying VIF.
+func benchmarkVCDial(b *testing.B, mode options.VCSecurityLevel) {
+ stats := testutil.NewBenchStats(16)
+
+ server := manager.InternalNew(naming.FixedRoutingID(0x5))
+ client := manager.InternalNew(naming.FixedRoutingID(0xc))
+
+ _, ep, err := server.Listen("tcp", "127.0.0.1:0", mode)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // Warmup to create the underlying VIF.
+ _, err = client.Dial(ep, mode)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // Reset the timer to exclude any underlying setup time from measurement.
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ b.StartTimer()
+ start := time.Now()
+
+ _, err := client.Dial(ep, mode)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ duration := time.Since(start)
+ b.StopTimer()
+
+ stats.Add(duration)
+
+ client.ShutdownEndpoint(ep)
+ }
+
+ client.Shutdown()
+ server.Shutdown()
+
+ fmt.Println()
+ fmt.Println(stats)
+}
diff --git a/runtimes/google/ipc/stream/benchmark/dial_vif.go b/runtimes/google/ipc/stream/benchmark/dial_vif.go
new file mode 100644
index 0000000..40289f2
--- /dev/null
+++ b/runtimes/google/ipc/stream/benchmark/dial_vif.go
@@ -0,0 +1,51 @@
+package benchmark
+
+import (
+ "fmt"
+ "net"
+ "testing"
+ "time"
+
+ "veyron.io/veyron/veyron/lib/testutil"
+ "veyron.io/veyron/veyron/runtimes/google/ipc/stream/vif"
+
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/options"
+)
+
+// benchmarkVIFDial measures VIF creation time over the underlying net connection.
+func benchmarkVIFDial(b *testing.B, mode options.VCSecurityLevel) {
+ stats := testutil.NewBenchStats(16)
+
+ // Reset the timer to exclude any underlying setup time from measurement.
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ nc, ns := net.Pipe()
+
+ server, err := vif.InternalNewAcceptedVIF(ns, naming.FixedRoutingID(0x5), nil, mode)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.StartTimer()
+ start := time.Now()
+
+ client, err := vif.InternalNewDialedVIF(nc, naming.FixedRoutingID(0xc), nil, mode)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ duration := time.Since(start)
+ b.StopTimer()
+
+ stats.Add(duration)
+
+ client.Close()
+ server.Close()
+ }
+
+ fmt.Println()
+ fmt.Println(stats)
+}