From e9cea86f4459fa4f7fcb6565133060d8044b089d Mon Sep 17 00:00:00 2001 From: manasachinta Date: Tue, 9 Jan 2024 18:10:32 -0500 Subject: [PATCH] *: types refactor modified response structs, removed percentileLatencies test cases --- .gitignore | 2 +- api/types/metric.go | 20 +++++++----- cmd/kperf/commands/runner/runner.go | 35 +++------------------ metrics/request.go | 35 ++++++--------------- metrics/request_test.go | 48 ----------------------------- request/schedule.go | 23 +++++++++----- 6 files changed, 43 insertions(+), 120 deletions(-) delete mode 100644 metrics/request_test.go diff --git a/.gitignore b/.gitignore index 760fb97..5610a22 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,4 @@ result/ tmp/ #.txt files which contain response stats -.txt +*.txt diff --git a/api/types/metric.go b/api/types/metric.go index 9ec36e5..97b21f3 100644 --- a/api/types/metric.go +++ b/api/types/metric.go @@ -4,19 +4,25 @@ import "time" // ResponseStats is the report about benchmark result. type ResponseStats struct { + // List of failures + FailureList []error + // All the observed latencies + Latencies []float64 + // total bytes read from apiserver + TotalReceivedBytes int64 +} + +type RunnerMetricReport struct { // Total represents total number of requests. Total int // List of failures FailureList []error // Duration means the time of benchmark. Duration time.Duration - // PercentileLatencies represents the latency distribution in seconds. - // - // NOTE: The key represents quantile. - PercentileLatencies map[float64]float64 + // All the observed latencies + Latencies []float64 // total bytes read from apiserver TotalReceivedBytes int64 - // TODO: - // 1. Support failures partitioned by http code and verb - // 2. Support to dump all latency data + // PercentileLatencies represents the latency distribution in seconds. + PercentileLatencies [][2]float64 // [2]float64{percentile, value} } diff --git a/cmd/kperf/commands/runner/runner.go b/cmd/kperf/commands/runner/runner.go index ee8f3de..52acd09 100644 --- a/cmd/kperf/commands/runner/runner.go +++ b/cmd/kperf/commands/runner/runner.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "sort" "github.com/Azure/kperf/api/types" "github.com/Azure/kperf/request" @@ -111,6 +110,8 @@ var runCommand = cli.Command{ } defer f.Close() } + + //TODO: add printResponseStats for .json format printResponseStats(f, stats) return nil }, @@ -151,33 +152,7 @@ func loadConfig(cliCtx *cli.Context) (*types.LoadProfile, error) { return &profileCfg, nil } -func printResponseStats(f *os.File, stats *types.ResponseStats) { - fmt.Fprint(f, "Response Stat: \n") - fmt.Fprintf(f, " Total: %v\n", stats.Total) - - fmt.Fprintf(f, " Total Failures: %d\n", len(stats.FailureList)) - - for _, v := range stats.FailureList { - fmt.Fprintf(f, " %v\n", v) - } - - fmt.Fprintf(f, " Observed Bytes: %v\n", stats.TotalReceivedBytes) - - fmt.Fprintf(f, " Duration: %v\n", stats.Duration.String()) - - requestsPerSec := float64(stats.Total) / stats.Duration.Seconds() - - fmt.Fprintf(f, " Requests/sec: %.2f\n", requestsPerSec) - - fmt.Fprint(f, " Latency Distribution:\n") - keys := make([]float64, 0, len(stats.PercentileLatencies)) - for q := range stats.PercentileLatencies { - keys = append(keys, q) - } - - sort.Float64s(keys) - - for _, q := range keys { - fmt.Fprintf(f, " [%.2f] %.3fs\n", q/100.0, stats.PercentileLatencies[q]) - } +// TODO: Complete this function +func printResponseStats(f *os.File, stats *request.Result) { + fmt.Fprintf(f, "Response Stat: %v\n", stats) } diff --git a/metrics/request.go b/metrics/request.go index d049416..6485304 100644 --- a/metrics/request.go +++ b/metrics/request.go @@ -2,10 +2,10 @@ package metrics import ( "container/list" - "math" - "sort" "sync" "sync/atomic" + + "github.com/Azure/kperf/api/types" ) // ResponseMetric is a measurement related to http response. @@ -17,7 +17,7 @@ type ResponseMetric interface { // ObserveReceivedBytes observes the bytes read from apiserver. ObserveReceivedBytes(bytes int64) // Gather returns the summary. - Gather() (latencies []float64, percentileLatencies map[float64]float64, failureList []error, bytes int64) + Gather() types.ResponseStats } type responseMetricImpl struct { @@ -55,9 +55,13 @@ func (m *responseMetricImpl) ObserveReceivedBytes(bytes int64) { } // Gather implements ResponseMetric. -func (m *responseMetricImpl) Gather() ([]float64, map[float64]float64, []error, int64) { +func (m *responseMetricImpl) Gather() types.ResponseStats { latencies := m.dumpLatencies() - return latencies, buildPercentileLatencies(latencies), m.failureList, atomic.LoadInt64(&m.receivedBytes) + return types.ResponseStats{ + FailureList: m.failureList, + Latencies: latencies, + TotalReceivedBytes: atomic.LoadInt64(&m.receivedBytes), + } } func (m *responseMetricImpl) dumpLatencies() []float64 { @@ -69,24 +73,3 @@ func (m *responseMetricImpl) dumpLatencies() []float64 { } return res } - -var percentiles = []float64{0, 50, 90, 95, 99, 100} - -func buildPercentileLatencies(latencies []float64) map[float64]float64 { - if len(latencies) == 0 { - return nil - } - - res := make(map[float64]float64, len(percentiles)) - - n := len(latencies) - sort.Float64s(latencies) - for _, p := range percentiles { - idx := int(math.Ceil(float64(n) * p / 100)) - if idx > 0 { - idx-- - } - res[p] = latencies[idx] - } - return res -} diff --git a/metrics/request_test.go b/metrics/request_test.go deleted file mode 100644 index eb4ad27..0000000 --- a/metrics/request_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package metrics - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBuildPercentileLatencies(t *testing.T) { - ls := make([]float64, 100) - ls[0] = 50 - ls[1] = 49 - ls[2] = 1 - res := buildPercentileLatencies(ls) - assert.Equal(t, float64(0), res[0]) - assert.Equal(t, float64(0), res[50]) - assert.Equal(t, float64(0), res[90]) - assert.Equal(t, float64(0), res[95]) - assert.Equal(t, float64(49), res[99]) - assert.Equal(t, float64(50), res[100]) - - ls = make([]float64, 1000) - ls[0] = 50 - ls[1] = 49 - ls[2] = -1 - res = buildPercentileLatencies(ls) - assert.Equal(t, float64(-1), res[0]) - assert.Equal(t, float64(0), res[50]) - assert.Equal(t, float64(0), res[90]) - assert.Equal(t, float64(0), res[95]) - assert.Equal(t, float64(0), res[99]) - assert.Equal(t, float64(50), res[100]) -} - -func TestResponseMetric(t *testing.T) { - c := NewResponseMetric() - for i := 100; i > 0; i-- { - c.ObserveLatency(float64(i)) - } - - _, res, _, _ := c.Gather() - assert.Equal(t, float64(1), res[0]) - assert.Equal(t, float64(50), res[50]) - assert.Equal(t, float64(90), res[90]) - assert.Equal(t, float64(95), res[95]) - assert.Equal(t, float64(99), res[99]) - assert.Equal(t, float64(100), res[100]) -} diff --git a/request/schedule.go b/request/schedule.go index 48a1862..64075ff 100644 --- a/request/schedule.go +++ b/request/schedule.go @@ -16,8 +16,17 @@ import ( const defaultTimeout = 60 * time.Second +// Result contains responseStats vlaues from Gather() and adds Duration and Total values separately +type Result struct { + types.ResponseStats + // Duration means the time of benchmark. + Duration time.Duration + // Total means the total number of requests. + Total int +} + // Schedule files requests to apiserver based on LoadProfileSpec. -func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.Interface) (*types.ResponseStats, error) { +func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.Interface) (*Result, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -81,12 +90,10 @@ func Schedule(ctx context.Context, spec *types.LoadProfileSpec, restCli []rest.I wg.Wait() totalDuration := time.Since(start) - _, percentileLatencies, failureList, bytes := respMetric.Gather() - return &types.ResponseStats{ - Total: spec.Total, - FailureList: failureList, - Duration: totalDuration, - TotalReceivedBytes: bytes, - PercentileLatencies: percentileLatencies, + responseStats := respMetric.Gather() + return &Result{ + ResponseStats: responseStats, + Duration: totalDuration, + Total: spec.Total, }, nil }