|
| 1 | +package meter |
| 2 | + |
| 3 | +// End-to-end emit benchmarks: meter -> real LowLatencyBuffer (backed by a |
| 4 | +// NoopWriter so we exercise the buffer append path without socket I/O). |
| 5 | +// Uses only the public Counter API, so the loop body is identical before and |
| 6 | +// after the WriteLine interface extension. |
| 7 | + |
| 8 | +import ( |
| 9 | + "runtime" |
| 10 | + "testing" |
| 11 | + "time" |
| 12 | + |
| 13 | + "github.com/Netflix/spectator-go/v2/spectator/writer" |
| 14 | +) |
| 15 | + |
| 16 | +type emitQuietLogger struct{} |
| 17 | + |
| 18 | +func (emitQuietLogger) Debugf(string, ...interface{}) {} |
| 19 | +func (emitQuietLogger) Infof(string, ...interface{}) {} |
| 20 | +func (emitQuietLogger) Errorf(string, ...interface{}) {} |
| 21 | + |
| 22 | +// bufWriter adapts a LowLatencyBuffer to the Writer interface (its Close has no |
| 23 | +// error return). |
| 24 | +type bufWriter struct{ b *writer.LowLatencyBuffer } |
| 25 | + |
| 26 | +func (w *bufWriter) Write(line string) { w.b.Write(line) } |
| 27 | +func (w *bufWriter) WriteLine(prefix, value string) { w.b.WriteLine(prefix, value) } |
| 28 | +func (w *bufWriter) WriteInt(prefix string, v int64) { w.b.WriteInt(prefix, v) } |
| 29 | +func (w *bufWriter) WriteUint(prefix string, v uint64) { w.b.WriteUint(prefix, v) } |
| 30 | +func (w *bufWriter) WriteFloat(prefix string, v float64) { w.b.WriteFloat(prefix, v) } |
| 31 | +func (w *bufWriter) WriteBytes(line []byte) { w.b.Write(string(line)) } |
| 32 | +func (w *bufWriter) WriteString(line string) { w.b.Write(line) } |
| 33 | +func (w *bufWriter) Close() error { w.b.Close(); return nil } |
| 34 | + |
| 35 | +func newBenchBuffer(b *testing.B) *bufWriter { |
| 36 | + b.Helper() |
| 37 | + shards := runtime.NumCPU() |
| 38 | + bufferSize := 2 * 16 * 60 * 1024 * shards |
| 39 | + buf := writer.NewLowLatencyBuffer(&writer.NoopWriter{}, emitQuietLogger{}, bufferSize, time.Millisecond) |
| 40 | + w := &bufWriter{buf} |
| 41 | + b.Cleanup(func() { w.Close() }) |
| 42 | + return w |
| 43 | +} |
| 44 | + |
| 45 | +var emitBenchTags = map[string]string{ |
| 46 | + "app": "gandalfAgent", |
| 47 | + "result": "allowed", |
| 48 | + "policy": "explicit", |
| 49 | +} |
| 50 | + |
| 51 | +// Cached counter (constructed once), Increment in the loop — isolates the emit path. |
| 52 | +func BenchmarkEmitBuffer_Counter_Increment(b *testing.B) { |
| 53 | + c := NewCounter(NewId("gandalfAgent.authzEngine.appAuthzResult", emitBenchTags), newBenchBuffer(b)) |
| 54 | + b.ReportAllocs() |
| 55 | + b.ResetTimer() |
| 56 | + for i := 0; i < b.N; i++ { |
| 57 | + c.Increment() |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +// Cached counter, Add(delta) in the loop — value requires formatting. |
| 62 | +func BenchmarkEmitBuffer_Counter_Add(b *testing.B) { |
| 63 | + c := NewCounter(NewId("gandalfAgent.authzEngine.appAuthzResult", emitBenchTags), newBenchBuffer(b)) |
| 64 | + b.ReportAllocs() |
| 65 | + b.ResetTimer() |
| 66 | + for i := 0; i < b.N; i++ { |
| 67 | + c.Add(int64(i) + 1) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +// Cached gauge, Set(float) in the loop. |
| 72 | +func BenchmarkEmitBuffer_Gauge_Set(b *testing.B) { |
| 73 | + g := NewGauge(NewId("gandalfAgent.gauge", emitBenchTags), newBenchBuffer(b)) |
| 74 | + b.ReportAllocs() |
| 75 | + b.ResetTimer() |
| 76 | + for i := 0; i < b.N; i++ { |
| 77 | + g.Set(3.14159) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +// Cached timer, Record in the loop. |
| 82 | +func BenchmarkEmitBuffer_Timer_Record(b *testing.B) { |
| 83 | + t := NewTimer(NewId("gandalfAgent.timer", emitBenchTags), newBenchBuffer(b)) |
| 84 | + b.ReportAllocs() |
| 85 | + b.ResetTimer() |
| 86 | + for i := 0; i < b.N; i++ { |
| 87 | + t.Record(time.Duration(i) * time.Millisecond) |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +// Cached distribution summary, Record in the loop. |
| 92 | +func BenchmarkEmitBuffer_DistSummary_Record(b *testing.B) { |
| 93 | + d := NewDistributionSummary(NewId("gandalfAgent.distsummary", emitBenchTags), newBenchBuffer(b)) |
| 94 | + b.ReportAllocs() |
| 95 | + b.ResetTimer() |
| 96 | + for i := 0; i < b.N; i++ { |
| 97 | + d.Record(int64(i)) |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +// Construct-per-call (gandalf's actual pattern) over a real buffer, via NewId |
| 102 | +// (the WithId path) for comparison. |
| 103 | +func BenchmarkEmitBuffer_Counter_ConstructPerCall(b *testing.B) { |
| 104 | + w := newBenchBuffer(b) |
| 105 | + b.ReportAllocs() |
| 106 | + b.ResetTimer() |
| 107 | + for i := 0; i < b.N; i++ { |
| 108 | + NewCounter(NewId("gandalfAgent.authzEngine.appAuthzResult", emitBenchTags), w).Increment() |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +// Construct-per-call via the direct (name, tags) path — what registry.Counter |
| 113 | +// now uses. No Id, no tag-map copy. |
| 114 | +func BenchmarkEmitBuffer_Counter_ConstructPerCall_Direct(b *testing.B) { |
| 115 | + w := newBenchBuffer(b) |
| 116 | + b.ReportAllocs() |
| 117 | + b.ResetTimer() |
| 118 | + for i := 0; i < b.N; i++ { |
| 119 | + NewCounterDirect("gandalfAgent.authzEngine.appAuthzResult", emitBenchTags, nil, w).Increment() |
| 120 | + } |
| 121 | +} |
0 commit comments