• Stars
    star
    211
  • Rank 186,867 (Top 4 %)
  • Language
    C++
  • License
    MIT License
  • Created about 4 years ago
  • Updated about 4 years ago

Reviews

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

Repository Details

Microbenchmarking for Modern C++

Highlights

Criterion is a micro-benchmarking library for modern C++.

  • Convenient static registration macros for setting up benchmarks
  • Parameterized benchmarks (e.g., vary input size)
  • Statistical analysis across multiple runs
  • Requires compiler support for C++17 or newer standard
  • Header-only library - single header file version available at single_include/
  • MIT License

Table of Contents

Getting Started

Let's say we have this merge sort implementation that needs to be benchmarked.

template<typename RandomAccessIterator, typename Compare>
void merge_sort(RandomAccessIterator first, RandomAccessIterator last,
                Compare compare, std::size_t size) {
  if (size < 2) return;
  auto middle = first + size / 2;
  merge_sort(first, middle, compare, size / 2);
  merge_sort(middle, last, compare, size - size/2);
  std::inplace_merge(first, middle, last, compare);
}

Simple Benchmark

Include <criterion/criterion.hpp> and you're good to go.

  • Use the BENCHMARK macro to declare a benchmark
  • Use SETUP_BENCHMARK and TEARDOWN_BENCHMARK to perform setup and teardown tasks
    • These tasks are not part of the measurement
#include <criterion/criterion.hpp>

BENCHMARK(MergeSort)
{
  SETUP_BENCHMARK(
    const auto size = 100;
    std::vector<int> vec(size, 0); // vector of size 100
  )
 
  // Code to be benchmarked
  merge_sort(vec.begin(), vec.end(), std::less<int>(), size);
  
  TEARDOWN_BENCHMARK(
    vec.clear();
  )
}

CRITERION_BENCHMARK_MAIN()

What if we want to run this benchmark on a variety of sizes?

Passing Arguments

  • The BENCHMARK macro can take typed parameters
  • Use GET_ARGUMENTS(n) to get the nth argument passed to the benchmark
  • For benchmarks that require arguments, use INVOKE_BENCHMARK_FOR_EACH and provide arguments
#include <criterion/criterion.hpp>

BENCHMARK(MergeSort, std::size_t) // <- one parameter to be passed to the benchmark
{
  SETUP_BENCHMARK(
    const auto size = GET_ARGUMENT(0); // <- get the argument passed to the benchmark
    std::vector<int> vec(size, 0);
  )
 
  // Code to be benchmarked
  merge_sort(vec.begin(), vec.end(), std::less<int>(), size);
  
  TEARDOWN_BENCHMARK(
    vec.clear();
  )
}

// Run the above benchmark for a number of inputs:

INVOKE_BENCHMARK_FOR_EACH(MergeSort,
  ("/10", 10),
  ("/100", 100),
  ("/1K", 1000),
  ("/10K", 10000),
  ("/100K", 100000)
)

CRITERION_BENCHMARK_MAIN()

Passing Arguments (Part 2)

Let's say we have the following struct and we need to create a std::shared_ptr to it.

struct Song {
  std::string artist;
  std::string title;
  Song(const std::string& artist_, const std::string& title_) :
    artist{ artist_ }, title{ title_ } {}
};

Here are two implementations for constructing the std::shared_ptr:

// Functions to be tested
auto Create_With_New() { 
  return std::shared_ptr<Song>(new Song("Black Sabbath", "Paranoid")); 
}

auto Create_With_MakeShared() { 
  return std::make_shared<Song>("Black Sabbath", "Paranoid"); 
}

We can setup a single benchmark that takes a std::function<> and measures performance like below.

BENCHMARK(ConstructSharedPtr, std::function<std::shared_ptr<Song>()>) 
{
  SETUP_BENCHMARK(
    auto test_function = GET_ARGUMENT(0);
  )

  // Code to be benchmarked
  auto song_ptr = test_function();
}

INVOKE_BENCHMARK_FOR_EACH(ConstructSharedPtr, 
  ("/new", Create_With_New),
  ("/make_shared", Create_With_MakeShared)
)

CRITERION_BENCHMARK_MAIN()

CRITERION_BENCHMARK_MAIN and Command-line Options

CRITERION_BENCHMARK_MAIN() provides a main function that:

  1. Handles command-line arguments,
  2. Runs the registered benchmarks
  3. Exports results to file if requested by user.

Here's the help/man generated by the main function:

foo@bar:~$ ./benchmarks -h

NAME
     ./benchmarks -- Run Criterion benchmarks

SYNOPSIS
     ./benchmarks
           [-w,--warmup <number>]
           [-l,--list] [--list_filtered <regex>] [-r,--run_filtered <regex>]
           [-e,--export_results {csv,json,md,asciidoc} <filename>]
           [-q,--quiet] [-h,--help]
DESCRIPTION
     This microbenchmarking utility repeatedly executes a list of benchmarks,
     statistically analyzing and reporting on the temporal behavior of the executed code.

     The options are as follows:

     -w,--warmup number
          Number of warmup runs (at least 1) to execute before the benchmark (default=3)

     -l,--list
          Print the list of available benchmarks

     --list_filtered regex
          Print a filtered list of available benchmarks (based on user-provided regex)

     -r,--run_filtered regex
          Run a filtered list of available benchmarks (based on user-provided regex)

     -e,--export_results format filename
          Export benchmark results to file. The following are the supported formats.

          csv       Comma separated values (CSV) delimited text file
          json      JavaScript Object Notation (JSON) text file
          md        Markdown (md) text file
          asciidoc  AsciiDoc (asciidoc) text file

     -q,--quiet
          Run benchmarks quietly, suppressing activity indicators

     -h,--help
          Print this help message

Exporting Results (csv, json, etc.)

Benchmarks can be exported to one of a number of formats: .csv, .json, .md, and .asciidoc.

Use --export_results (or -e) to export results to one of the supported formats.

foo@bar:~$ ./vector_sort -e json results.json -q # run quietly and export to JSON

foo@bar:~$ cat results.json
{
  "benchmarks": [
    {
      "name": "VectorSort/100",
      "warmup_runs": 2,
      "iterations": 2857140,
      "mean_execution_time": 168.70,
      "fastest_execution_time": 73.00,
      "slowest_execution_time": 88809.00,
      "lowest_rsd_execution_time": 84.05,
      "lowest_rsd_percentage": 3.29,
      "lowest_rsd_index": 57278,
      "average_iteration_performance": 5927600.84,
      "fastest_iteration_performance": 13698630.14,
      "slowest_iteration_performance": 11260.12
    },
    {
      "name": "VectorSort/1000",
      "warmup_runs": 2,
      "iterations": 2254280,
      "mean_execution_time": 1007.70,
      "fastest_execution_time": 640.00,
      "slowest_execution_time": 102530.00,
      "lowest_rsd_execution_time": 647.45,
      "lowest_rsd_percentage": 0.83,
      "lowest_rsd_index": 14098,
      "average_iteration_performance": 992355.48,
      "fastest_iteration_performance": 1562500.00,
      "slowest_iteration_performance": 9753.24
    },
    {
      "name": "VectorSort/10000",
      "warmup_runs": 2,
      "iterations": 259320,
      "mean_execution_time": 8833.26,
      "fastest_execution_time": 6276.00,
      "slowest_execution_time": 114548.00,
      "lowest_rsd_execution_time": 8374.15,
      "lowest_rsd_percentage": 0.11,
      "lowest_rsd_index": 7905,
      "average_iteration_performance": 113208.45,
      "fastest_iteration_performance": 159337.16,
      "slowest_iteration_performance": 8729.96
    }
  ]
}

Building Library and Samples

cmake -Hall -Bbuild
cmake --build build

# run `merge_sort` sample
./build/samples/merge_sort/merge_sort

Generating Single Header

python3 utils/amalgamate/amalgamate.py -c single_include.json -s .

Contributing

Contributions are welcome, have a look at the CONTRIBUTING.md document for more information.

License

The project is available under the MIT license.

More Repositories

1

awesome-hpp

A curated list of awesome header-only C++ libraries
3,468
star
2

indicators

Activity Indicators for Modern C++
C++
3,004
star
3

argparse

Argument Parser for Modern C++
C++
2,655
star
4

tabulate

Table Maker for Modern C++
C++
1,926
star
5

pprint

Pretty Printer for Modern C++
C++
911
star
6

csv2

Fast CSV parser and writer for Modern C++
C++
552
star
7

alpaca

Serialization library written in C++17 - Pack C++ structs into a compact byte-array without any macros or boilerplate code
C++
474
star
8

structopt

Parse command line arguments by defining a struct
C++
455
star
9

fccf

fccf: A command-line tool that quickly searches through C/C++ source code in a directory based on a search string and prints relevant code snippets that match the query.
C++
359
star
10

glob

Glob for C++17
C++
246
star
11

csv

[DEPRECATED] See https://github.com/p-ranav/csv2
C++
234
star
12

binary_log

Fast binary logger for C++
C++
207
star
13

hypergrep

Recursively search directories for a regex pattern
C++
201
star
14

saveddit

Bulk Downloader for Reddit
Python
169
star
15

PhotoLab

AI-Powered Photo Editor (Python, PyQt6, PyTorch)
Python
161
star
16

box

box is a text-based visual programming language inspired by Unreal Engine Blueprint function graphs.
Python
120
star
17

cppgit2

Git for Modern C++ (A libgit2 Wrapper Library)
C++
116
star
18

psched

Priority-based Task Scheduling for Modern C++
C++
84
star
19

repr

repr for Modern C++: Return printable string representation of a value
C++
83
star
20

fswatch

File/Directory Watcher for Modern C++
C++
79
star
21

envy

envy: Deserialize environment variables into type-safe structs
C++
66
star
22

pipeline

Pipelines for Modern C++
C++
57
star
23

iris

Lightweight Component Model and Messaging Framework based on ØMQ
C++
53
star
24

merged_depth

Monocular Depth Estimation - Weighted-average prediction from multiple pre-trained depth estimation models
Python
47
star
25

unicode_display_width

Displayed width of UTF-8 strings in Modern C++
C++
44
star
26

task_system

Task System presented in "Better Code: Concurrency - Sean Parent"
C++
39
star
27

cgol

Conway's Game of Life in the Terminal
C++
35
star
28

small_vector

"Small Vector" optimization for Modern C++: store up to a small number of items on the stack
C++
33
star
29

jsonlint

Lightweight command-line tool for validating JSON
C++
33
star
30

result

Result<T, E> for Modern C++
C++
32
star
31

container_traits

Container Traits for Modern C++
C++
28
star
32

lexer

Hackable Lexer with UTF-8 support
C++
21
star
33

lc

Fast multi-threaded line counter in Modern C++ (2-10x faster than `wc -l` for large files)
C++
18
star
34

oystr

oystr recursively searches directories for a substring.
C++
10
star
35

walnut.v1

The Walnut programming language
C++
8
star
36

line-detector

OpenCV-based Hough Transform Line Detection
C++
8
star
37

ttt

Terminal Typing Test
C++
7
star
38

OpenGL-Engine

OpenGL 3D Rendering Engine
C++
7
star
39

wxPython-text-editor

wxPython Text Editor
Python
6
star
40

Vulkan-Earth

Vulkan-based 3D Rendering of Earth
HTML
6
star
41

strcpp.old

String Manipulation API for C++
C++
6
star
42

DiverseDepth

The code and data of DiverseDepth
Python
6
star
43

ImageViewer-Qt6

Minimalist image viewer in Qt6
C++
6
star
44

any_of_trait

Type traits for any_of and any_but
C++
5
star
45

zcm

A Lightweight Component Model using ZeroMQ
C++
4
star
46

StaticAnalysis

GitHub action for C++ static analysis
Python
4
star
47

video_device_discovery

Find all video devices connected to Linux-based embedded platform
C++
3
star
48

krpci

C++ client to kRPC for communication with Kerbal Space Program (KSP)
C++
2
star
49

activity-plotter

Linux Scheduler Thread Activity Plotter
Python
2
star
50

python-zcm

ZeroMQ-based Component Model in Python
Python
2
star
51

emacs_config

Emacs configuration
Emacs Lisp
1
star
52

plexil-analysis

Timing Analysis for the Plan Interchange Language (Plexil)
Python
1
star
53

object-tracker

OpenCV-based Real-time Object Tracking
C++
1
star
54

json.old

JSON Manipulation Library for C++
C++
1
star
55

phd-dissertation

TeX
1
star
56

OpenGL-Engine-II

OpenGL 3D Rendering Engine II - Alternate Architecture
C++
1
star
57

arangit

Python program that can scan a .git folder and reconstruct a git version control property graph in ArangoDB
Python
1
star
58

ros-installer

Script to install ROS Indigo from source
Python
1
star