Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,11 @@ functions:
type: test
params:
binary: bash
add_expansions_to_env: true
env:
VERSION_ID: ${version_id}
BASE_SHA: "${revision}"
HEAD_SHA: "${github_commit}"
include_expansions_in_env: [PERF_URI_PRIVATE_ENDPOINT]
args: [*task-runner, perf-pr-comment]

Expand Down Expand Up @@ -693,6 +696,7 @@ tasks:
params:
binary: bash
args: [*task-runner, driver-benchmark]
- func: assume-test-secrets-ec2-role
- func: send-perf-data
- func: send-perf-pr-comment

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ internal/cmd/compilecheck/compilecheck.so
api-report.md
api-report.txt

# Ignore perf report files
perf-report.md
perf-report.txt

# Ignore secrets files
secrets-expansion.yml
secrets-export.sh
Expand Down
16 changes: 15 additions & 1 deletion etc/perf-pr-comment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@

set -eux

# Generate perf report.
pushd ./internal/cmd/perfcomp >/dev/null || exist
GOWORK=off go run main.go --project="mongo-go-driver" ${VERSION_ID}
GOWORK=off go run main.go --project="mongo-go-driver" ${VERSION_ID} > ./parseperfcomp/perf-report.txt
popd >/dev/null

if [[ -n "${BASE_SHA+set}" && -n "${HEAD_SHA+set}" && "$BASE_SHA" != "$HEAD_SHA" ]]; then
# Parse and generate perf comparison comment.
go run ./internal/cmd/perfcomp/parseperfcomp/main.go
# Make the PR comment.
target=$DRIVERS_TOOLS/.evergreen/github_app/create_or_modify_comment.sh
bash $target -m "## 👋 GoDriver Performance" -c "$(pwd)/perf-report.md" -h $HEAD_SHA -o "mongodb" -n "mongo-go-driver"
else
# Skip comment if it isn't a PR run.
echo "Skipping Perf PR comment"
fi

rm ./internal/cmd/perfcomp/parseperfcomp/perf-report.txt
39 changes: 28 additions & 11 deletions internal/cmd/perfcomp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"log"
"math"
"os"
"sort"
"strings"
"text/tabwriter"
"time"
Expand Down Expand Up @@ -166,7 +167,19 @@ func main() {
if err != nil {
log.Fatalf("Error getting energy statistics: %v", err)
}
log.Println(generatePRComment(allEnergyStats, version))

// Log energy stats output
prComment := generatePRComment(allEnergyStats, version)
log.Println("👋 GoDriver Performance")
log.Println(prComment)

// Save for PR comment if it is a PR run
commitSHA := os.Getenv("HEAD_SHA")
if commitSHA != "" {
fmt.Printf("Version ID: %s\n", version)
fmt.Printf("Commit SHA: %s\n", commitSHA) // Use fmt to print to stdout
fmt.Println(prComment)
}
}

func findRawData(ctx context.Context, project string, version string, coll *mongo.Collection) ([]RawData, error) {
Expand Down Expand Up @@ -305,27 +318,31 @@ func getEnergyStatsForAllBenchMarks(ctx context.Context, patchRawData []RawData,

func generatePRComment(energyStats []*EnergyStats, version string) string {
var comment strings.Builder
comment.WriteString("# 👋GoDriver Performance\n")
fmt.Fprintf(&comment, "The following benchmark tests for version %s had statistically significant changes (i.e., |z-score| > 1.96):\n", version)
fmt.Fprintf(&comment, "The following benchmark tests for version %s had statistically significant changes (i.e., |z-score| > 1.96):\n\n", version)

w := tabwriter.NewWriter(&comment, 0, 0, 1, ' ', 0)
fmt.Fprintln(w, "| Benchmark\t| Measurement\t| H-Score\t| Z-Score\t| % Change\t| Stable Reg\t| Patch Value\t|")
fmt.Fprintln(w, "| ---------\t| -----------\t| -------\t| -------\t| --------\t| ----------\t| -----------\t|")

var testCount int64
var significantEnergyStats []EnergyStats
for _, es := range energyStats {
if math.Abs(es.ZScore) > 1.96 {
testCount += 1
fmt.Fprintf(w, "| %s\t| %s\t| %.4f\t| %.4f\t| %.4f\t| Avg: %.4f, Med: %.4f, Stdev: %.4f\t| %.4f\t|\n", es.Benchmark, es.Measurement, es.HScore, es.ZScore, es.PercentChange, es.StableRegion.Mean, es.StableRegion.Median, es.StableRegion.Std, es.MeasurementVal)
if es.Measurement != "iterations" && math.Abs(es.ZScore) > 1.96 {
significantEnergyStats = append(significantEnergyStats, *es)
}
}
w.Flush()

if testCount == 0 {
if len(significantEnergyStats) == 0 {
comment.Reset()
comment.WriteString("# 👋GoDriver Performance\n")
comment.WriteString("There were no significant changes to the performance to report.")
fmt.Fprintf(&comment, "There were no significant changes to the performance to report for version %s.\n", version)
} else {
sort.Slice(significantEnergyStats, func(i, j int) bool {
return math.Abs(significantEnergyStats[i].PercentChange) > math.Abs(significantEnergyStats[j].PercentChange)
})
for _, es := range significantEnergyStats {
fmt.Fprintf(w, "| %s\t| %s\t| %.4f\t| %.4f\t| %.4f\t| Avg: %.4f, Med: %.4f, Stdev: %.4f\t| %.4f\t|\n", es.Benchmark, es.Measurement, es.HScore, es.ZScore, es.PercentChange, es.StableRegion.Mean, es.StableRegion.Median, es.StableRegion.Std, es.MeasurementVal)
}
}
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()
Expand Down
122 changes: 122 additions & 0 deletions internal/cmd/perfcomp/parseperfcomp/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (C) MongoDB, Inc. 2025-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

package main

import (
"bufio"
"fmt"
"log"
"net/url"
"os"
"strings"
)

const parsePerfCompDir = "./internal/cmd/perfcomp/parseperfcomp/"
const perfReportFileTxt = "perf-report.txt"
const perfReportFileMd = "perf-report.md"
const perfVariant = "^perf$"

func main() {
var line string

// open file to read
fRead, err := os.Open(parsePerfCompDir + perfReportFileTxt)
if err != nil {
log.Fatalf("Could not open %s: %v", perfReportFileTxt, err)
}
defer fRead.Close()

// open file to write
fWrite, err := os.Create(perfReportFileMd)
if err != nil {
log.Fatalf("Could not create %s: %v", perfReportFileMd, err)
}
defer fWrite.Close()

fmt.Fprintf(fWrite, "## 👋 GoDriver Performance\n")

// read the file line by line using scanner
scanner := bufio.NewScanner(fRead)

var version string
var evgLink string

for scanner.Scan() {
line = scanner.Text()
if strings.Contains(line, "Version ID:") {
// parse version
version = strings.Split(line, " ")[2]
} else if strings.Contains(line, "Commit SHA:") {
// parse commit SHA and write header
fmt.Fprintf(fWrite, "\n<details>\n<summary><b>%s</b></summary>\n\t<br>\n\n", line)
} else if strings.Contains(line, "version "+version) {
// dynamic Evergreen perf task link
evgLink, err = generateEvgLink(version, perfVariant)
if err != nil {
log.Println(err)
fmt.Fprintf(fWrite, "%s\n", line)
} else {
printUrlToLine(fWrite, line, evgLink, "version", -1)
}
} else if strings.Contains(line, "For a comprehensive view of all microbenchmark results for this PR's commit, please check out the Evergreen perf task for this patch.") {
// last line of comment
evgLink, err = generateEvgLink(version, "")
if err != nil {
log.Println(err)
fmt.Fprintf(fWrite, "%s\n", line)
} else {
printUrlToLine(fWrite, line, evgLink, "Evergreen", 0)
}
} else {
// all other regular lines
fmt.Fprintf(fWrite, "%s\n", line)
}
}

fmt.Fprintf(fWrite, "</details>\n")
}

func generateEvgLink(version string, variant string) (string, error) {
baseUrl := "https://spruce.mongodb.com"
page := "0"
sorts := "STATUS:ASC;BASE_STATUS:DESC"

u, err := url.Parse(baseUrl)
if err != nil {
return "", fmt.Errorf("Error parsing URL: %v", err)
}

u.Path = fmt.Sprintf("version/%s/tasks", version)

// construct query parameters
queryParams := url.Values{}
queryParams.Add("page", page)
queryParams.Add("sorts", sorts)
if variant != "" {
queryParams.Add("variant", variant)
}

u.RawQuery = queryParams.Encode()
return u.String(), nil
}

func printUrlToLine(fWrite *os.File, line string, link string, targetWord string, step int) {
words := strings.Split(line, " ")
for i, w := range words {
if i > 0 && words[i+step] == targetWord {
fmt.Fprintf(fWrite, "[%s](%s)", w, link)
} else {
fmt.Fprint(fWrite, w)
}

if i < len(words)-1 {
fmt.Fprint(fWrite, " ")
} else {
fmt.Fprint(fWrite, "\n")
}
}
}
Loading