Files
NixMsg/test/load/stats.go

96 lines
2.1 KiB
Go

package load
import (
"fmt"
"math"
"sort"
"strings"
)
// Percentiles 是毫秒延迟分位。
type Percentiles struct {
P50 float64
P95 float64
P99 float64
N int
}
func calcPercentiles(ms []float64) Percentiles {
out := Percentiles{N: len(ms)}
if len(ms) == 0 {
return out
}
cp := append([]float64(nil), ms...)
sort.Float64s(cp)
out.P50 = percentileAt(cp, 0.50)
out.P95 = percentileAt(cp, 0.95)
out.P99 = percentileAt(cp, 0.99)
return out
}
func percentileAt(sorted []float64, p float64) float64 {
n := len(sorted)
if n == 1 {
return sorted[0]
}
idx := int(math.Ceil(p*float64(n))) - 1
if idx < 0 {
idx = 0
}
if idx >= n {
idx = n - 1
}
return sorted[idx]
}
// Report 是一次压测输出:提交/送达分位、错误码分布、掉线数。
type Report struct {
Connections int
Alive int
Disconnects int
Sends int
ActualRate float64
SubmitMs Percentiles
DeliverMs Percentiles
Errors map[string]int
GroupMembers int
GroupQueueMs float64
}
func (r Report) Format() string {
var b strings.Builder
fmt.Fprintf(&b, "connections: %d alive: %d disconnects: %d\n", r.Connections, r.Alive, r.Disconnects)
fmt.Fprintf(&b, "sends: %d", r.Sends)
if r.ActualRate > 0 {
fmt.Fprintf(&b, " actual_rate: %.2f/s", r.ActualRate)
}
b.WriteByte('\n')
fmt.Fprintf(&b, "submit_ms: n=%d p50=%.1f p95=%.1f p99=%.1f\n", r.SubmitMs.N, r.SubmitMs.P50, r.SubmitMs.P95, r.SubmitMs.P99)
fmt.Fprintf(&b, "deliver_ms: n=%d p50=%.1f p95=%.1f p99=%.1f\n", r.DeliverMs.N, r.DeliverMs.P50, r.DeliverMs.P95, r.DeliverMs.P99)
if r.GroupMembers > 0 {
fmt.Fprintf(&b, "group: members=%d queue_ms=%.1f\n", r.GroupMembers, r.GroupQueueMs)
}
if len(r.Errors) == 0 {
b.WriteString("errors: (none)\n")
} else {
b.WriteString("errors:")
keys := make([]string, 0, len(r.Errors))
for k := range r.Errors {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(&b, " %s=%d", k, r.Errors[k])
}
b.WriteByte('\n')
}
return b.String()
}
func addError(m map[string]int, code string) {
if code == "" {
code = "error"
}
m[code]++
}