From efb742b043153f7e0b8b2b2cff45534b26c6bfb9 Mon Sep 17 00:00:00 2001 From: Ling Yuan Date: Mon, 11 Nov 2024 14:17:37 -0800 Subject: [PATCH] Optimize counter inc-by-one Conceivably increment-by-one is by far the most common operation on counters. This PR optimize such operation, making it alloc-free. Benchmark with: ``` func BenchmarkInc(b *testing.B) { b.Run("Increment", func(b *testing.B) { w := writer.MemoryWriter{} id := NewId("foo", nil) c := NewCounter(id, &w) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { c.Increment() } }) } ``` before optimization: ``` goos: darwin goarch: arm64 pkg: github.com/Netflix/spectator-go/v2/spectator/meter cpu: Apple M3 Pro BenchmarkInc/Increment-12 10298648 115.2 ns/op 120 B/op 3 allocs/op PASS ok github.com/Netflix/spectator-go/v2/spectator/meter 2.485s ``` after optimization: ``` goos: darwin goarch: arm64 pkg: github.com/Netflix/spectator-go/v2/spectator/meter cpu: Apple M3 Pro BenchmarkInc/Increment-12 40374824 31.44 ns/op 97 B/op 0 allocs/op PASS ok github.com/Netflix/spectator-go/v2/spectator/meter 3.563s ``` --- spectator/meter/counter.go | 19 +++++++++++-------- spectator/meter/counter_test.go | 12 +++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/spectator/meter/counter.go b/spectator/meter/counter.go index efd53e4..d19a710 100644 --- a/spectator/meter/counter.go +++ b/spectator/meter/counter.go @@ -13,14 +13,14 @@ import ( // // https://netflix.github.io/spectator/en/latest/intro/counter/ type Counter struct { - id *Id - writer writer.Writer - meterTypeSymbol string + id *Id + writer writer.Writer + incByOneLine string } // NewCounter generates a new counter, using the provided meter identifier. func NewCounter(id *Id, writer writer.Writer) *Counter { - return &Counter{id, writer, "c"} + return &Counter{id, writer, fmt.Sprintf("c:%s:1", id.spectatordId)} } // MeterId returns the meter identifier. @@ -30,14 +30,17 @@ func (c *Counter) MeterId() *Id { // Increment increments the counter. func (c *Counter) Increment() { - var line = fmt.Sprintf("%s:%s:%d", c.meterTypeSymbol, c.id.spectatordId, 1) - c.writer.Write(line) + c.writer.Write(c.incByOneLine) } // Add adds an int64 delta to the current measurement. func (c *Counter) Add(delta int64) { + if delta == 1 { + c.writer.Write(c.incByOneLine) + return + } if delta > 0 { - var line = fmt.Sprintf("%s:%s:%d", c.meterTypeSymbol, c.id.spectatordId, delta) + var line = fmt.Sprintf("c:%s:%d", c.id.spectatordId, delta) c.writer.Write(line) } } @@ -45,7 +48,7 @@ func (c *Counter) Add(delta int64) { // AddFloat adds a float64 delta to the current measurement. func (c *Counter) AddFloat(delta float64) { if delta > 0.0 { - var line = fmt.Sprintf("%s:%s:%f", c.meterTypeSymbol, c.id.spectatordId, delta) + var line = fmt.Sprintf("c:%s:%f", c.id.spectatordId, delta) c.writer.Write(line) } } diff --git a/spectator/meter/counter_test.go b/spectator/meter/counter_test.go index bd57947..fe1a314 100644 --- a/spectator/meter/counter_test.go +++ b/spectator/meter/counter_test.go @@ -23,15 +23,21 @@ func TestCounter_Add(t *testing.T) { id := NewId("add", nil) c := NewCounter(id, &w) - c.Add(4) + c.Add(1) - expected := "c:add:4" + expected := "c:add:1" if w.Lines()[0] != expected { t.Error("Expected ", expected, " got ", w.Lines()[0]) } + c.Add(4) + + expected = "c:add:4" + if w.Lines()[1] != expected { + t.Error("Expected ", expected, " got ", w.Lines()[1]) + } c.Add(-1) - if len(w.Lines()) != 1 { + if len(w.Lines()) != 2 { t.Error("Negative deltas should be ignored") } }