Files
visor/test/benchmarks
Adin ScannellandgVisor bot caec235b70 Simplify profiling and benchmarks.
- Tweak the benchmarks to work with b.N where appropriate. In many cases,
  b.N was simply being ignored. This creates an implicit dependency in the
  user passing a reasonable benchtime (less than or equal to the actual
  runtime of the test, or using the X syntax) otherwise the test runs
  forever.
- In cases where the above is impossible, explicitly set benchtime from
  the test wrapper, to prevent the above behavior (tensorflow).
- Drop the *Reverse variants, which are simply hey benchmarks. We should
  just add a hey benchmark. The platforms benchmarks already include a
  native platform, and thus these benchmarks are incredibly confusing.
  (In other words, BenchmarkNginxReverse has nothing to do with an nginx
  benchmark for runsc.)
- Remove the redunant Harness object, which contains no state, in order
  to slightly simplify the code.
- Make Block and Heap profiling actually work, but setting appropriate
  runtime parameters (and plumbing them through the config).
- Split the profiling into two phases: start and stop, since some will
  need to be started early, and others will need to happen at the end.

PiperOrigin-RevId: 349495377
2020-12-29 18:29:12 -08:00
..
2020-12-29 18:29:12 -08:00
2020-12-29 18:29:12 -08:00
2020-12-29 18:29:12 -08:00
2020-12-29 18:29:12 -08:00
2020-11-16 14:36:10 -08:00
2020-12-29 18:29:12 -08:00
2020-12-07 16:20:12 -08:00
2020-12-07 16:20:12 -08:00
2020-12-29 18:29:12 -08:00

Benchmark tools

This package and subpackages are for running macro benchmarks on runsc. They are meant to replace the previous //benchmarks benchmark-tools written in python.

Benchmarks are meant to look like regular golang benchmarks using the testing.B library.

Setup

To run benchmarks you will need:

  • Docker installed (17.09.0 or greater).

The easiest way to setup runsc for running benchmarks is to use the make file. From the root directory:

  • Download images: make load-all-images
  • Install runsc suitable for benchmarking, which should probably not have strace or debug logs enabled. For example:make configure RUNTIME=myrunsc ARGS=--platform=kvm.
  • Restart docker: sudo service docker restart

You should now have a runtime with the following options configured in /etc/docker/daemon.json

"myrunsc": {
            "path": "/tmp/myrunsc/runsc",
            "runtimeArgs": [
                "--debug-log",
                "/tmp/bench/logs/runsc.log.%TEST%.%TIMESTAMP%.%COMMAND%",
                "--platform=kvm"
            ]
        },

This runtime has been configured with a debugging off and strace logs off and is using kvm for demonstration.

Running benchmarks

Given the runtime above runtime myrunsc, run benchmarks with the following:

make sudo TARGETS=//path/to:target ARGS="--runtime=myrunsc -test.v \
  -test.bench=." OPTIONS="-c opt

For example, to run only the Iperf tests:

make sudo TARGETS=//test/benchmarks/network:network_test \
  ARGS="--runtime=myrunsc -test.v -test.bench=Iperf" OPTIONS="-c opt"

Benchmarks are run with root as some benchmarks require root privileges to do things like drop caches.

Writing benchmarks

Benchmarks consist of docker images as Dockerfiles and golang testing.B benchmarks.

Dockerfiles:

  • Are stored at //images.
  • New Dockerfiles go in an appropriately named directory at //images/benchmarks/my-cool-dockerfile.
  • Dockerfiles for benchmarks should:
    • Use explicitly versioned packages.
    • Not use ENV and CMD statements...it is easy to add these in the API.
  • Note: A common pattern for getting access to a tmpfs mount is to copy files there after container start. See: //test/benchmarks/build/bazel_test.go. You can also make your own with RunOpts.Mounts.

testing.B packages

In general, benchmarks should look like this:

func BenchmarkMyCoolOne(b *testing.B) {
  machine, err := harness.GetMachine()
  // check err
  defer machine.CleanUp()

  ctx := context.Background()
  container := machine.GetContainer(ctx, b)
  defer container.CleanUp(ctx)

  b.ResetTimer()

  // Respect b.N.
  for i := 0; i < b.N; i++ {
    out, err := container.Run(ctx, dockerutil.RunOpts{
      Image: "benchmarks/my-cool-image",
      Env: []string{"MY_VAR=awesome"},
      other options...see dockerutil
    }, "sh", "-c", "echo MY_VAR")
    // check err...
    b.StopTimer()

    // Do parsing and reporting outside of the timer.
    number := parseMyMetric(out)
    b.ReportMetric(number, "my-cool-custom-metric")

    b.StartTimer()
  }
}

func TestMain(m *testing.M) {
    harness.Init()
    os.Exit(m.Run())
}

Some notes on the above:

  • Respect b.N in that users of the benchmark may want to "run for an hour" or something of the sort.
  • Use the b.ReportMetric() method to report custom metrics.
  • Set the timer if time is useful for reporting. There isn't a way to turn off default metrics in testing.B (B/op, allocs/op, ns/op).
  • Take a look at dockerutil at //pkg/test/dockerutil to see all methods available from containers. The API is based on the "official" docker API for golang.
  • harness.GetMachine() marks how many machines this tests needs. If you have a client and server and to mark them as multiple machines, call harness.GetMachine() twice.

Profiling

For profiling, the runtime is required to have the --profile flag enabled. This flag loosens seccomp filters so that the runtime can write profile data to disk. This configuration is not recommended for production.

  • Install runsc with the --profile flag: make configure RUNTIME=myrunsc ARGS="--profile --platform=kvm --vfs2". The kvm and vfs2 flags are not required, but are included for demonstration.
  • Restart docker: sudo service docker restart

To run and generate CPU profiles fs_test test run:

make sudo TARGETS=//test/benchmarks/fs:fs_test \
  ARGS="--runtime=myrunsc -test.v -test.bench=. --pprof-cpu" OPTIONS="-c opt"

Profiles would be at: /tmp/profile/myrunsc/CONTAINERNAME/cpu.pprof