kota's memex

test benchmarks

Run benchmarks with:
go test -bench=. -benchmem

func BenchmarkIsPalindrome(b *testing.B) {
  for i := 0; i < b.N; i++ {
    IsPalindrome("A man, a plan, a canal: Panama")
  }
}

benchmark non-tests

cpu

Slap this at the start of your program.

f, err := os.Create("cpu.prof")
if err != nil {
    log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

One problem you might run into is that your defer wont run if you control-c out of your program. This is a general rule, but in this particular case if means your profile wont be written. You can catch that signal and stop the profile there and then exit out:

f, err := os.Create("cpu.prof")
if err != nil {
  log.Fatal(err)
}
pprof.StartCPUProfile(f)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
  <-c
  pprof.StopCPUProfile()
  os.Exit(1)
}()

mem

Memory profiling is similar except that the relevant function needs called just once before the program closes.

f, err := os.Create("mem.prof")
if err != nil {
  log.Fatal(err)
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
  <-c
  runtime.GC()
  if err := pprof.WriteHeapProfile(f); err != nil {
    log.Fatal("could not write memory profile: ", err)
  }
  f.Close()
  os.Exit(1)
}()

https://pkg.go.dev/golang.org/x/perf/cmd/benchstat

This tool makes it super easy to store benchmark results and later compare them.

profiling

Generate profiles:

go test -cpuprofile=cpu.out
go test -blockprofile=block.out
go test -memprofile=mem.out

Often you want profiles for benchmarks not tests:

go test -run=NONE -bench=BenchmarkIsPalindrome -cpuprofile=cpu.out

Printing out text info about a profile:

go tool pprof -text -nodecount=10 ./palindrome.test cpu.out

You can also render it to a nice SVG or DOT format if the graphviz program is installed. The SVG renders great in a web browser.

Additionally, there's a nice package that can be used to profile some specific code easily:
https://github.com/pkg/profile

disable CPU frequency scaling

This will give a more fair benchmark when comparing tools.

!/bin/bash
for i in /sys/devices/system/cpu/cpu[0-7]
do
    echo performance > $i/cpufreq/scaling_governor
done

fake io.Writer

Sometimes you need to benchmark a function with an io.Writer, but don't actually want to waste memory or space by storing the data. You can using the iotest.TrunctateWriter for this:

buf := new(bytes.Buffer)
w := iotest.TruncateWriter(buf, 0) // no need to actually store the data

tricks

struct alignment

https://itnext.io/structure-size-optimization-in-golang-alignment-padding-more-effective-memory-layout-linters-fffdcba27c61

There's also a linter (disabled by default) as part of golintci called: fieldalignment which checks for places where you can improve this.