From d39a552c8a5b25ce949ee4124d9a3bf45003e2fd Mon Sep 17 00:00:00 2001 From: William Banfield Date: Tue, 26 Oct 2021 09:52:26 +0200 Subject: [PATCH] benchmark different mutex versions --- internal/p2p/metrics.go | 26 +++++++- internal/p2p/metrics_test.go | 111 +++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/internal/p2p/metrics.go b/internal/p2p/metrics.go index 3677180de..d09eef3b9 100644 --- a/internal/p2p/metrics.go +++ b/internal/p2p/metrics.go @@ -160,7 +160,7 @@ func NopMetrics() *Metrics { // type that is passed in. // This method uses a map on the Metrics struct so that each label name only needs // to be produced once to prevent expensive string operations. -func (m *Metrics) ValueToMetricLabel(i interface{}) string { +func (m *Metrics) ValueToMetricLabelRW(i interface{}) string { t := reflect.TypeOf(i) m.mtx.RLock() @@ -178,3 +178,27 @@ func (m *Metrics) ValueToMetricLabel(i interface{}) string { m.messageLabelNames[t] = l return l } + +// ValueToMetricLabel is a method that is used to produce a prometheus label value of the golang +// type that is passed in. +// This method uses a map on the Metrics struct so that each label name only needs +// to be produced once to prevent expensive string operations. +func (m *Metrics) ValueToMetricLabelM(i interface{}) string { + t := reflect.TypeOf(i) + m.mtx.Lock() + defer m.mtx.Unlock() + + if s, ok := m.messageLabelNames[t]; ok { + return s + } + + s := t.String() + ss := valueToLabelRegexp.FindStringSubmatch(s) + l := fmt.Sprintf("%s_%s", ss[1], ss[2]) + m.messageLabelNames[t] = l + return l +} + +func (m *Metrics) ValueToMetricLabel(i interface{}) string { + return m.ValueToMetricLabelRW(i) +} diff --git a/internal/p2p/metrics_test.go b/internal/p2p/metrics_test.go index 53b3c47bd..95519ede1 100644 --- a/internal/p2p/metrics_test.go +++ b/internal/p2p/metrics_test.go @@ -1,9 +1,11 @@ package p2p import ( + "sync" "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/proto/tendermint/consensus" "github.com/tendermint/tendermint/proto/tendermint/p2p" ) @@ -17,3 +19,112 @@ func TestValueToMetricsLabel(t *testing.T) { str = m.ValueToMetricLabel(r) assert.Equal(t, "p2p_PexResponse", str) } + +func BenchmarkValueToMetricsLabel(b *testing.B) { + numGoRoutines := 16 + msgTypes := []interface{}{ + &p2p.PexResponse{}, + &p2p.PexRequest{}, + &p2p.PexAddress{}, + &consensus.HasVote{}, + &consensus.NewRoundStep{}, + &consensus.NewValidBlock{}, + &consensus.VoteSetBits{}, + &consensus.VoteSetMaj23{}, + &consensus.Vote{}, + &consensus.BlockPart{}, + &consensus.ProposalPOL{}, + } + b.Run("RW Mutex Version", func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + m := NopMetrics() + wg := &sync.WaitGroup{} + b.StartTimer() + for j := 0; j < numGoRoutines; j++ { + wg.Add(1) + go func() { + defer wg.Done() + for k := 0; k < 100; k++ { + m.ValueToMetricLabelRW(msgTypes[k%len(msgTypes)]) + } + }() + } + wg.Wait() + } + }) + b.Run("Mutex Version", func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + m := NopMetrics() + wg := &sync.WaitGroup{} + b.StartTimer() + for j := 0; j < numGoRoutines; j++ { + wg.Add(1) + go func() { + defer wg.Done() + for k := 0; k < 100; k++ { + m.ValueToMetricLabelM(msgTypes[k%len(msgTypes)]) + } + }() + } + wg.Wait() + } + }) +} + +func BenchmarkValueToMetricsLabelPrefilled(b *testing.B) { + numGoRoutines := 16 + msgTypes := []interface{}{ + &p2p.PexResponse{}, + &p2p.PexRequest{}, + &p2p.PexAddress{}, + &consensus.HasVote{}, + &consensus.NewRoundStep{}, + &consensus.NewValidBlock{}, + &consensus.VoteSetBits{}, + &consensus.VoteSetMaj23{}, + &consensus.Vote{}, + &consensus.BlockPart{}, + &consensus.ProposalPOL{}, + } + // create a metric set and put all of the types into the label map. + m := NopMetrics() + for _, t := range msgTypes { + m.ValueToMetricLabel(t) + } + b.Run("RW Mutex Version", func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + wg := &sync.WaitGroup{} + b.StartTimer() + for j := 0; j < numGoRoutines; j++ { + wg.Add(1) + go func() { + defer wg.Done() + for k := 0; k < 100; k++ { + m.ValueToMetricLabelRW(msgTypes[k%len(msgTypes)]) + } + }() + } + wg.Wait() + } + }) + b.Run("Mutex Version", func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + wg := &sync.WaitGroup{} + b.StartTimer() + for j := 0; j < numGoRoutines; j++ { + wg.Add(1) + go func() { + defer wg.Done() + for k := 0; k < 100; k++ { + m.ValueToMetricLabelM(msgTypes[k%len(msgTypes)]) + } + }() + } + wg.Wait() + } + }) +}