@@ -7,8 +7,10 @@ import (
77 "crypto/rand"
88 "errors"
99 "fmt"
10+ "math"
1011 "os"
1112 "runtime"
13+ "sort"
1214 "sync"
1315 "sync/atomic"
1416 "time"
@@ -38,6 +40,49 @@ import (
3840
3941const serverName = "benchmarks.rps.server"
4042
43+ type latencyStats struct {
44+ latencies []time.Duration
45+ mu sync.Mutex
46+ }
47+
48+ func (l * latencyStats ) add (d time.Duration ) {
49+ l .mu .Lock ()
50+ defer l .mu .Unlock ()
51+ l .latencies = append (l .latencies , d )
52+ }
53+
54+ func (l * latencyStats ) calculate () (time.Duration , time.Duration ) {
55+ l .mu .Lock ()
56+ defer l .mu .Unlock ()
57+
58+ if len (l .latencies ) == 0 {
59+ return 0 , 0
60+ }
61+
62+ // Sort latencies for P99 calculation
63+ sort .Slice (l .latencies , func (i , j int ) bool {
64+ return l .latencies [i ] < l .latencies [j ]
65+ })
66+
67+ // Calculate average
68+ var total time.Duration
69+ for _ , d := range l .latencies {
70+ total += d
71+ }
72+
73+ avg := total / time .Duration (len (l .latencies ))
74+
75+ // Calculate P99
76+ p99Index := int (math .Floor (float64 (len (l .latencies )) * 0.99 ))
77+ if p99Index >= len (l .latencies ) {
78+ p99Index = len (l .latencies ) - 1
79+ }
80+
81+ p99 := l .latencies [p99Index ]
82+
83+ return avg , p99
84+ }
85+
4186type benchStats struct {
4287 ok atomic.Uint64
4388 error atomic.Uint64
@@ -52,6 +97,7 @@ func runConnection(
5297 msg []byte ,
5398 opts []client.CallOption ,
5499 stats * benchStats ,
100+ latencyStats * latencyStats ,
55101) {
56102 defer wg .Done ()
57103
@@ -63,6 +109,7 @@ func runConnection(
63109 case <- ctx .Done ():
64110 return
65111 default :
112+ start := time .Now ()
66113 // Run the query
67114 resp , err := client .Echo (
68115 ctx ,
@@ -88,6 +135,7 @@ func runConnection(
88135 continue
89136 }
90137
138+ latencyStats .add (time .Since (start ))
91139 stats .ok .Add (1 )
92140 }
93141 }
@@ -113,10 +161,11 @@ func runBenchmark(
113161 logger log.Logger ,
114162 msg []byte ,
115163 opts []client.CallOption ,
116- ) (uint64 , uint64 ) {
164+ ) (uint64 , uint64 , time. Duration , time. Duration ) {
117165 var wg sync.WaitGroup
118166
119167 stats := & benchStats {}
168+ latencyStats := & latencyStats {latencies : make ([]time.Duration , 0 , 2000000 )}
120169
121170 // Create benchmark context with timeout
122171 benchCtx , cancel := context .WithTimeout (ctx , time .Duration (duration )* time .Second )
@@ -126,7 +175,7 @@ func runBenchmark(
126175 for i := 0 ; i < connections ; i ++ {
127176 wg .Add (1 )
128177
129- go runConnection (benchCtx , & wg , cli , logger , msg , opts , stats )
178+ go runConnection (benchCtx , & wg , cli , logger , msg , opts , stats , latencyStats )
130179 }
131180
132181 // Wait for timeout or cancellation
@@ -135,11 +184,13 @@ func runBenchmark(
135184 // Wait for all goroutines to finish
136185 wg .Wait ()
137186
138- return stats .ok .Load (), stats .error .Load ()
187+ avgLatency , p99Latency := latencyStats .calculate ()
188+ return stats .ok .Load (), stats .error .Load (), avgLatency , p99Latency
139189}
140190
141191// bench is the main benchmark function.
142192func bench (ctx context.Context , cfg * clientConfig , logger log.Logger , cli client.Type ) error {
193+
143194 // Log configuration
144195 logger .Info ("Configuration" ,
145196 "connections" , cfg .Connections ,
@@ -168,18 +219,20 @@ func bench(ctx context.Context, cfg *clientConfig, logger log.Logger, cli client
168219
169220 // Run warmup phase
170221 logger .Info ("Warming up..." )
171- warmupOk , warmupErr := runBenchmark (ctx , 5 , cfg .Connections , cli , logger , msg , opts )
222+ warmupOk , warmupErr , _ , _ := runBenchmark (ctx , 5 , cfg .Connections , cli , logger , msg , opts )
172223 logger .Debug ("Warmup complete" , "requests_ok" , warmupOk , "requests_error" , warmupErr )
173224
174225 // Run benchmark phase
175226 logger .Info ("Running benchmark..." )
176- reqsOk , reqsError := runBenchmark (ctx , cfg .Duration , cfg .Connections , cli , logger , msg , opts )
227+ reqsOk , reqsError , avg , p99 := runBenchmark (ctx , cfg .Duration , cfg .Connections , cli , logger , msg , opts )
177228
178229 // Log results
179230 logger .Info ("Summary" ,
180231 "requests_ok" , reqsOk ,
181232 "requests_error" , reqsError ,
182233 "qps" , float64 (reqsOk )/ float64 (cfg .Duration ),
234+ "avg_latency_us" , avg .Microseconds (),
235+ "p99_latency_us" , p99 .Microseconds (),
183236 "connections" , cfg .Connections ,
184237 "duration_seconds" , cfg .Duration ,
185238 "package_size" , cfg .PackageSize ,
0 commit comments