Skip to content

Commit

Permalink
Add support for -benchmem format with -mem flag (#5)
Browse files Browse the repository at this point in the history
This change adds a `-mem` flag which can parse output generated from `go test -benchmem`.
  • Loading branch information
markuswustenberg authored Nov 17, 2023
2 parents 2b6bd13 + 3922174 commit 1e65af0
Show file tree
Hide file tree
Showing 7 changed files with 547 additions and 2 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,19 @@ PASS
ok github.com/maragudk/go-bench2csv 7.324s
```

Also works with `go test -benchmem`:

```shell
$ go test -cpu 1,2,4,8 -bench . -benchmem | bench2csv -mem >benchmark.csv
goos: darwin
goarch: arm64
pkg: github.com/maragudk/go-bench2csv
BenchmarkProcess/in_parallel_just_for_fun 4106 292497 ns/op 53892 B/op 738 allocs/op
BenchmarkProcess/in_parallel_just_for_fun-2 7929 151227 ns/op 53897 B/op 738 allocs/op
BenchmarkProcess/in_parallel_just_for_fun-4 15013 79910 ns/op 53909 B/op 738 allocs/op
BenchmarkProcess/in_parallel_just_for_fun-8 18214 66196 ns/op 53941 B/op 738 allocs/op
PASS
ok github.com/maragudk/go-bench2csv 7.402s
```

Made in 🇩🇰 by [maragu](https://www.maragu.dk/), maker of [online Go courses](https://www.golang.dk/).
36 changes: 34 additions & 2 deletions bench2csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ const (
Operations
Duration
Frequency
BytesPerOp
AllocsPerOp
Default = Name | Parallelism | Operations | Duration
Memory = BytesPerOp | AllocsPerOp
)

// benchmakrMatcher matches a benchmark output line.
// See https://regex101.com/r/Uv4LNN/latest
// benchmarkMatcher matches a benchmark output line.
// See https://regex101.com/r/EEQMWQ/latest
var benchmarkMatcher = regexp.MustCompile(
`^Benchmark` + // "Benchmark" prefix
`(?P<name>[^-\s]+)` + // Name
Expand All @@ -29,6 +32,16 @@ var benchmarkMatcher = regexp.MustCompile(
`\s+` +
`(?P<duration>\d+(?:\.\d+)?)` + // Duration for each operation
`\sns/op` + // Duration unit suffix

// Optionally, with -benchmem...
`(\s+` +
`(?P<bytesPerOp>\d+)` + // Bytes per operation
`\sB/op\s+` + // Bytes per operation unit suffix
`(?P<allocsPerOp>\d+)` + // Allocs per operation
`\sallocs/op` + // Allocs per operation unit suffix
`)?` +

// The end
`$`)

// Process benchmark output from in, write CSV to csvOut, and pipe benchmark output to errOut.
Expand All @@ -51,6 +64,12 @@ func Process(in io.Reader, csvOut, errOut io.Writer, format int) error {
if format&Frequency != 0 {
header = append(header, "frequency")
}
if format&BytesPerOp != 0 {
header = append(header, "bytes_per_op")
}
if format&AllocsPerOp != 0 {
header = append(header, "allocs_per_op")
}

if _, err := fmt.Fprintln(csvOut, strings.Join(header, ",")); err != nil {
return err
Expand All @@ -74,6 +93,13 @@ func Process(in io.Reader, csvOut, errOut io.Writer, format int) error {
operations := submatches[2]
duration := submatches[3]

bytesPerOp := "0"
allocsPerOp := "0"
if len(submatches) > 6 {
bytesPerOp = submatches[5]
allocsPerOp = submatches[6]
}

if parallelism == "" {
parallelism = "1"
}
Expand All @@ -100,6 +126,12 @@ func Process(in io.Reader, csvOut, errOut io.Writer, format int) error {
if format&Frequency != 0 {
values = append(values, frequency)
}
if format&BytesPerOp != 0 {
values = append(values, bytesPerOp)
}
if format&AllocsPerOp != 0 {
values = append(values, allocsPerOp)
}

if _, err := fmt.Fprintln(csvOut, strings.Join(values, ",")); err != nil {
return err
Expand Down
24 changes: 24 additions & 0 deletions bench2csv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,30 @@ func TestProcess(t *testing.T) {
}
})

t.Run("outputs CSV with benchmem statistics if set", func(t *testing.T) {
var csv strings.Builder

err := bench2csv.Process(strings.NewReader(readFile(t, "input2.txt")), &csv, io.Discard,
bench2csv.Default|bench2csv.Memory)
noErr(t, err)

if csv.String() != readFile(t, "output3.csv") {
t.Fatal("Unexpected output:", csv.String())
}
})

t.Run("outputs CSV with benchmem statistics and frequency if set", func(t *testing.T) {
var csv strings.Builder

err := bench2csv.Process(strings.NewReader(readFile(t, "input2.txt")), &csv, io.Discard,
bench2csv.Default|bench2csv.Frequency|bench2csv.Memory)
noErr(t, err)

if csv.String() != readFile(t, "output4.csv") {
t.Fatal("Unexpected output:", csv.String())
}
})

t.Run("pipes input to output", func(t *testing.T) {
input := readFile(t, "input1.txt")

Expand Down
5 changes: 5 additions & 0 deletions cmd/bench2csv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ func start() error {
format := bench2csv.Default

freq := flag.Bool("freq", false, "Include frequency output")
mem := flag.Bool("mem", false, "Include -benchmem output")

flag.Parse()

if *freq {
format |= bench2csv.Frequency
}

if *mem {
format |= bench2csv.Memory
}

return bench2csv.Process(os.Stdin, os.Stdout, os.Stderr, format)
}
Loading

0 comments on commit 1e65af0

Please sign in to comment.