-
Notifications
You must be signed in to change notification settings - Fork 70
DRIVERS-3254: Add perfcomp to drivers-evergreen-tools #677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
817285f
Set up for perfcomp build from DET
zhouselena 826490d
Ignore bin and temp files created by perfcomp
zhouselena 508e17b
Perf analysis compare logic
zhouselena 33e3c7f
Parse perf txt to md report
zhouselena 8805889
Project-generalized perf comp CLI ported over from Go Driver
zhouselena 26597c1
Add README for usage and instructions
zhouselena 4c02876
Rename functions and add comments for clarity
zhouselena 0338c9e
Add testing for compare.go
zhouselena e8e535f
Error messages should be lowercase
zhouselena a4e766d
Define context in caller runCompare
zhouselena 1330563
Rename connection string
zhouselena abaa20c
Fix create comment for no stat sig benchmarks
zhouselena b6bff32
Update README to remind rerun build.sh
zhouselena 0569078
Shell script builds and runs perfcomp
zhouselena 4f0d903
Project flag must be mandatory
zhouselena 82f1dc9
Propogate errors and other fixes
zhouselena 141a568
Cleanup go version
zhouselena 73ca146
Cleanup
zhouselena 876925c
Update triage context instructions
zhouselena 837ef5e
Add compare options for context,project,task,variant,version
zhouselena bea9582
Update shell script with required flags
zhouselena 1dac817
Update README with flag behaviour
zhouselena 48ed412
Update flags
zhouselena 463eb92
Fix validation
zhouselena 862a167
Update go.mod
zhouselena File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# perfcomp | ||
|
||
**perfcomp** is a performance analyzer on a PR commit basis. | ||
|
||
## 📦 Installation | ||
|
||
To install the latest version: | ||
|
||
```bash | ||
go install github.com/mongodb-labs/drivers-evergreen-tools/perfcomp/cmd/perfcomp@latest | ||
``` | ||
|
||
Or build it locally in `bin/perfcomp`: | ||
|
||
```bash | ||
bash build.sh | ||
``` | ||
|
||
## 🔧 Usage | ||
|
||
### Parameters | ||
|
||
To use `perfcomp`, you should have an analytics node URI env variable called `PERF_URI_PRIVATE_ENDPOINT`. You can request for it from the devprod performance team. | ||
|
||
To run in your project repository, you need to create a [performance context](https://performance-monitoring-and-analysis.server-tig.prod.corp.mongodb.com/contexts) that captures all benchmarks in your project. This needs to be a triage context. Feel free to refer to the [Go Driver context](https://performance-monitoring-and-analysis.server-tig.prod.corp.mongodb.com/context/name/GoDriver%20perf%20task) as a template. | ||
|
||
> _If you are creating a triage context for the first time, it may take a few hours for your project's data to be tagged._ | ||
|
||
You also need the name of the performance task and variant specific to your project. You can do a query in the analytics node `raw_results` collection: | ||
|
||
``` | ||
db.raw_results.find({ | ||
“info.project”: “<project>”, | ||
“info.version”: “<random_evergreen_version>" | ||
}) | ||
``` | ||
|
||
and look for the `variant` and `task_name` properties. | ||
|
||
### perfcomp CLI | ||
|
||
```bash | ||
perfcomp is a cli that reports stat-sig results between evergreen patches with the mainline commit | ||
|
||
Usage: | ||
perfcomp [command] | ||
|
||
Available Commands: | ||
compare compare evergreen patch to mainline commit | ||
mdreport generates markdown output after run | ||
``` | ||
|
||
### Commands | ||
|
||
#### compare | ||
|
||
```bash | ||
compare evergreen patch to mainline commit | ||
|
||
Usage: | ||
perfcomp compare [version_id] [flags] | ||
|
||
Flags: | ||
--context string specify the performance triage context, ex. "GoDriver perf task" (required) | ||
--project string specify the name of an existing Evergreen project, ex. "mongo-go-driver" (required) | ||
--task string specify the evergreen perf task name, ex. "perf" (required) | ||
--variant string specify the perf task variant, ex. "perf" (required) | ||
``` | ||
|
||
#### mdreport | ||
|
||
```bash | ||
generates markdown output after compare run (must be run after `compare`) | ||
|
||
Usage: | ||
perfcomp mdreport | ||
``` | ||
|
||
### Run via shell script | ||
|
||
Alternatively, you can run the perfcomp shell script. This script will run build and then run `compare`. From the root directory, | ||
|
||
```bash | ||
PERF_URI_PRIVATE_ENDPOINT="<perf_uri>" VERSION_ID="<version>" PROJECT="<project>" CONTEXT="<context>" TASK="<task>" VARIANT="<variant>" .evergreen/run-perf-comp.sh | ||
``` | ||
|
||
If you would like to see a markdown preview of the report, you can also pass in `HEAD_SHA=""`. This will generate `.evergreen/perfcomp/perf-report.md`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/usr/bin/env bash | ||
set -euo pipefail | ||
|
||
BIN_DIR="bin" | ||
mkdir -p $BIN_DIR | ||
go build -o $BIN_DIR/perfcomp ./cmd/perfcomp/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"math" | ||
"os" | ||
"sort" | ||
"strings" | ||
"text/tabwriter" | ||
"time" | ||
|
||
"github.com/mongodb-labs/drivers-evergreen-tools/perfcomp" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func newCompareCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "compare", | ||
Short: "compare evergreen patch to mainline commit", | ||
// Version id is a required argument | ||
Args: func(cmd *cobra.Command, args []string) error { | ||
if len(args) < 1 { | ||
return fmt.Errorf("this command requires an evergreen patch version ID") | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().String("project", "", "specify the name of an existing Evergreen project, ex. \"mongo-go-driver\"") | ||
cmd.Flags().String("task", "", "specify the evergreen perf task name, ex. \"perf\"") | ||
cmd.Flags().String("variant", "", "specify the perf task variant, ex. \"perf\"") | ||
cmd.Flags().String("context", "", "specify the performance triage context, ex. \"GoDriver perf task\"") | ||
|
||
for _, flag := range []string{"project", "task", "variant", "context"} { | ||
cmd.MarkFlagRequired(flag) | ||
} | ||
|
||
cmd.Run = func(cmd *cobra.Command, args []string) { | ||
// Check for variables | ||
uri := os.Getenv("PERF_URI_PRIVATE_ENDPOINT") | ||
if uri == "" { | ||
log.Fatal("PERF_URI_PRIVATE_ENDPOINT env variable is not set") | ||
} | ||
|
||
// Retrieve and validate flag values | ||
zhouselena marked this conversation as resolved.
Show resolved
Hide resolved
|
||
project, err := cmd.Flags().GetString("project") | ||
if err != nil { | ||
log.Fatalf("failed to get project flag: %v", err) | ||
} | ||
task, err := cmd.Flags().GetString("task") | ||
if err != nil { | ||
log.Fatalf("failed to get task flag: %v", err) | ||
} | ||
variant, err := cmd.Flags().GetString("variant") | ||
if err != nil { | ||
log.Fatalf("failed to get variant flag: %v", err) | ||
} | ||
context, err := cmd.Flags().GetString("context") | ||
if err != nil { | ||
log.Fatalf("failed to get context flag: %v", err) | ||
} | ||
|
||
// Validate all flags | ||
for _, flag := range []string{project, task, variant, context} { | ||
if flag == "" { | ||
log.Fatalf("must provide %s", flag) | ||
} | ||
} | ||
|
||
// Run compare function | ||
if err := runCompare(cmd, args, project, task, variant, context); err != nil { | ||
zhouselena marked this conversation as resolved.
Show resolved
Hide resolved
|
||
log.Fatalf("failed to compare: %v", err) | ||
} | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func createComment(result perfcomp.CompareResult) string { | ||
var comment strings.Builder | ||
|
||
if len(result.SigEnergyStats) == 0 { | ||
comment.Reset() | ||
fmt.Fprintf(&comment, "There were no significant changes to the performance to report for version %s.\n", result.Version) | ||
} else { | ||
fmt.Fprintf(&comment, "The following benchmark tests for version %s had statistically significant changes (i.e., |z-score| > 1.96):\n\n", result.Version) | ||
|
||
w := tabwriter.NewWriter(&comment, 0, 0, 1, ' ', 0) | ||
|
||
fmt.Fprintln(w, "| Benchmark\t| Measurement\t| % Change\t| Patch Value\t| Stable Region\t| H-Score\t| Z-Score\t| ") | ||
fmt.Fprintln(w, "| ---------\t| -----------\t| --------\t| -----------\t| -------------\t| -------\t| -------\t|") | ||
|
||
sort.Slice(result.SigEnergyStats, func(i, j int) bool { | ||
return math.Abs(result.SigEnergyStats[i].PercentChange) > math.Abs(result.SigEnergyStats[j].PercentChange) | ||
}) | ||
for _, es := range result.SigEnergyStats { | ||
fmt.Fprintf(w, "| %s\t| %s\t| %.4f\t| %.4f\t| Avg: %.4f, Med: %.4f, Stdev: %.4f\t| %.4f\t| %.4f\t|\n", es.Benchmark, es.Measurement, es.PercentChange, es.MeasurementVal, es.StableRegion.Mean, es.StableRegion.Median, es.StableRegion.Std, es.HScore, es.ZScore) | ||
zhouselena marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
w.Flush() | ||
} | ||
|
||
comment.WriteString("\n*For a comprehensive view of all microbenchmark results for this PR's commit, please check out the Evergreen perf task for this patch.*") | ||
return comment.String() | ||
|
||
} | ||
|
||
func runCompare(cmd *cobra.Command, args []string, project string, taskName string, variant string, perfContext string) error { | ||
perfAnalyticsConnString := os.Getenv("PERF_URI_PRIVATE_ENDPOINT") | ||
version := args[len(args)-1] | ||
|
||
ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) | ||
defer cancel() | ||
|
||
res, err := perfcomp.Compare(ctx, perfAnalyticsConnString, | ||
perfcomp.WithVersion(version), | ||
perfcomp.WithProject(project), | ||
perfcomp.WithTask(taskName), | ||
perfcomp.WithVariant(variant), | ||
perfcomp.WithContext(perfContext), | ||
) | ||
if err != nil { | ||
log.Fatalf("failed to compare: %v", err) | ||
} | ||
|
||
res.CommitSHA = os.Getenv("HEAD_SHA") | ||
res.MainlineCommit = os.Getenv("BASE_SHA") | ||
|
||
prComment := createComment(*res) | ||
log.Println("🧪 Performance Results") | ||
log.Println(prComment) | ||
|
||
if res.CommitSHA != "" { | ||
// Write results to .txt file to parse into markdown comment | ||
fWrite, err := os.Create(perfReportFileTxt) | ||
if err != nil { | ||
log.Fatalf("Could not create %s: %v", perfReportFileTxt, err) | ||
} | ||
defer fWrite.Close() | ||
|
||
fmt.Fprintf(fWrite, "Version ID: %s\n", version) | ||
fmt.Fprintf(fWrite, "Commit SHA: %s\n", res.CommitSHA) | ||
fmt.Fprintln(fWrite, prComment) | ||
log.Printf("PR commit %s: saved to %s for markdown comment.\n", res.CommitSHA, perfReportFileTxt) | ||
} | ||
|
||
return nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
func main() { | ||
cmd := &cobra.Command{ | ||
Use: "perfcomp", | ||
Short: "perfcomp is a cli that reports stat-sig results between evergreen patches with the mainline commit", | ||
Version: "0.0.0-alpha", | ||
} | ||
|
||
cmd.AddCommand(newCompareCommand()) | ||
cmd.AddCommand(newMdCommand()) | ||
|
||
if err := cmd.Execute(); err != nil { | ||
log.Fatalf("error: %v", err) | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.