• Stars
    star
    181
  • Rank 212,110 (Top 5 %)
  • Language CMake
  • License
    Other
  • Created over 8 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple framework for compile-time benchmarks

Metabench Travis status Appveyor status

A simple framework for compile-time microbenchmarks

Overview

Metabench is a single, self-contained CMake module making it easy to create compile-time microbenchmarks. Compile-time benchmarks measure the performance of compiling a piece of code instead of measuring the performance of running it, as regular benchmarks do. The micro part in microbenchmark means that Metabench can be used to benchmark precise parts of a C++ file, such as the instantiation of a single function. Writing benchmarks of this kind is very useful for C++ programmers writing metaprogramming-heavy libraries, which are known to cause long compilation times. Metabench was designed to be very simple to use, while still allowing fairly complex benchmarks to be written.

Metabench is also a collection of compile-time microbenchmarks written using the metabench.cmake module. The benchmarks measure the compile-time performance of various algorithms provided by different metaprogramming libraries. The benchmarks are updated nightly with the latest version of each library, and the results are published at http://metaben.ch.

Requirements

Metabench requires CMake 3.1 or higher and Ruby 2.1 or higher. Metabench is known to work with CMake's Unix Makefiles and Ninja generators.

Usage

To use Metabench, make sure you have the dependencies listed above and simply drop the metabench.cmake file somewhere in your CMake search path for modules. Then, use include(metabench) to include the module in your CMake file, add individual datasets to be benchmarked using metabench_add_dataset, and finally specify which datasets should be put together into a chart via metabench_add_chart. For example, a minimal CMake file using Metabench would look like:

# Make sure Metabench can be found when writing include(metabench)
list(APPEND CMAKE_MODULE_PATH "path/to/metabench/directory")

# Actually include the module
include(metabench)

# Add new datasets
metabench_add_dataset(dataset1 "path/to/dataset1.cpp.erb" "[1, 5, 10]")
metabench_add_dataset(dataset2 "path/to/dataset2.cpp.erb" "(1...15)")
metabench_add_dataset(dataset3 "path/to/dataset3.cpp.erb" "(1...20).step(5)")

# Add a new chart
metabench_add_chart(chart DATASETS dataset1 dataset2 dataset3)

This will create a target named chart, which, when run, will gather benchmark data from each dataset and output JSON files for easy integration with other tools. A HTML file is generated for easy visualization of the datasets as a NVD3 chart. To understand what the path/to/datasetN.cpp.erb files are, read what follows.

The principle

Benchmarking the compilation time of a single .cpp file is rather useless, because one could simply run the compiler and time that single execution instead. What is really useful is to have a means of running variations of the same .cpp file automatically. For example, we might be interested in benchmarking the compilation time for creating a std::tuple with many elements in it. To do so, we could write the following test case:

#include <tuple>

int main() {
    auto tuple = std::make_tuple(1, 2, 3, 4, 5);
}

We would run the compiler and time the compilation, and then change the test case by augmenting the number of elements in the tuple:

#include <tuple>

int main() {
    auto tuple = std::make_tuple(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}

We would measure the compilation time for this file, and repeat the process until satisfactory data has been gathered. This tedious task of generating different (but obviously related) .cpp files and running the compiler to gather timings is what Metabench automates. It does this by taking a .cpp.erb file written using the ERB template system, and generating a family of .cpp files from that template. It then compiles these .cpp files and gathers benchmark data from these compilations.

Concretely, you start by writing a .cpp.erb file (say std_tuple.cpp.erb) that may contain ERB markup:

#include <tuple>

int main() {
    auto tuple = std::make_tuple(<%= (1..n).to_a.join(', ') %>);
}

Code contained inside <%= ... %> is just normal Ruby code. When the file will be rendered, the contents of <%= ... %> will be replaced with the result of evaluating this Ruby code, which will look like:

#include <tuple>

int main() {
    auto tuple = std::make_tuple(1, 2, 3, ..., n);
}

The ERB markup language has many other features; we encourage readers to take a look at the Wikipedia page. What happens is that Metabench will generate a .cpp file for different values of n, and will gather benchmark data for each of these values. Now, this isn't the whole story. More often than not, we're only interested in benchmarking part of a C++ file. Indeed, if we benchmark the whole file in our example above, we'll end up measuring the time required to #include the <tuple> header in addition to the time required for creating the std::tuple. While this might be negligible in our example, this situation arises in nontrivial examples, and would make the resulting data nearly worthless. Hence, we have to tell Metabench what part(s) of the file it should measure. This is done by guarding the relevant part(s) of the code with a preprocessor #if:

#include <tuple>

int main() {
#if defined(METABENCH)
    auto tuple = std::make_tuple(<%= (1..n).to_a.join(', ') %>);
#endif
}

What Metabench will actually do is compile the file once with the macro defined (and hence with the content of the block), and once without it. It will then subtract the time for compiling the file without the content of the block to the time for compiling the whole file, which should represent a good approximation of the time for compiling what's inside the block.

On the C++ side of things, the .cpp file will be compiled (to benchmark it) as if it were located in the directory containing the .cpp.erb file, so that relative include paths can be used. Furthermore, it will be compiled as if the .cpp file were part of a CMake executable added in the same directory as the call to metabench_add_dataset. This way, any variable or property set in CMake will also apply when benchmarking the file. In other words, Metabench tries to create the illusion that the code is actually compiled as if it were written in the .cpp.erb file.

This is it for the basic usage of the module! The example/ directory contains a fully working example of using Metabench to create benchmarks. For a more involved example, you can take a look at the benchmark suite in the benchmark/ directory. Note that only the most basic usage of Metabench was covered here. To know all the features provided by the module, you should read the reference documentation provided as comments inside the CMake module.

A note on benchmark resolution

Like any measurement tool, Metabench has a limited resolution. For example, when the code being measured (inside the #ifdef METABENCH/#endif pair) takes only a few milliseconds to compile, the timings reported by Metabench may be completely inside the noise. Typically, the resolution of timings taken by Metabench is similar to that of the time command. A good technique to make sure the results of a benchmark are not inside the noise is to reduce the relative uncertainty of the measurement. This can be done by increasing the total compilation time of the measured block, by repeating the same thing (or a similar one) multiple times:

#include <tuple>

int main() {
#if defined(METABENCH)
    auto tuple1 = std::make_tuple(<%= (1..n).to_a.join(', ') %>);
    auto tuple2 = std::make_tuple(<%= (1..n).to_a.join(', ') %>);
    auto tuple3 = std::make_tuple(<%= (1..n).to_a.join(', ') %>);
    auto tuple4 = std::make_tuple(<%= (1..n).to_a.join(', ') %>);
#endif
}

History

Metabench was initially developed inside the Boost.Hana library as a mean to benchmark compile-time algorithms. After seeing that a self-standing framework would be useful to the general C++ community, it was decided to extract it into its own project.

License

Please see LICENSE.md.

More Repositories

1

dyno

Runtime polymorphism done right
C++
975
star
2

mpl11

Enjoy template metaprogramming
C++
113
star
3

libawful

A collection of awful archetypes to ease the testing of generic C++ libraries
C++
109
star
4

reveal-sampler

A reveal.js plugin to fetch code samples from source files
JavaScript
26
star
5

chaos-pp

Clone of the Chaos CVS repository using git-cvsimport.
C
12
star
6

wg21

My proposals for the C++ standard
TeX
11
star
7

cppnow-2015-hana

Presentation on Hana for C++Now 2015
JavaScript
10
star
8

hawick_circuits

Implementation of an algorithm to find all the elementary circuits in a directed (multi)graph.
C++
10
star
9

d2

A library-based approach to deadlock detection using dynamic analysis.
C++
9
star
10

react

A C++11 framework for incremental computation.
C++
7
star
11

nstl

The non standard template library.
C
7
star
12

cppcon-2018-compile-time-programming

My slides on compile-time programming in C++20 for CppCon 2018
JavaScript
7
star
13

cisp

A C preprocessor library implementing object semantics.
C
6
star
14

duck

A template metaprogramming library for concept-based overloading in C++11.
C++
6
star
15

joy

A preprocessor metaprogramming library.
C
6
star
16

P1221-presentation

Presentation of https://wg21.link/P1221 on behalf of Jason Rice
CSS
5
star
17

cppcon-2015-hana

Presentation on Hana and metaprogramming for CppCon 2015
CSS
5
star
18

meetingcpp-2016

My presentation material for the closing keynote of Meeting C++ 2016
HTML
5
star
19

cppnow-2016-metaprogramming-for-the-brave

My "Metaprogramming for the brave" presentation for C++Now 2016
HTML
5
star
20

cppnow-2018-runtime-polymorphism

My presentation on runtime polymorphism at C++Now 2018
JavaScript
5
star
21

cppnow-2016-metaprogramming-for-dummies

My "Metaprogramming for dummies" presentation for C++Now 2016
CSS
5
star
22

hana-thesis

Bachelor's thesis on Hana and category theory. In french only.
JavaScript
4
star
23

accu-2017-metaprogramming-in-cpp14

My presentation on metaprogramming for ACCU 2017
HTML
4
star
24

cppnow-2017-fun-with-boost-hana

My "Fun with Boost.Hana" presentation for C++Now 2017
HTML
3
star
25

accu-2018-runtime-polymorphism

My presentation on runtime polymorphism at ACCU 2018
JavaScript
3
star
26

cppcon-2017-runtime-polymorphism

My presentation on runtime polymorphism at CppCon 2017
CSS
3
star
27

cppcon-2019-abi-from-the-ground-up

My talk on the C++ ABI for CppCon 2019
CSS
3
star
28

cppnow-2018-empty-objects

Lightning talk about template errors for C++Now 2018
CSS
2
star
29

mpl_extensions

Extensions to the Boost.MPL library.
C++
2
star
30

accu-2017-rust-traits-in-cpp

My lightning talk on dynamic polymorphism at ACCU 2017
CSS
2
star
31

accu-bay-area-meetup-03-2017

My presentation on metaprogramming for the March 2017 ACCU Bay Area C++ Meetup
HTML
2
star
32

cppnow-2014-mpl11

Presentation of the MPL11 at C++Now 2014
JavaScript
2
star
33

docker-cpp-test

Nothing to see here. I'm experimenting with Docker on Travis CI
2
star
34

cppnow-2013-d2

My presentation on deadlock detection for the C++Now 2013 conference.
CSS
1
star
35

dbg-d2

The dbg library with modifications for use by d2.
C++
1
star
36

macroize

A small utility to backslash escape long macro definitions in C and C++.
Python
1
star