Benchmarking

GitLab CE comes with a set of benchmarks that are executed for every build. This makes it easier to measure performance of certain components over time.

Benchmarks are written as RSpec tests using a few extra helpers. To write a benchmark, first tag the top-level describe:

describe MaruTheCat, benchmark: true do

end

This ensures the benchmark is executed separately from other test collections. It also exposes the various RSpec matchers used for writing benchmarks to the test group.

Next, lets write the actual benchmark:

describe MaruTheCat, benchmark: true do
  let(:maru) { MaruTheChat.new }

  describe '#jump_in_box' do
    benchmark_subject { maru.jump_in_box }

    it { is_expected.to iterate_per_second(9000) }
  end
end

Here benchmark_subject is a small wrapper around RSpec's subject method that makes it easier to specify the subject of a benchmark. Using RSpec's regular subject would require us to write the following instead:

subject { -> { maru.jump_in_box } }

The iterate_per_second matcher defines the amount of times per second a subject should be executed. The higher the amount of iterations the better.

By default the allowed standard deviation is a maximum of 30%. This can be adjusted by chaining the with_maximum_stddev on the iterate_per_second matcher:

it { is_expected.to iterate_per_second(9000).with_maximum_stddev(50) }

This can be useful if the code in question depends on external resources of which the performance can vary a lot (e.g. physical HDDs, network calls, etc). However, in most cases 30% should be enough so only change this when really needed.

Benchmarks Location

Benchmarks should be stored in spec/benchmarks and should follow the regular Rails specs structure. That is, model benchmarks go in spec/benchmark/models, benchmarks for code in the lib directory go in spec/benchmarks/lib, etc.

Underlying Technology

The benchmark setup uses benchmark-ips which takes care of the heavy lifting such as warming up code, calculating iterations, standard deviation, etc.