From 77be8b71d99a1a3c5dbac54565de79bd38ef79c3 Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Fri, 14 Jul 2023 10:43:56 -0700 Subject: [PATCH] Improve splitting by operation speed (#277) Use non-copying splitting for faster compare and merging. Add error comparison --- cli/cmp.go | 14 ++++++---- cli/merge.go | 2 +- pkg/bench/analyze_test.go | 2 +- pkg/bench/ops.go | 54 +++++++++++++++++++++++++++++++++++---- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/cli/cmp.go b/cli/cmp.go index 7beb07a1..2f84898c 100644 --- a/cli/cmp.go +++ b/cli/cmp.go @@ -98,15 +98,15 @@ func printCompare(ctx *cli.Context, before, after bench.Operations) { start, end := ops.ActiveTimeRange(!isMultiOp) return end.Sub(start).Round(time.Second) } - - for _, typ := range before.OpTypes() { + afterOps := after.SortSplitByOpType() + for typ, before := range before.SortSplitByOpType() { if wantOp := ctx.String("analyze.op"); wantOp != "" { if wantOp != typ { continue } } - before := before.FilterByOp(typ) - after := after.FilterByOp(typ) + + after := afterOps[typ] console.Println("-------------------") console.SetColor("Print", color.New(color.FgHiWhite)) console.Println("Operation:", typ) @@ -117,7 +117,11 @@ func printCompare(ctx *cli.Context, before, after bench.Operations) { console.Println(err) continue } - + if bErrs, aErrs := before.NErrors(), after.NErrors(); bErrs+aErrs > 0 { + console.SetColor("Print", color.New(color.FgHiRed)) + console.Println("Errors:", bErrs, "->", aErrs) + console.SetColor("Print", color.New(color.FgWhite)) + } if len(before) != len(after) { console.Println("Operations:", len(before), "->", len(after)) } diff --git a/cli/merge.go b/cli/merge.go index 658f3165..265d3836 100644 --- a/cli/merge.go +++ b/cli/merge.go @@ -109,7 +109,7 @@ func mainMerge(ctx *cli.Context) error { }() } } - for typ, ops := range allOps.ByOp() { + for typ, ops := range allOps.SortSplitByOpType() { start, end := ops.ActiveTimeRange(true) if !start.Before(end) { console.Errorf("Type %v contains no overlapping items", typ) diff --git a/pkg/bench/analyze_test.go b/pkg/bench/analyze_test.go index aea5582b..e88e6c39 100644 --- a/pkg/bench/analyze_test.go +++ b/pkg/bench/analyze_test.go @@ -41,7 +41,7 @@ func TestOperations_Segment(t *testing.T) { if err != nil { t.Fatal(err) } - for typ, ops := range ops.ByOp() { + for typ, ops := range ops.SortSplitByOpType() { segs := ops.Segment(SegmentOptions{ From: time.Time{}, PerSegDuration: time.Second, diff --git a/pkg/bench/ops.go b/pkg/bench/ops.go index 4c5f9114..a6fce676 100644 --- a/pkg/bench/ops.go +++ b/pkg/bench/ops.go @@ -222,6 +222,25 @@ func (o Operations) SortByEndpoint() { }) } +// SortByOpType will sort the operations by operation type. +// Earliest operations first. +func (o Operations) SortByOpType() { + if sort.SliceIsSorted(o, func(i, j int) bool { + if o[i].OpType == o[j].OpType { + return o[i].Start.Before(o[j].Start) + } + return o[i].OpType < o[j].OpType + }) { + return + } + sort.Slice(o, func(i, j int) bool { + if o[i].OpType == o[j].OpType { + return o[i].Start.Before(o[j].Start) + } + return o[i].OpType < o[j].OpType + }) +} + // SortByDuration will sort the operations by duration taken to complete. // Fastest operations first. func (o Operations) SortByDuration() { @@ -347,12 +366,26 @@ func (o Operations) SortSplitByEndpoint() map[string]Operations { return dst } -// ByOp separates the operations by op. -func (o Operations) ByOp() map[string]Operations { - dst := make(map[string]Operations, 1) - for _, o := range o { - dst[o.OpType] = append(dst[o.OpType], o) +// SortSplitByOpType will sort operations by op + start time and split by op. +func (o Operations) SortSplitByOpType() map[string]Operations { + o.SortByOpType() + dst := make(map[string]Operations, 5) + lastOp := "" + start := 0 + for i, op := range o { + if op.OpType == lastOp { + continue + } + if lastOp != "" { + dst[lastOp] = o[start:i] + } + lastOp = op.OpType + start = i + } + if lastOp != "" { + dst[lastOp] = o[start:] } + return dst } @@ -829,6 +862,17 @@ func (o Operations) Errors() []string { return errs } +// NErrors returns the number of errors found. +func (o Operations) NErrors() int { + var n int + for _, op := range o { + if len(op.Err) != 0 { + n++ + } + } + return n +} + // FilterSuccessful returns the successful requests. func (o Operations) FilterSuccessful() Operations { if len(o) == 0 {