diff --git a/cmd/perf/internal/common/common.go b/cmd/perf/internal/common/common.go index 808d9d8..c3cc4b4 100644 --- a/cmd/perf/internal/common/common.go +++ b/cmd/perf/internal/common/common.go @@ -112,10 +112,10 @@ func (r Result) Print() { log.Info(fmt.Sprintf("Peak %s: %f %s \n", r.Metric, r.Peak, r.MetricUnit)) } -func (r Result) PrintYaml(b *strings.Builder) { +func (r Result) PrintYaml(b *strings.Builder, yamlTag string) { if r.PodName != "" { b.WriteString(fmt.Sprintf("Pod: %s\n", r.PodName)) } - b.WriteString(fmt.Sprintf("Average%s: %f\n", r.Metric, r.Average)) - b.WriteString(fmt.Sprintf("Peak%s: %f\n", r.Metric, r.Peak)) + b.WriteString(fmt.Sprintf("average_%s: %f\n", yamlTag, r.Average)) + b.WriteString(fmt.Sprintf("peak_%s: %f\n", yamlTag, r.Peak)) } diff --git a/cmd/perf/internal/quantify.go b/cmd/perf/internal/quantify.go index 815644c..b919efa 100644 --- a/cmd/perf/internal/quantify.go +++ b/cmd/perf/internal/quantify.go @@ -163,9 +163,9 @@ func (o *QuantifyOptions) processPods(timeToReadinessResults []common.Result) er if o.yamlOutput { for _, timeToReadinessResult := range timeToReadinessResults { b := strings.Builder{} - timeToReadinessResult.PrintYaml(&b) - aggregatedMemoryResult.PrintYaml(&b) - aggregatedCPURateResult.PrintYaml(&b) + timeToReadinessResult.PrintYaml(&b, "time_to_readiness") + aggregatedMemoryResult.PrintYaml(&b, "memory") + aggregatedCPURateResult.PrintYaml(&b, "cpu") f, err := os.Create("results.yaml") if err != nil { diff --git a/cmd/perfcompare/main.go b/cmd/perfcompare/main.go new file mode 100644 index 0000000..33509c2 --- /dev/null +++ b/cmd/perfcompare/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "math" + "os" + + log "github.com/sirupsen/logrus" + "gopkg.in/alecthomas/kingpin.v2" + "gopkg.in/yaml.v2" +) + +type ApplicationPerformance struct { + AverageTimeToReadiness float64 `yaml:"average_time_to_readiness"` + PeakTimeToReadiness float64 `yaml:"peak_time_to_readiness"` + AverageMemory float64 `yaml:"average_memory"` + PeakMemory float64 `yaml:"peak_memory"` + AverageCPU float64 `yaml:"average_cpu"` + PeakCPU float64 `yaml:"peak_cpu"` +} + +// Compare two ApplicationPerformance structs and print the differences with a threshold check +func (ap ApplicationPerformance) Compare(newData ApplicationPerformance, threshold float64) { + log.Infoln("Comparing Performance:") + compareField("Average Time to Readiness", ap.AverageTimeToReadiness, newData.AverageTimeToReadiness, threshold) + compareField("Peak Time to Readiness", ap.PeakTimeToReadiness, newData.PeakTimeToReadiness, threshold) + compareField("Average Memory", ap.AverageMemory, newData.AverageMemory, threshold) + compareField("Peak Memory", ap.PeakMemory, newData.PeakMemory, threshold) + compareField("Average CPU", ap.AverageCPU, newData.AverageCPU, threshold) + compareField("Peak CPU", ap.PeakCPU, newData.PeakCPU, threshold) +} + +func compareField(fieldName string, oldData, newData float64, threshold float64) { + diff := newData - oldData + percentChange := diff / oldData * 100 + diff = math.Round(diff*100) / 100 + percentChange = math.Round(percentChange*100) / 100 + + // Print the comparison result with 2 decimal places + log.Infof("%s: Old Data = %.2f, New Data = %.2f, Difference = %.2f (%.2f%% change)\n", fieldName, oldData, newData, diff, percentChange) + + // Check if the percent change exceeds the threshold + if percentChange > threshold*100 { + log.Warnf("Attention: %s increased by more than %.0f%% -> %f\n", fieldName, threshold*100, percentChange) + } +} + +// Read YAML file and unmarshal into ApplicationPerformance struct +func readYAMLFile(filename string) (ApplicationPerformance, error) { + data, err := os.ReadFile(filename) + if err != nil { + return ApplicationPerformance{}, err + } + + var performance ApplicationPerformance + err = yaml.Unmarshal(data, &performance) + if err != nil { + return ApplicationPerformance{}, err + } + + return performance, nil +} + +func main() { + // Command-line argument parsing + app := kingpin.New("perf-compare", "A tool to compare application performance data from YAML files.") + old := app.Flag("old", "Path to the old results as YAML file.").Short('o').Required().String() + current := app.Flag("new", "Path to the new results as YAML file.").Short('n').Required().String() + threshold := app.Flag("threshold", "The threshold for the performance comparison like 0.10 for %10.").Short('t').Required().Float64() + + if _, err := app.Parse(os.Args[1:]); err != nil { + log.Fatal(err) + } + + // Read data from the YAML files + oldData, err := readYAMLFile(*old) + if err != nil { + log.Fatalf("Failed to read old data: %v", err) + } + + newData, err := readYAMLFile(*current) + if err != nil { + log.Fatalf("Failed to read new data: %v", err) + } + + // Compare the two datasets + oldData.Compare(newData, *threshold) +} diff --git a/cmd/perfcompare/testdata/newData.yaml b/cmd/perfcompare/testdata/newData.yaml new file mode 100644 index 0000000..7fb685f --- /dev/null +++ b/cmd/perfcompare/testdata/newData.yaml @@ -0,0 +1,6 @@ +average_time_to_readiness: 5.800000 +peak_time_to_readiness: 11.000000 +average_memory: 33800000.00000 +peak_memory: 44000000.000000 +average_cpu: 65.000000 +peak_cpu: 85.000000 \ No newline at end of file diff --git a/cmd/perfcompare/testdata/oldData.yaml b/cmd/perfcompare/testdata/oldData.yaml new file mode 100644 index 0000000..d4b222c --- /dev/null +++ b/cmd/perfcompare/testdata/oldData.yaml @@ -0,0 +1,6 @@ +average_time_to_readiness: 5.000000 +peak_time_to_readiness: 10.000000 +average_memory: 33000000.00000 +peak_memory: 40000000.000000 +average_cpu: 60.000000 +peak_cpu: 70.000000 \ No newline at end of file