package main
import (
"encoding/json"
"fmt"
"math/rand"
"os"
"path/filepath"
"sort"
)
type MetricComparison struct {
Metric string `json:"metric"`
BaselineMean float64 `json:"baseline_mean"`
CandidateMean float64 `json:"candidate_mean"`
Ratio float64 `json:"candidate_over_baseline"`
Low float64 `json:"difference_ci_low"`
High float64 `json:"difference_ci_high"`
Pass bool `json:"pass"`
Note string `json:"note,omitempty"`
}
type WorkloadComparison struct {
Workload string `json:"workload"`
Concurrency int `json:"concurrency"`
Pass bool `json:"pass"`
Metrics []MetricComparison `json:"metrics"`
}
type Comparison struct {
Baseline string `json:"baseline"`
Candidate string `json:"candidate"`
Pass bool `json:"pass"`
Workloads []WorkloadComparison `json:"workloads"`
}
func mean(v []float64) float64 {
sum := 0.
for _, x := range v {
sum += x
}
return sum / float64(len(v))
}
func bootstrap(a, b []float64) (float64, float64) {
rng := rand.New(rand.NewSource(42))
values := make([]float64, 10000)
for i := range values {
av, bv := 0., 0.
for range a {
av += a[rng.Intn(len(a))]
}
for range b {
bv += b[rng.Intn(len(b))]
}
values[i] = bv/float64(len(b)) - av/float64(len(a))
}
sort.Float64s(values)
return values[250], values[9749]
}
func compareFiles(baseline, candidate, output, filter string) error {
load := func(path string) (BenchReport, error) {
raw, err := os.ReadFile(path)
if err != nil {
return BenchReport{}, err
}
var report BenchReport
err = json.Unmarshal(raw, &report)
return report, err
}
a, err := load(baseline)
if err != nil {
return err
}
b, err := load(candidate)
if err != nil {
return err
}
if a.Dataset != b.Dataset {
return fmt.Errorf("dataset mismatch")
}
if a.Preparation != b.Preparation {
return fmt.Errorf("database preparation mismatch")
}
am, bm := map[string][]Sample{}, map[string][]Sample{}
for _, s := range a.Samples {
key := fmt.Sprintf("%s/%d", s.Workload, s.Concurrency)
am[key] = append(am[key], s)
}
for _, s := range b.Samples {
key := fmt.Sprintf("%s/%d", s.Workload, s.Concurrency)
bm[key] = append(bm[key], s)
}
keys := []string{}
for k := range am {
keys = append(keys, k)
}
sort.Strings(keys)
result := Comparison{Baseline: baseline, Candidate: candidate, Pass: true, Workloads: []WorkloadComparison{}}
metrics := []struct {
name string
larger bool
extract func(Sample) float64
}{
{"p50_ms", false, func(s Sample) float64 { return s.P50MS }},
{"p95_ms", false, func(s Sample) float64 { return s.P95MS }},
{"rps", true, func(s Sample) float64 { return s.RPS }},
{"cpu_ms_per_request", false, func(s Sample) float64 {
if s.Requests == s.Errors {
return 0
}
return float64(s.After.CPUTicks-s.Before.CPUTicks) * 10 / float64(s.Requests-s.Errors)
}},
{"peak_pss_kb", false, func(s Sample) float64 { return float64(s.Peak.PSSKB) }},
{"peak_rss_kb", false, func(s Sample) float64 { return float64(s.Peak.RSSKB) }},
}
for _, key := range keys {
x, y := am[key], bm[key]
if filter != "" && x[0].Workload != filter {
continue
}
if len(x) != len(y) || len(x) < 5 {
return fmt.Errorf("need matching >=5 repetitions for %s", key)
}
w := WorkloadComparison{Workload: x[0].Workload, Concurrency: x[0].Concurrency, Pass: true, Metrics: []MetricComparison{}}
for i := range x {
if x[i].Repeat != y[i].Repeat || x[i].Concurrency != y[i].Concurrency {
return fmt.Errorf("repetition mismatch %s", key)
}
if x[i].Requests != y[i].Requests {
return fmt.Errorf("request count mismatch %s", key)
}
if x[i].Errors != 0 || y[i].Errors != 0 {
w.Pass = false
}
}
for _, metric := range metrics {
av, bv := []float64{}, []float64{}
for i := range x {
av = append(av, metric.extract(x[i]))
bv = append(bv, metric.extract(y[i]))
}
lo, hi := bootstrap(av, bv)
m := MetricComparison{Metric: metric.name, BaselineMean: mean(av), CandidateMean: mean(bv), Low: lo, High: hi, Pass: hi <= 0}
if m.BaselineMean != 0 {
m.Ratio = m.CandidateMean / m.BaselineMean
}
if metric.larger {
m.Pass = lo >= 0
}
if metric.name == "cpu_ms_per_request" && (m.BaselineMean == 0 || m.CandidateMean == 0) {
m.Pass = false
m.Note = "CPU tick resolution insufficient; increase request count"
}
if x[0].Requests < 20 && (metric.name == "p50_ms" || metric.name == "p95_ms") {
m.Note = "low-sample full-response duration; not a reliable tail estimate"
}
w.Pass = w.Pass && m.Pass
w.Metrics = append(w.Metrics, m)
}
result.Pass = result.Pass && w.Pass
result.Workloads = append(result.Workloads, w)
fmt.Printf("%s pass=%v\n", key, w.Pass)
}
if len(bm) != len(am) {
return fmt.Errorf("candidate workload set differs")
}
if len(result.Workloads) == 0 {
return fmt.Errorf("no matching workload")
}
if err = os.MkdirAll(filepath.Dir(output), 0755); err != nil {
return err
}
raw, _ := json.MarshalIndent(result, "", " ")
if err = os.WriteFile(output, append(raw, '\n'), 0644); err != nil {
return err
}
if !result.Pass {
return fmt.Errorf("comparison contains failing or inconclusive gates; see %s", output)
}
return nil
}