├── .cirrus.yml ├── .clang-tidy ├── .github ├── FUNDING.yml └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── build └── .gitignore ├── cmake └── nanobenchConfig.cmake.in ├── docs ├── .gitignore ├── .nojekyll ├── CNAME ├── CODE_OF_CONDUCT.html ├── _images │ ├── nanobench-logo-small.svg │ └── totalruntime.svg ├── _static │ ├── _sphinx_javascript_frameworks_compat.js │ ├── basic.css │ ├── css │ │ ├── badge_only.css │ │ ├── fonts │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── lato-bold-italic.woff │ │ │ ├── lato-bold-italic.woff2 │ │ │ ├── lato-bold.woff │ │ │ ├── lato-bold.woff2 │ │ │ ├── lato-normal-italic.woff │ │ │ ├── lato-normal-italic.woff2 │ │ │ ├── lato-normal.woff │ │ │ └── lato-normal.woff2 │ │ └── theme.css │ ├── doctools.js │ ├── documentation_options.js │ ├── favicon.ico │ ├── file.png │ ├── jquery-3.6.0.js │ ├── jquery.js │ ├── js │ │ ├── badge_only.js │ │ ├── html5shiv-printshiv.min.js │ │ ├── html5shiv.min.js │ │ └── theme.js │ ├── language_data.js │ ├── minus.png │ ├── nanobench-logo.png │ ├── nanobench-logo.svg │ ├── plus.png │ ├── pygments.css │ ├── searchtools.js │ ├── sphinx_highlight.js │ ├── underscore-1.13.1.js │ └── underscore.js ├── comparison.html ├── genindex.html ├── index.html ├── license.html ├── reference.html ├── search.html ├── searchindex.js └── tutorial.html ├── src ├── .clang-format ├── CMakeLists.txt ├── cmake │ └── CMakeLists.txt ├── comparisons │ ├── DigitalInBlue_celero │ │ ├── .gitignore │ │ └── celero.cpp │ ├── cameron317_microbench │ │ ├── .gitignore │ │ ├── microbench.cpp │ │ └── out.txt │ ├── catchorg_Catch2 │ │ ├── .gitignore │ │ ├── catch.cpp │ │ └── out.txt │ ├── google_benchmark │ │ ├── .gitignore │ │ ├── gbench │ │ ├── gbench.cpp │ │ └── out.txt │ ├── iboB_picobench │ │ ├── .gitignore │ │ ├── out.txt │ │ └── picobench.cpp │ ├── ivafanas_sltbench │ │ ├── main.cpp │ │ └── out.txt │ ├── libnonius_nonius │ │ ├── .gitignore │ │ ├── main.cpp │ │ └── out.txt │ ├── nanobench │ │ ├── main.cpp │ │ └── out.txt │ └── timer.cpp ├── docs │ ├── CODE_OF_CONDUCT.md │ ├── Doxyfile │ ├── _generated │ │ ├── mustache.render.csv │ │ ├── mustache.render.html │ │ ├── mustache.render.json │ │ ├── mustache.template.csv │ │ ├── mustache.template.html │ │ ├── mustache.template.json │ │ ├── mustache.template.pyperf │ │ └── tutorial_render_simple.txt │ ├── _static │ │ └── nanobench-logo.png │ ├── banane.svg │ ├── code │ │ ├── CMakeLists.txt │ │ ├── full_example.cpp │ │ ├── full_example_simple.cpp │ │ ├── pyperf_compare_to.txt │ │ ├── pyperf_hist.txt │ │ ├── pyperf_stats.txt │ │ ├── template-csv.txt │ │ ├── template-htmlBoxplot.txt │ │ ├── template-json.txt │ │ └── template-pyperf.txt │ ├── comparison.rst │ ├── conf.py │ ├── favicon.ico │ ├── generate.sh │ ├── genindex.rst │ ├── index.rst │ ├── license.rst │ ├── nanobench-logo-compressed.png │ ├── nanobench-logo-small.svg │ ├── nanobench-logo.png │ ├── nanobench-logo.svg │ ├── reference.rst │ ├── requirements.txt │ ├── totalruntime.svg │ └── tutorial.rst ├── include │ ├── .clang-format │ ├── CMakeLists.txt │ └── nanobench.h ├── scripts │ ├── all.sh │ ├── asymptotes-google.cpp │ ├── build.sh │ ├── clang-analyze-build.sh │ ├── doctest-no-output.cpp │ ├── gh-md-toc │ ├── lint │ │ ├── lint-all.py │ │ ├── lint-clang-format.py │ │ └── lint-version.py │ └── toc-insert.sh └── test │ ├── CMakeLists.txt │ ├── app │ ├── CMakeLists.txt │ ├── doctest.cpp │ └── nanobench.cpp │ ├── example_always_the_same.cpp │ ├── example_atomic.cpp │ ├── example_branch_misses.cpp │ ├── example_complexity.cpp │ ├── example_containers.cpp │ ├── example_csv.cpp │ ├── example_hide_output.cpp │ ├── example_preconfigured_name.cpp │ ├── example_pyperf.cpp │ ├── example_random2.cpp │ ├── example_random_number_generators.cpp │ ├── example_random_uniform01.cpp │ ├── example_shuffle.cpp │ ├── thirdparty │ ├── CMakeLists.txt │ └── doctest │ │ └── doctest.h │ ├── tutorial_complexity_set.cpp │ ├── tutorial_complexity_sort.cpp │ ├── tutorial_context.cpp │ ├── tutorial_fast_v1.cpp │ ├── tutorial_fast_v2.cpp │ ├── tutorial_fluctuating_v1.cpp │ ├── tutorial_fluctuating_v2.cpp │ ├── tutorial_mustache.cpp │ ├── tutorial_render_simple.cpp │ ├── tutorial_slow_v1.cpp │ ├── tutorial_slow_v2.cpp │ ├── unit_api.cpp │ ├── unit_cold.cpp │ ├── unit_exact_iters_and_epochs.cpp │ ├── unit_romutrio.cpp │ ├── unit_templates.cpp │ ├── unit_timeunit.cpp │ └── unit_to_s.cpp └── ubsan.supp /.cirrus.yml: -------------------------------------------------------------------------------- 1 | # examples from: 2 | # * https://github.com/bitcoin/bitcoin/blob/master/.cirrus.yml 3 | # * https://github.com/chux0519/pegasocks/blob/master/.cirrus.yml 4 | 5 | # global defaults 6 | env: 7 | PACKAGE_MANAGER_INSTALL: "apt-get update && apt-get install -y" 8 | CCACHE_SIZE: "50M" 9 | CCACHE_DIR: "/tmp/ccache_dir" 10 | CCACHE_NOHASHDIR: "1" # Debug info might contain a stale path if the build dir changes, but this is fine 11 | 12 | linux_task: 13 | container: 14 | matrix: 15 | - image: ubuntu:bionic 16 | - image: ubuntu:focal 17 | - image: debian:buster 18 | - image: gcc:latest 19 | 20 | ccache_cache: 21 | folder: "/tmp/ccache_dir" 22 | 23 | env_script: 24 | - uname -a 25 | 26 | install_script: 27 | - apt update && apt upgrade -y 28 | - DEBIAN_FRONTEND=noninteractive apt install -y cmake ninja-build build-essential ccache 29 | 30 | build_script: 31 | - cd build 32 | - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release 33 | - cmake --build . -- -j2 34 | 35 | test_script: 36 | - cd build 37 | - ./nb 38 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*, 3 | -altera-id-dependent-backward-branch, 4 | -altera-struct-pack-align, 5 | -altera-unroll-loops, 6 | -bugprone-easily-swappable-parameters, 7 | -cert-err58-cpp, 8 | -clang-diagnostic-padded, 9 | -cppcoreguidelines-avoid-magic-numbers, 10 | -cppcoreguidelines-macro-usage, 11 | -fuchsia-default-arguments-calls, 12 | -fuchsia-default-arguments, 13 | -fuchsia-multiple-inheritance, 14 | -fuchsia-overloaded-operator, 15 | -fuchsia-trailing-return, 16 | -google-explicit-constructor, 17 | -google-runtime-int, 18 | -hicpp-invalid-access-moved, 19 | -hicpp-no-array-decay, 20 | -hicpp-no-malloc, 21 | -hicpp-vararg, 22 | -llvm-header-guard, 23 | -llvmlibc-callee-namespace, 24 | -llvmlibc-implementation-in-namespace, 25 | -llvmlibc-restrict-system-libc-headers, 26 | -misc-definitions-in-headers, 27 | -modernize-concat-nested-namespaces, 28 | -modernize-macro-to-enum, 29 | -modernize-use-trailing-return-type, 30 | -readability-identifier-length, 31 | -readability-magic-numbers, 32 | ' 33 | WarningsAsErrors: '*' 34 | HeaderFilterRegex: 'nanobench\.h' 35 | ... 36 | 37 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [martinus] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '43 10 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp', 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .clangd/ 3 | .cache/ 4 | **/*.ppm 5 | **/perf.data 6 | **/perf.data.old 7 | /src/docs/_build 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: xenial 3 | 4 | # see https://github.com/nlohmann/json/blob/develop/.travis.yml 5 | matrix: 6 | include: 7 | 8 | - os: osx 9 | osx_image: xcode11.4 10 | compiler: clang 11 | env: 12 | - COMPILER=clang++ 13 | - CMAKE_OPTIONS="-DNB_cxx_standard=17" 14 | 15 | # compile on ARM 16 | - os: linux 17 | arch: arm64 18 | compiler: gcc 19 | env: 20 | - COMPILER=g++-8 21 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=11" 22 | addons: 23 | apt: 24 | sources: ['ubuntu-toolchain-r-test'] 25 | packages: ['g++-8'] 26 | 27 | 28 | # compile on S390x 29 | - os: linux 30 | arch: s390x 31 | compiler: gcc 32 | env: 33 | - COMPILER=g++ 34 | - CMAKE_OPTIONS="-DNB_sanitizer=OFF -DNB_cxx_standard=11" 35 | 36 | # g++-8 37 | - os: linux 38 | compiler: gcc 39 | env: 40 | - COMPILER=g++-8 41 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=11" 42 | addons: 43 | apt: 44 | sources: ['ubuntu-toolchain-r-test'] 45 | packages: ['g++-8'] 46 | 47 | # clang++-10 48 | - os: linux 49 | compiler: clang 50 | env: 51 | - COMPILER=clang++-10 52 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=11" 53 | addons: 54 | apt: 55 | sources: 56 | - ubuntu-toolchain-r-test 57 | - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main' 58 | key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' 59 | packages: 60 | - clang-10 61 | 62 | - os: linux 63 | compiler: clang 64 | env: 65 | - COMPILER=clang++-10 66 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=14" 67 | addons: 68 | apt: 69 | sources: 70 | - ubuntu-toolchain-r-test 71 | - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main' 72 | key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' 73 | packages: 74 | - clang-10 75 | 76 | - os: linux 77 | compiler: clang 78 | env: 79 | - COMPILER=clang++-10 80 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=17" 81 | addons: 82 | apt: 83 | sources: 84 | - ubuntu-toolchain-r-test 85 | - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main' 86 | key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' 87 | packages: 88 | - clang-10 89 | 90 | # gcc 8 91 | - os: linux 92 | compiler: gcc 93 | env: 94 | - COMPILER=g++-8 95 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=14" 96 | addons: 97 | apt: 98 | sources: ['ubuntu-toolchain-r-test'] 99 | packages: ['g++-8'] 100 | 101 | - os: linux 102 | compiler: gcc 103 | env: 104 | - COMPILER=g++-8 105 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=17" 106 | addons: 107 | apt: 108 | sources: ['ubuntu-toolchain-r-test'] 109 | packages: ['g++-8'] 110 | 111 | # g++-8 32bit 112 | - os: linux 113 | compiler: gcc 114 | env: 115 | - COMPILER=g++-8 116 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=11" 117 | - CXXFLAGS="-m32" 118 | - LDFLAGS="-m32" 119 | addons: 120 | apt: 121 | sources: ['ubuntu-toolchain-r-test'] 122 | packages: ['g++-8-multilib', 'linux-libc-dev:i386'] 123 | 124 | - os: linux 125 | compiler: gcc 126 | env: 127 | - COMPILER=g++-8 128 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=14" 129 | - CXXFLAGS="-m32" 130 | - LDFLAGS="-m32" 131 | addons: 132 | apt: 133 | sources: ['ubuntu-toolchain-r-test'] 134 | packages: ['g++-8-multilib', 'linux-libc-dev:i386'] 135 | 136 | - os: linux 137 | compiler: gcc 138 | env: 139 | - COMPILER=g++-8 140 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=17" 141 | - CXXFLAGS="-m32" 142 | - LDFLAGS="-m32" 143 | addons: 144 | apt: 145 | sources: ['ubuntu-toolchain-r-test'] 146 | packages: ['g++-8-multilib', 'linux-libc-dev:i386'] 147 | 148 | # clang++-6.0 149 | - os: linux 150 | compiler: clang 151 | env: 152 | - COMPILER=clang++-6.0 153 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=11" 154 | addons: 155 | apt: 156 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] 157 | packages: ['g++-6', 'clang-6.0',] 158 | 159 | - os: linux 160 | compiler: clang 161 | env: 162 | - COMPILER=clang++-6.0 163 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=14" 164 | addons: 165 | apt: 166 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] 167 | packages: ['g++-6', 'clang-6.0',] 168 | 169 | - os: linux 170 | compiler: clang 171 | env: 172 | - COMPILER=clang++-6.0 173 | - CMAKE_OPTIONS="-DNB_sanitizer=ON -DNB_cxx_standard=17" 174 | addons: 175 | apt: 176 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] 177 | packages: ['g++-6', 'clang-6.0',] 178 | 179 | install: 180 | - if [[ "${CODE_COVERAGE}" == "true" ]]; then gem install coveralls-lcov ; fi 181 | 182 | script: 183 | # make sure CXX is correctly set 184 | - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi 185 | 186 | # show OS/compiler version 187 | - uname -a 188 | - $CXX --version 189 | 190 | # build 191 | - cd build 192 | - cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} && cmake --build . -- -j2 193 | 194 | # check file 195 | - file ./nb 196 | 197 | # run test 198 | - ./nb 199 | 200 | # coverage 201 | - | 202 | if [[ "${CODE_COVERAGE}" = "true" ]]; then 203 | cmake --build . --target lcov 204 | fi 205 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(nanobench LANGUAGES CXX) 3 | 4 | # determine whether this is a standalone project or included by other projects 5 | set(NANOBENCH_STANDALONE_PROJECT OFF) 6 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 7 | set(NANOBENCH_STANDALONE_PROJECT ON) 8 | endif() 9 | 10 | if (NANOBENCH_STANDALONE_PROJECT) 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # generate compile_commands.json 12 | 13 | set(NB_cxx_standard "11" CACHE STRING "C++ standard, e.g. 11, 14, 17") 14 | 15 | set(CMAKE_CXX_STANDARD ${NB_cxx_standard}) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | set(CMAKE_CXX_EXTENSIONS OFF) 18 | 19 | # configuration see .clang-tidy 20 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 21 | find_program(CLANG_TIDY_PROGRAM NAMES clang-tidy) 22 | if(CLANG_TIDY_PROGRAM) 23 | set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_PROGRAM}") 24 | endif() 25 | endif() 26 | 27 | find_program(CCACHE_PROGRAM ccache) 28 | if(CCACHE_PROGRAM) 29 | SET(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") 30 | endif(CCACHE_PROGRAM) 31 | 32 | add_executable(nb "") 33 | 34 | if (NB_sanitizer) 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 36 | # see https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#silencing-unsigned-integer-overflow 37 | # Compile with -g and -fno-omit-frame-pointer to get proper debug information in your binary 38 | target_compile_options(nb PRIVATE -g) 39 | target_compile_options(nb PRIVATE -O2) 40 | target_compile_options(nb PRIVATE -fno-omit-frame-pointer) 41 | 42 | target_compile_options(nb PRIVATE -fsanitize=address) 43 | target_link_libraries(nb PRIVATE -fsanitize=address) 44 | 45 | target_compile_options(nb PRIVATE -fsanitize=undefined,float-divide-by-zero) 46 | target_link_libraries(nb PRIVATE -fsanitize=undefined,float-divide-by-zero) 47 | 48 | target_compile_options(nb PRIVATE -fsanitize=integer) 49 | target_link_libraries(nb PRIVATE -fsanitize=integer) 50 | 51 | target_compile_options(nb PRIVATE -fsanitize=nullability) 52 | target_link_libraries(nb PRIVATE -fsanitize=nullability) 53 | 54 | # we have to globally set the property here, so it actually works https://cmake.org/pipermail/cmake/2010-March/036020.html 55 | set_source_files_properties( 56 | src/test/tutorial_fast_v1.cpp 57 | src/test/tutorial_fast_v2.cpp 58 | PROPERTIES COMPILE_FLAGS "-fno-sanitize=integer") 59 | 60 | endif() 61 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 62 | # need to use gold linker, otherwise travis gets '/usr/bin/ld: --push-state: unknown option' error 63 | target_link_libraries(nb PRIVATE -fuse-ld=gold) 64 | 65 | target_compile_options(nb PRIVATE -g) 66 | target_compile_options(nb PRIVATE -O2) 67 | target_compile_options(nb PRIVATE -fno-omit-frame-pointer) 68 | 69 | target_compile_options(nb PRIVATE -fsanitize=undefined,float-divide-by-zero,float-cast-overflow) 70 | target_link_libraries(nb PRIVATE -fsanitize=undefined,float-divide-by-zero,float-cast-overflow) 71 | 72 | target_compile_options(nb PRIVATE -fsanitize=pointer-compare,pointer-subtract) 73 | target_link_libraries(nb PRIVATE -fsanitize=pointer-compare,pointer-subtract) 74 | 75 | target_compile_options(nb PRIVATE -fsanitize=address) 76 | target_link_libraries(nb PRIVATE -fsanitize=address) 77 | endif() 78 | endif() 79 | 80 | add_subdirectory(src) 81 | add_compile_flags_target(nb) 82 | 83 | target_sources_local(nb PUBLIC .clang-tidy) 84 | 85 | 86 | include(GNUInstallDirs) 87 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake) 88 | set(INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}) 89 | 90 | # Install library target 91 | add_library(nanobench STATIC ${PROJECT_SOURCE_DIR}/src/test/app/nanobench.cpp) 92 | target_compile_features(nanobench PUBLIC cxx_std_11) 93 | target_include_directories(nanobench 94 | PUBLIC 95 | $ 96 | $ 97 | ) 98 | install( 99 | TARGETS nanobench 100 | EXPORT install_targets 101 | LIBRARY DESTINATION ${INSTALL_LIBDIR} 102 | ARCHIVE DESTINATION ${INSTALL_LIBDIR} 103 | ) 104 | 105 | # Install targets file 106 | install(EXPORT install_targets 107 | FILE 108 | ${PROJECT_NAME}Targets.cmake 109 | NAMESPACE 110 | ${PROJECT_NAME}:: 111 | DESTINATION 112 | ${INSTALL_CONFIGDIR} 113 | ) 114 | 115 | # Install ${PROJECT_NAME}Config.cmake 116 | include(CMakePackageConfigHelpers) 117 | configure_package_config_file( 118 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in 119 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 120 | INSTALL_DESTINATION ${INSTALL_CONFIGDIR} 121 | ) 122 | install(FILES 123 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 124 | DESTINATION ${INSTALL_CONFIGDIR} 125 | ) 126 | 127 | # Install headers 128 | install(FILES src/include/nanobench.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 129 | else() 130 | add_library(nanobench STATIC ${PROJECT_SOURCE_DIR}/src/test/app/nanobench.cpp) 131 | add_library(nanobench::nanobench ALIAS nanobench) 132 | set_property(TARGET nanobench PROPERTY CXX_STANDARD 17) 133 | target_include_directories(nanobench PUBLIC ${PROJECT_SOURCE_DIR}/src/include) 134 | endif() 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2023 Martin Leitner-Ankerl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ankerl::nanobench 3 | 4 | ![ankerl::nanobench logo](src/docs/nanobench-logo-small.svg) 5 | 6 | [![Release](https://img.shields.io/github/release/martinus/nanobench.svg)](https://github.com/martinus/nanobench/releases) 7 | [![GitHub license](https://img.shields.io/github/license/martinus/nanobench.svg)](https://raw.githubusercontent.com/martinus/nanobench/master/LICENSE) 8 | [![Travis CI Build Status](https://travis-ci.com/martinus/nanobench.svg?branch=master)](https://travis-ci.com/martinus/nanobench) 9 | [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/martinus/nanobench?branch=master&svg=true)](https://ci.appveyor.com/project/martinus/nanobench) 10 | [![Join the chat at https://gitter.im/nanobench/community](https://badges.gitter.im/nanobench/community.svg)](https://gitter.im/nanobench/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 11 | 12 | `ankerl::nanobench` is a platform independent microbenchmarking library for C++11/14/17/20. 13 | 14 | ```cpp 15 | #define ANKERL_NANOBENCH_IMPLEMENT 16 | #include 17 | 18 | int main() { 19 | double d = 1.0; 20 | ankerl::nanobench::Bench().run("some double ops", [&] { 21 | d += 1.0 / d; 22 | if (d > 5.0) { 23 | d -= 5.0; 24 | } 25 | ankerl::nanobench::doNotOptimizeAway(d); 26 | }); 27 | } 28 | ``` 29 | 30 | The whole executable runs for ~60ms and prints 31 | 32 | ```markdown 33 | | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark 34 | |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- 35 | | 7.52 | 132,948,239.79 | 1.1% | 6.65 | 24.07 | 0.276 | 1.00 | 8.9% | 0.00 | `some double ops` 36 | ``` 37 | 38 | Which github renders as 39 | 40 | | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark 41 | |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- 42 | | 7.52 | 132,948,239.79 | 1.1% | 6.65 | 24.07 | 0.276 | 1.00 | 8.9% | 0.00 | `some double ops` 43 | 44 | The benchmarked code takes **7.52** nanoseconds to run, so ~**133** million times per seconds. Measurements fluctuate by 45 | **1.1%**. On average **6.65** instructions are executed in **24.07** CPU cycles, resulting in **0.276** instructions per 46 | cycle. A **single** branch is in the code, which branch prediction missed in **8.9%** of the cases. Total runtime of 47 | the benchmark with the name `some double ops` is **0.00**, so just a few milliseconds. 48 | 49 | # Design Goals 50 | 51 | * **Ease of use**: Simple & [powerful API](https://nanobench.ankerl.com/reference.html), fast compile times, [easy to integrate anywhere](https://nanobench.ankerl.com/tutorial.html#installation). 52 | * **Fast**: Get accurate results as fast as possible. nanobench is [~80 times faster than google benchmark](https://nanobench.ankerl.com/comparison.html#runtime). 53 | * **Accurate**: Get deterministic, repeatable, and accurate results that you can make sound decisions on. 54 | * **Robust**: Be robust against outliers, warn if results are not reliable. 55 | 56 | # Documentation 57 | 58 | [Extensive documentation is available](https://nanobench.ankerl.com). 59 | 60 | # More 61 | 62 | * [Code of Conduct](src/docs/CODE_OF_CONDUCT.md) - Contributor Covenant Code of Conduct 63 | * I need a better logo. Currently I use a small bench. Nanobench. Ha ha. 64 | 65 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: 2 | - Visual Studio 2022 3 | 4 | version: '{build}' 5 | 6 | init: 7 | - cmake --version 8 | - msbuild /version 9 | 10 | platform: 11 | - x86 12 | - x64 13 | 14 | before_build: 15 | - cmake . -DCMAKE_CXX_FLAGS="%CXX_FLAGS%" -DCMAKE_EXE_LINKER_FLAGS="%LINKER_FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" 16 | 17 | build_script: 18 | - cmake --build . --config Release 19 | 20 | test_script: 21 | - Release\nb.exe 22 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /cmake/nanobenchConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(nanobench_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | 3 | if(NOT TARGET nanobench::nanobench) 4 | include("${nanobench_CMAKE_DIR}/nanobenchTargets.cmake") 5 | endif() 6 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .doctrees 2 | .buildinfo 3 | _sources 4 | objects.inv 5 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | nanobench.ankerl.com -------------------------------------------------------------------------------- /docs/_static/_sphinx_javascript_frameworks_compat.js: -------------------------------------------------------------------------------- 1 | /* Compatability shim for jQuery and underscores.js. 2 | * 3 | * Copyright Sphinx contributors 4 | * Released under the two clause BSD licence 5 | */ 6 | 7 | /** 8 | * small helper function to urldecode strings 9 | * 10 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 11 | */ 12 | jQuery.urldecode = function(x) { 13 | if (!x) { 14 | return x 15 | } 16 | return decodeURIComponent(x.replace(/\+/g, ' ')); 17 | }; 18 | 19 | /** 20 | * small helper function to urlencode strings 21 | */ 22 | jQuery.urlencode = encodeURIComponent; 23 | 24 | /** 25 | * This function returns the parsed url parameters of the 26 | * current request. Multiple values per key are supported, 27 | * it will always return arrays of strings for the value parts. 28 | */ 29 | jQuery.getQueryParameters = function(s) { 30 | if (typeof s === 'undefined') 31 | s = document.location.search; 32 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 33 | var result = {}; 34 | for (var i = 0; i < parts.length; i++) { 35 | var tmp = parts[i].split('=', 2); 36 | var key = jQuery.urldecode(tmp[0]); 37 | var value = jQuery.urldecode(tmp[1]); 38 | if (key in result) 39 | result[key].push(value); 40 | else 41 | result[key] = [value]; 42 | } 43 | return result; 44 | }; 45 | 46 | /** 47 | * highlight a given string on a jquery object by wrapping it in 48 | * span elements with the given class name. 49 | */ 50 | jQuery.fn.highlightText = function(text, className) { 51 | function highlight(node, addItems) { 52 | if (node.nodeType === 3) { 53 | var val = node.nodeValue; 54 | var pos = val.toLowerCase().indexOf(text); 55 | if (pos >= 0 && 56 | !jQuery(node.parentNode).hasClass(className) && 57 | !jQuery(node.parentNode).hasClass("nohighlight")) { 58 | var span; 59 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 60 | if (isInSVG) { 61 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 62 | } else { 63 | span = document.createElement("span"); 64 | span.className = className; 65 | } 66 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 67 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 68 | document.createTextNode(val.substr(pos + text.length)), 69 | node.nextSibling)); 70 | node.nodeValue = val.substr(0, pos); 71 | if (isInSVG) { 72 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 73 | var bbox = node.parentElement.getBBox(); 74 | rect.x.baseVal.value = bbox.x; 75 | rect.y.baseVal.value = bbox.y; 76 | rect.width.baseVal.value = bbox.width; 77 | rect.height.baseVal.value = bbox.height; 78 | rect.setAttribute('class', className); 79 | addItems.push({ 80 | "parent": node.parentNode, 81 | "target": rect}); 82 | } 83 | } 84 | } 85 | else if (!jQuery(node).is("button, select, textarea")) { 86 | jQuery.each(node.childNodes, function() { 87 | highlight(this, addItems); 88 | }); 89 | } 90 | } 91 | var addItems = []; 92 | var result = this.each(function() { 93 | highlight(this, addItems); 94 | }); 95 | for (var i = 0; i < addItems.length; ++i) { 96 | jQuery(addItems[i].parent).before(addItems[i].target); 97 | } 98 | return result; 99 | }; 100 | 101 | /* 102 | * backward compatibility for jQuery.browser 103 | * This will be supported until firefox bug is fixed. 104 | */ 105 | if (!jQuery.browser) { 106 | jQuery.uaMatch = function(ua) { 107 | ua = ua.toLowerCase(); 108 | 109 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 110 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 111 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 112 | /(msie) ([\w.]+)/.exec(ua) || 113 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 114 | []; 115 | 116 | return { 117 | browser: match[ 1 ] || "", 118 | version: match[ 2 ] || "0" 119 | }; 120 | }; 121 | jQuery.browser = {}; 122 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 123 | } 124 | -------------------------------------------------------------------------------- /docs/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 14 | "TEXTAREA", 15 | "INPUT", 16 | "SELECT", 17 | "BUTTON", 18 | ]); 19 | 20 | const _ready = (callback) => { 21 | if (document.readyState !== "loading") { 22 | callback(); 23 | } else { 24 | document.addEventListener("DOMContentLoaded", callback); 25 | } 26 | }; 27 | 28 | /** 29 | * Small JavaScript module for the documentation. 30 | */ 31 | const Documentation = { 32 | init: () => { 33 | Documentation.initDomainIndexTable(); 34 | Documentation.initOnKeyListeners(); 35 | }, 36 | 37 | /** 38 | * i18n support 39 | */ 40 | TRANSLATIONS: {}, 41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 42 | LOCALE: "unknown", 43 | 44 | // gettext and ngettext don't access this so that the functions 45 | // can safely bound to a different name (_ = Documentation.gettext) 46 | gettext: (string) => { 47 | const translated = Documentation.TRANSLATIONS[string]; 48 | switch (typeof translated) { 49 | case "undefined": 50 | return string; // no translation 51 | case "string": 52 | return translated; // translation exists 53 | default: 54 | return translated[0]; // (singular, plural) translation tuple exists 55 | } 56 | }, 57 | 58 | ngettext: (singular, plural, n) => { 59 | const translated = Documentation.TRANSLATIONS[singular]; 60 | if (typeof translated !== "undefined") 61 | return translated[Documentation.PLURAL_EXPR(n)]; 62 | return n === 1 ? singular : plural; 63 | }, 64 | 65 | addTranslations: (catalog) => { 66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 67 | Documentation.PLURAL_EXPR = new Function( 68 | "n", 69 | `return (${catalog.plural_expr})` 70 | ); 71 | Documentation.LOCALE = catalog.locale; 72 | }, 73 | 74 | /** 75 | * helper function to focus on search bar 76 | */ 77 | focusSearchBar: () => { 78 | document.querySelectorAll("input[name=q]")[0]?.focus(); 79 | }, 80 | 81 | /** 82 | * Initialise the domain index toggle buttons 83 | */ 84 | initDomainIndexTable: () => { 85 | const toggler = (el) => { 86 | const idNumber = el.id.substr(7); 87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 88 | if (el.src.substr(-9) === "minus.png") { 89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 90 | toggledRows.forEach((el) => (el.style.display = "none")); 91 | } else { 92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 93 | toggledRows.forEach((el) => (el.style.display = "")); 94 | } 95 | }; 96 | 97 | const togglerElements = document.querySelectorAll("img.toggler"); 98 | togglerElements.forEach((el) => 99 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 100 | ); 101 | togglerElements.forEach((el) => (el.style.display = "")); 102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 103 | }, 104 | 105 | initOnKeyListeners: () => { 106 | // only install a listener if it is really needed 107 | if ( 108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 110 | ) 111 | return; 112 | 113 | document.addEventListener("keydown", (event) => { 114 | // bail for input elements 115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 116 | // bail with special keys 117 | if (event.altKey || event.ctrlKey || event.metaKey) return; 118 | 119 | if (!event.shiftKey) { 120 | switch (event.key) { 121 | case "ArrowLeft": 122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 123 | 124 | const prevLink = document.querySelector('link[rel="prev"]'); 125 | if (prevLink && prevLink.href) { 126 | window.location.href = prevLink.href; 127 | event.preventDefault(); 128 | } 129 | break; 130 | case "ArrowRight": 131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 132 | 133 | const nextLink = document.querySelector('link[rel="next"]'); 134 | if (nextLink && nextLink.href) { 135 | window.location.href = nextLink.href; 136 | event.preventDefault(); 137 | } 138 | break; 139 | } 140 | } 141 | 142 | // some keyboard layouts may need Shift to get / 143 | switch (event.key) { 144 | case "/": 145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 146 | Documentation.focusSearchBar(); 147 | event.preventDefault(); 148 | } 149 | }); 150 | }, 151 | }; 152 | 153 | // quick alias for translations 154 | const _ = Documentation.gettext; 155 | 156 | _ready(Documentation.init); 157 | -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '', 4 | LANGUAGE: 'en', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false, 12 | SHOW_SEARCH_SUMMARY: true, 13 | ENABLE_SEARCH_SHORTCUTS: true, 14 | }; -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /docs/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /docs/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/nanobench-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/nanobench-logo.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #9C6500 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #E40000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #008400 } /* Generic.Inserted */ 23 | .highlight .go { color: #717171 } /* Generic.Output */ 24 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #B00040 } /* Keyword.Type */ 34 | .highlight .m { color: #666666 } /* Literal.Number */ 35 | .highlight .s { color: #BA2121 } /* Literal.String */ 36 | .highlight .na { color: #687822 } /* Name.Attribute */ 37 | .highlight .nb { color: #008000 } /* Name.Builtin */ 38 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #880000 } /* Name.Constant */ 40 | .highlight .nd { color: #AA22FF } /* Name.Decorator */ 41 | .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ 43 | .highlight .nf { color: #0000FF } /* Name.Function */ 44 | .highlight .nl { color: #767600 } /* Name.Label */ 45 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #19177C } /* Name.Variable */ 48 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #666666 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */ 58 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ 61 | .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #008000 } /* Literal.String.Other */ 65 | .highlight .sr { color: #A45A77 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ 67 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */ 70 | .highlight .vc { color: #19177C } /* Name.Variable.Class */ 71 | .highlight .vg { color: #19177C } /* Name.Variable.Global */ 72 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */ 74 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/sphinx_highlight.js: -------------------------------------------------------------------------------- 1 | /* Highlighting utilities for Sphinx HTML documentation. */ 2 | "use strict"; 3 | 4 | const SPHINX_HIGHLIGHT_ENABLED = true 5 | 6 | /** 7 | * highlight a given string on a node by wrapping it in 8 | * span elements with the given class name. 9 | */ 10 | const _highlight = (node, addItems, text, className) => { 11 | if (node.nodeType === Node.TEXT_NODE) { 12 | const val = node.nodeValue; 13 | const parent = node.parentNode; 14 | const pos = val.toLowerCase().indexOf(text); 15 | if ( 16 | pos >= 0 && 17 | !parent.classList.contains(className) && 18 | !parent.classList.contains("nohighlight") 19 | ) { 20 | let span; 21 | 22 | const closestNode = parent.closest("body, svg, foreignObject"); 23 | const isInSVG = closestNode && closestNode.matches("svg"); 24 | if (isInSVG) { 25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 26 | } else { 27 | span = document.createElement("span"); 28 | span.classList.add(className); 29 | } 30 | 31 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 32 | parent.insertBefore( 33 | span, 34 | parent.insertBefore( 35 | document.createTextNode(val.substr(pos + text.length)), 36 | node.nextSibling 37 | ) 38 | ); 39 | node.nodeValue = val.substr(0, pos); 40 | 41 | if (isInSVG) { 42 | const rect = document.createElementNS( 43 | "http://www.w3.org/2000/svg", 44 | "rect" 45 | ); 46 | const bbox = parent.getBBox(); 47 | rect.x.baseVal.value = bbox.x; 48 | rect.y.baseVal.value = bbox.y; 49 | rect.width.baseVal.value = bbox.width; 50 | rect.height.baseVal.value = bbox.height; 51 | rect.setAttribute("class", className); 52 | addItems.push({ parent: parent, target: rect }); 53 | } 54 | } 55 | } else if (node.matches && !node.matches("button, select, textarea")) { 56 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 57 | } 58 | }; 59 | const _highlightText = (thisNode, text, className) => { 60 | let addItems = []; 61 | _highlight(thisNode, addItems, text, className); 62 | addItems.forEach((obj) => 63 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 64 | ); 65 | }; 66 | 67 | /** 68 | * Small JavaScript module for the documentation. 69 | */ 70 | const SphinxHighlight = { 71 | 72 | /** 73 | * highlight the search words provided in localstorage in the text 74 | */ 75 | highlightSearchWords: () => { 76 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight 77 | 78 | // get and clear terms from localstorage 79 | const url = new URL(window.location); 80 | const highlight = 81 | localStorage.getItem("sphinx_highlight_terms") 82 | || url.searchParams.get("highlight") 83 | || ""; 84 | localStorage.removeItem("sphinx_highlight_terms") 85 | url.searchParams.delete("highlight"); 86 | window.history.replaceState({}, "", url); 87 | 88 | // get individual terms from highlight string 89 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 90 | if (terms.length === 0) return; // nothing to do 91 | 92 | // There should never be more than one element matching "div.body" 93 | const divBody = document.querySelectorAll("div.body"); 94 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 95 | window.setTimeout(() => { 96 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 97 | }, 10); 98 | 99 | const searchBox = document.getElementById("searchbox"); 100 | if (searchBox === null) return; 101 | searchBox.appendChild( 102 | document 103 | .createRange() 104 | .createContextualFragment( 105 | '" 109 | ) 110 | ); 111 | }, 112 | 113 | /** 114 | * helper function to hide the search marks again 115 | */ 116 | hideSearchWords: () => { 117 | document 118 | .querySelectorAll("#searchbox .highlight-link") 119 | .forEach((el) => el.remove()); 120 | document 121 | .querySelectorAll("span.highlighted") 122 | .forEach((el) => el.classList.remove("highlighted")); 123 | localStorage.removeItem("sphinx_highlight_terms") 124 | }, 125 | 126 | initEscapeListener: () => { 127 | // only install a listener if it is really needed 128 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 129 | 130 | document.addEventListener("keydown", (event) => { 131 | // bail for input elements 132 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 133 | // bail with special keys 134 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; 135 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { 136 | SphinxHighlight.hideSearchWords(); 137 | event.preventDefault(); 138 | } 139 | }); 140 | }, 141 | }; 142 | 143 | _ready(SphinxHighlight.highlightSearchWords); 144 | _ready(SphinxHighlight.initEscapeListener); 145 | -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | # see https://clang.llvm.org/docs/ClangFormatStyleOptions.html 2 | --- 3 | BasedOnStyle: LLVM 4 | Language: Cpp 5 | Standard: Cpp11 6 | 7 | # very limited width, so examples look good in the generated documentation 8 | ColumnLimit: 80 9 | 10 | AccessModifierOffset: -4 11 | IndentWidth: 4 12 | UseTab: Never 13 | 14 | AlignEscapedNewlines: Left 15 | AllowShortFunctionsOnASingleLine: Empty 16 | AllowShortLambdasOnASingleLine: Empty 17 | AlwaysBreakTemplateDeclarations: true 18 | BreakConstructorInitializers: BeforeComma 19 | IndentPPDirectives: AfterHash 20 | PointerAlignment: Left 21 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(cmake) 2 | add_subdirectory(include) 3 | add_subdirectory(test) 4 | -------------------------------------------------------------------------------- /src/cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # NOTE: This helper function assumes no generator expressions are used for the source files 2 | # source: https://crascit.com/2016/01/31/enhanced-source-file-handling-with-target_sources/ 3 | function(target_sources_local target) 4 | if(POLICY CMP0076) 5 | # New behavior is available, so just forward to it by ensuring 6 | # that we have the policy set to request the new behavior, but 7 | # don't change the policy setting for the calling scope 8 | cmake_policy(PUSH) 9 | cmake_policy(SET CMP0076 NEW) 10 | target_sources(${target} ${ARGN}) 11 | cmake_policy(POP) 12 | return() 13 | endif() 14 | 15 | # Must be using CMake 3.12 or earlier, so simulate the new behavior 16 | unset(_srcList) 17 | get_target_property(_targetSourceDir ${target} SOURCE_DIR) 18 | 19 | foreach(src ${ARGN}) 20 | if(NOT src STREQUAL "PRIVATE" AND 21 | NOT src STREQUAL "PUBLIC" AND 22 | NOT src STREQUAL "INTERFACE" AND 23 | NOT IS_ABSOLUTE "${src}") 24 | # Relative path to source, prepend relative to where target was defined 25 | file(RELATIVE_PATH src "${_targetSourceDir}" "${CMAKE_CURRENT_LIST_DIR}/${src}") 26 | endif() 27 | list(APPEND _srcList ${src}) 28 | endforeach() 29 | target_sources(${target} ${_srcList}) 30 | endfunction() 31 | 32 | MESSAGE(STATUS "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}, C++${CMAKE_CXX_STANDARD}, CMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}") 33 | 34 | function(add_compile_flags_target target) 35 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 36 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) 37 | target_compile_options(${target} PRIVATE -fdiagnostics-color) 38 | target_compile_options(${target} PRIVATE -Wno-zero-as-null-pointer-constant) # doesn't work with spaceship operator 39 | endif() 40 | target_compile_options(${target} PRIVATE -Werror) 41 | target_compile_options(${target} PRIVATE -pedantic) 42 | target_compile_options(${target} PRIVATE -pedantic-errors) 43 | target_compile_options(${target} PRIVATE -fvisibility=hidden) 44 | target_compile_options(${target} PRIVATE -fstrict-aliasing) 45 | endif() 46 | 47 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") 48 | target_compile_options(${target} PRIVATE -Wall) 49 | target_compile_options(${target} PRIVATE -Wextra) 50 | target_compile_options(${target} PRIVATE -fdiagnostics-show-option) 51 | target_compile_options(${target} PRIVATE -Wconversion) 52 | target_compile_options(${target} PRIVATE -Wold-style-cast) 53 | target_compile_options(${target} PRIVATE -Wfloat-equal) 54 | target_compile_options(${target} PRIVATE -Wlogical-op) 55 | target_compile_options(${target} PRIVATE -Wundef) 56 | target_compile_options(${target} PRIVATE -Wredundant-decls) 57 | target_compile_options(${target} PRIVATE -Wshadow) 58 | target_compile_options(${target} PRIVATE -Wstrict-overflow=2) 59 | target_compile_options(${target} PRIVATE -Wwrite-strings) 60 | target_compile_options(${target} PRIVATE -Wpointer-arith) 61 | target_compile_options(${target} PRIVATE -Wcast-qual) 62 | target_compile_options(${target} PRIVATE -Wformat=2) 63 | # target_compile_options(${target} PRIVATE -Wswitch-default) 64 | target_compile_options(${target} PRIVATE -Wmissing-include-dirs) 65 | target_compile_options(${target} PRIVATE -Wcast-align) 66 | target_compile_options(${target} PRIVATE -Wswitch-enum) 67 | target_compile_options(${target} PRIVATE -Wnon-virtual-dtor) 68 | target_compile_options(${target} PRIVATE -Wctor-dtor-privacy) 69 | target_compile_options(${target} PRIVATE -Wsign-conversion) 70 | target_compile_options(${target} PRIVATE -Wdisabled-optimization) 71 | target_compile_options(${target} PRIVATE -Weffc++) 72 | # target_compile_options(${target} PRIVATE -Winline) 73 | target_compile_options(${target} PRIVATE -Winvalid-pch) 74 | target_compile_options(${target} PRIVATE -Wmissing-declarations) 75 | target_compile_options(${target} PRIVATE -Woverloaded-virtual) 76 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) 77 | target_compile_options(${target} PRIVATE -Wnoexcept) 78 | endif() 79 | 80 | # no way to silence it in the expression decomposition macros: _Pragma() in macros doesn't work for the c++ front-end of g++ 81 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 82 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69543 83 | # Also the warning is completely worthless nowadays - http://stackoverflow.com/questions/14016993 84 | #target_compile_options(${target} PRIVATE -Waggregate-return) 85 | 86 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) 87 | target_compile_options(${target} PRIVATE -Wdouble-promotion) 88 | target_compile_options(${target} PRIVATE -Wtrampolines) 89 | #target_compile_options(${target} PRIVATE -Wzero-as-null-pointer-constant) # doesn't work with spaceship operator 90 | #target_compile_options(${target} PRIVATE -Wuseless-cast) 91 | target_compile_options(${target} PRIVATE -Wvector-operation-performance) 92 | endif() 93 | 94 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) 95 | target_compile_options(${target} PRIVATE -Wshift-overflow=2) 96 | target_compile_options(${target} PRIVATE -Wnull-dereference) 97 | target_compile_options(${target} PRIVATE -Wduplicated-cond) 98 | endif() 99 | 100 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) 101 | target_compile_options(${target} PRIVATE -Walloc-zero) 102 | target_compile_options(${target} PRIVATE -Walloca) 103 | target_compile_options(${target} PRIVATE -Wduplicated-branches) 104 | endif() 105 | 106 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) 107 | target_compile_options(${target} PRIVATE -Wcast-align=strict) 108 | endif() 109 | endif() 110 | 111 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 112 | target_compile_options(${target} PRIVATE -Weverything) 113 | target_compile_options(${target} PRIVATE -Wno-c++98-compat) 114 | target_compile_options(${target} PRIVATE -Wno-c++98-compat-pedantic) 115 | target_compile_options(${target} PRIVATE -Wno-c++98-compat-bind-to-temporary-copy) 116 | target_compile_options(${target} PRIVATE -Wno-c++98-compat-local-type-template-args) 117 | target_compile_options(${target} PRIVATE -Qunused-arguments -fcolor-diagnostics) # needed for ccache integration on travis 118 | endif() 119 | 120 | if(MSVC) 121 | target_compile_options(${target} PRIVATE /EHsc) # exception handling 122 | target_compile_options(${target} PRIVATE /std:c++latest) # for post c++14 updates in MSVC 123 | target_compile_options(${target} PRIVATE /permissive-) # force standard conformance - this is the better flag than /Za 124 | target_compile_options(${target} PRIVATE /W4) 125 | # target_compile_options(${target} PRIVATE /Wall) # turns on warnings from levels 1 through 4 which are off by default - https://msdn.microsoft.com/en-us/library/23k5d385.aspx 126 | 127 | add_definitions(/MP) # parallel builds 128 | 129 | target_compile_options(${target} PRIVATE 130 | /wd4514 # unreferenced inline function has been removed 131 | /wd4571 # SEH related 132 | /wd4710 # function not inlined 133 | /wd4711 # function 'x' selected for automatic inline expansion 134 | 135 | /wd4616 # invalid compiler warnings - https://msdn.microsoft.com/en-us/library/t7ab6xtd.aspx 136 | /wd4619 # invalid compiler warnings - https://msdn.microsoft.com/en-us/library/tacee08d.aspx 137 | 138 | /wd4820 # padding in structs 139 | /wd4625 # copy constructor was implicitly defined as deleted 140 | /wd4626 # assignment operator was implicitly defined as deleted 141 | /wd5027 # move assignment operator was implicitly defined as deleted 142 | /wd5026 # move constructor was implicitly defined as deleted 143 | /wd4623 # default constructor was implicitly defined as deleted 144 | /wd4774 # 'swprintf_s' : format string expected in argument 3 is not a string literal 145 | 146 | /wd5045 # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified 147 | /wd5039 #: '_Thrd_start': pointer or reference to potentially throwing function passed to extern C function under -EHc. Undefined behavior 148 | ) 149 | endif() 150 | 151 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" ) 152 | target_compile_options(${target} PRIVATE -ggdb -g) 153 | 154 | # enable lots of warning checking 155 | #target_compile_options(robinhood-test PRIVATE -Werror -Wall -Wextra -Weffc++ -Wconversion -Wunreachable-code -Wuninitialized -Wshadow -Wfloat-equal -Wmissing-braces) 156 | 157 | # make sure OpenMP in the tests work 158 | #target_compile_options(robinhood-test PRIVATE -fopenmp) 159 | #target_link_libraries(robinhood-test PRIVATE -fopenmp) 160 | 161 | #target_compile_options(robinhood-test PRIVATE -fsanitize=undefined,float-divide-by-zero,float-cast-overflow) 162 | #target_link_libraries(robinhood-test PRIVATE -fsanitize=undefined,float-divide-by-zero,float-cast-overflow) 163 | 164 | #target_compile_options(robinhood-test PRIVATE -fsanitize=address) 165 | #target_link_libraries(robinhood-test PRIVATE -fsanitize=address) 166 | 167 | # warns in doctest.h :( 168 | #target_compile_options(robinhood-test PRIVATE -fsanitize=memory) 169 | #target_link_libraries(robinhood-test PRIVATE -fsanitize=memory) 170 | endif() 171 | endfunction() 172 | -------------------------------------------------------------------------------- /src/comparisons/DigitalInBlue_celero/.gitignore: -------------------------------------------------------------------------------- 1 | celero/ 2 | ce -------------------------------------------------------------------------------- /src/comparisons/DigitalInBlue_celero/celero.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | // g++ -O2 main.cpp -I. -L /home/martinus/git/Celero/build -lcelero -o ce 6 | // LD_LIBRARY_PATH=/home/martinus/git/Celero/build ./ce 7 | CELERO_MAIN 8 | 9 | // This gives segmentation fault :-( 10 | 11 | uint64_t x = 1; 12 | BENCHMARK(FrameworkComparison, ComparisonFast, 50, 50000) { 13 | celero::DoNotOptimizeAway(x += x); 14 | } 15 | -------------------------------------------------------------------------------- /src/comparisons/cameron317_microbench/.gitignore: -------------------------------------------------------------------------------- 1 | systemtime.* 2 | microbench.* 3 | mb 4 | -------------------------------------------------------------------------------- /src/comparisons/cameron317_microbench/microbench.cpp: -------------------------------------------------------------------------------- 1 | #include "microbench.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // g++ -O2 -c systemtime.cpp 9 | // g++ -O2 -c microbench.cpp 10 | // g++ microbench.o systemtime.o -o mb 11 | int main(int, char**) { 12 | // something fast 13 | uint64_t x = 1; 14 | std::cout << moodycamel::microbench( 15 | [&]() { 16 | x += x; 17 | }, 18 | 10000000, 51) 19 | << " sec x += x (x==" << x << ")" << std::endl; 20 | 21 | std::cout << moodycamel::microbench([&] { 22 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 23 | }) << " sec sleep 10ms" 24 | << std::endl; 25 | 26 | std::random_device dev; 27 | std::mt19937_64 rng(dev()); 28 | std::cout << moodycamel::microbench( 29 | [&] { 30 | // each run, perform a random number of rng calls 31 | auto iterations = rng() & UINT64_C(0xff); 32 | for (uint64_t i = 0; i < iterations; ++i) { 33 | (void)rng(); 34 | } 35 | }, 36 | 1000, 51) 37 | << " sec random fluctuations" << std::endl; 38 | } 39 | -------------------------------------------------------------------------------- /src/comparisons/cameron317_microbench/out.txt: -------------------------------------------------------------------------------- 1 | 3.12506e-07 sec x += x (x==0) 2 | 10.056 sec sleep 10ms 3 | 0.000661384 sec random fluctuations 4 | -------------------------------------------------------------------------------- /src/comparisons/catchorg_Catch2/.gitignore: -------------------------------------------------------------------------------- 1 | catch.hpp 2 | -------------------------------------------------------------------------------- /src/comparisons/catchorg_Catch2/catch.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/catchorg/Catch2 2 | // g++ -O2 catch.cpp -o c 3 | 4 | #define CATCH_CONFIG_ENABLE_BENCHMARKING 5 | #define CATCH_CONFIG_MAIN 6 | #include "catch.hpp" // NOLINT 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE("comparison_fast"){ 13 | uint64_t x = 1; 14 | BENCHMARK("x += x") { 15 | return x += x; 16 | }; 17 | } 18 | 19 | TEST_CASE("comparison_slow") { 20 | BENCHMARK("sleep 10ms") { 21 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 22 | }; 23 | } 24 | 25 | // NOLINTNEXTLINE(fuchsia-statically-constructed-objects,llvmlibc-implementation-in-namespace) 26 | TEST_CASE("comparison_fluctuating_v2") { 27 | std::random_device dev; 28 | std::mt19937_64 rng(dev()); 29 | BENCHMARK("random fluctuations") { 30 | // each run, perform a random number of rng calls 31 | auto iterations = rng() & UINT64_C(0xff); 32 | for (uint64_t i = 0; i < iterations; ++i) { 33 | (void)rng(); 34 | } 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/comparisons/catchorg_Catch2/out.txt: -------------------------------------------------------------------------------- 1 | 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | c is a Catch v2.9.2 host application. 4 | Run with -? for options 5 | 6 | ------------------------------------------------------------------------------- 7 | comparison_fast 8 | ------------------------------------------------------------------------------- 9 | catch.cpp:12 10 | ............................................................................... 11 | 12 | benchmark name samples iterations estimated 13 | mean low mean high mean 14 | std dev low std dev high std dev 15 | ------------------------------------------------------------------------------- 16 | x += x 100 12414 1.2414 ms 17 | 1 ns 1 ns 1 ns 18 | 0 ns 0 ns 0 ns 19 | 20 | 21 | ------------------------------------------------------------------------------- 22 | comparison_slow 23 | ------------------------------------------------------------------------------- 24 | catch.cpp:19 25 | ............................................................................... 26 | 27 | benchmark name samples iterations estimated 28 | mean low mean high mean 29 | std dev low std dev high std dev 30 | ------------------------------------------------------------------------------- 31 | sleep 10ms 100 1 1.01319 s 32 | 10.1357 ms 10.1302 ms 10.1396 ms 33 | 23.539 us 18.061 us 29.575 us 34 | 35 | 36 | ------------------------------------------------------------------------------- 37 | comparison_fluctuating_v2 38 | ------------------------------------------------------------------------------- 39 | catch.cpp:25 40 | ............................................................................... 41 | 42 | benchmark name samples iterations estimated 43 | mean low mean high mean 44 | std dev low std dev high std dev 45 | ------------------------------------------------------------------------------- 46 | random fluctuations 100 28 2.3324 ms 47 | 827 ns 810 ns 844 ns 48 | 88 ns 79 ns 99 ns 49 | 50 | 51 | =============================================================================== 52 | test cases: 3 | 3 passed 53 | assertions: - none - 54 | 55 | -------------------------------------------------------------------------------- /src/comparisons/google_benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | benchmark.h 2 | -------------------------------------------------------------------------------- /src/comparisons/google_benchmark/gbench: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/src/comparisons/google_benchmark/gbench -------------------------------------------------------------------------------- /src/comparisons/google_benchmark/gbench.cpp: -------------------------------------------------------------------------------- 1 | #include "benchmark.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Build instructions: https://github.com/google/benchmark#installation 8 | // curl --output benchmark.h 9 | // https://raw.githubusercontent.com/google/benchmark/master/include/benchmark/benchmark.h 10 | // g++ -O2 main.cpp -Lgit/benchmark/build/src -lbenchmark -lpthread -o m 11 | void ComparisonFast(benchmark::State& state) { 12 | uint64_t x = 1; 13 | for (auto _ : state) { 14 | x += x; 15 | } 16 | benchmark::DoNotOptimize(x); 17 | } 18 | BENCHMARK(ComparisonFast); 19 | 20 | void ComparisonSlow(benchmark::State& state) { 21 | for (auto _ : state) { 22 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 23 | } 24 | } 25 | BENCHMARK(ComparisonSlow); 26 | 27 | void ComparisonFluctuating(benchmark::State& state) { 28 | std::random_device dev; 29 | std::mt19937_64 rng(dev()); 30 | for (auto _ : state) { 31 | // each run, perform a random number of rng calls 32 | auto iterations = rng() & UINT64_C(0xff); 33 | for (uint64_t i = 0; i < iterations; ++i) { 34 | (void)rng(); 35 | } 36 | } 37 | } 38 | BENCHMARK(ComparisonFluctuating); 39 | 40 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /src/comparisons/google_benchmark/out.txt: -------------------------------------------------------------------------------- 1 | 2019-10-12 12:03:25 2 | Running ./gbench 3 | Run on (12 X 4600 MHz CPU s) 4 | CPU Caches: 5 | L1 Data 32K (x6) 6 | L1 Instruction 32K (x6) 7 | L2 Unified 256K (x6) 8 | L3 Unified 12288K (x1) 9 | Load Average: 0.21, 0.55, 0.60 10 | ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead. 11 | ---------------------------------------------------------------- 12 | Benchmark Time CPU Iterations 13 | ---------------------------------------------------------------- 14 | ComparisonFast 0.313 ns 0.313 ns 1000000000 15 | ComparisonSlow 10137913 ns 3920 ns 1000 16 | ComparisonFluctuating 993 ns 992 ns 706946 17 | -------------------------------------------------------------------------------- /src/comparisons/iboB_picobench/.gitignore: -------------------------------------------------------------------------------- 1 | picobench.hpp 2 | pb -------------------------------------------------------------------------------- /src/comparisons/iboB_picobench/out.txt: -------------------------------------------------------------------------------- 1 | ComparisonFast: 2 | =============================================================================== 3 | Name (baseline is *) | Dim | Total ms | ns/op |Baseline| Ops/second 4 | =============================================================================== 5 | ComparisonFast * | 8 | 0.000 | 6 | - |156862745.1 6 | ComparisonFast * | 64 | 0.000 | 1 | - |512000000.0 7 | ComparisonFast * | 512 | 0.000 | 0 | - |2560000000.0 8 | ComparisonFast * | 4096 | 0.001 | 0 | - |3110098709.2 9 | ComparisonFast * | 8192 | 0.003 | 0 | - |3141104294.5 10 | =============================================================================== 11 | ComparisonSlow: 12 | =============================================================================== 13 | Name (baseline is *) | Dim | Total ms | ns/op |Baseline| Ops/second 14 | =============================================================================== 15 | ComparisonSlow * | 1 | 10.056 |10055959 | - | 99.4 16 | ComparisonSlow * | 2 | 20.178 |10088773 | - | 99.1 17 | ComparisonSlow * | 5 | 50.570 |10114054 | - | 98.9 18 | ComparisonSlow * | 10 | 101.136 |10113643 | - | 98.9 19 | =============================================================================== 20 | fluctuating: 21 | =============================================================================== 22 | Name (baseline is *) | Dim | Total ms | ns/op |Baseline| Ops/second 23 | =============================================================================== 24 | ComparisonFluctuating * | 8 | 0.012 | 1551 | - | 644485.6 25 | ComparisonFluctuating * | 64 | 0.068 | 1057 | - | 945584.6 26 | ComparisonFluctuating * | 512 | 0.565 | 1103 | - | 906222.0 27 | ComparisonFluctuating * | 4096 | 4.469 | 1090 | - | 916619.4 28 | ComparisonFluctuating * | 8192 | 9.003 | 1098 | - | 909957.2 29 | =============================================================================== 30 | -------------------------------------------------------------------------------- /src/comparisons/iboB_picobench/picobench.cpp: -------------------------------------------------------------------------------- 1 | #define PICOBENCH_IMPLEMENT_WITH_MAIN 2 | #include "picobench.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | // https://github.com/iboB/picobench 11 | // g++ -O2 picobench.cpp -o pb 12 | 13 | PICOBENCH_SUITE("ComparisonFast"); 14 | static void ComparisonFast(picobench::state& state) { 15 | uint64_t x = 1; 16 | for (auto _ : state) { 17 | x += x; 18 | } 19 | state.set_result(x); 20 | } 21 | PICOBENCH(ComparisonFast); 22 | 23 | PICOBENCH_SUITE("ComparisonSlow"); 24 | void ComparisonSlow(picobench::state& state) { 25 | for (auto _ : state) { 26 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 27 | } 28 | } 29 | PICOBENCH(ComparisonSlow).iterations({1, 2, 5, 10}); 30 | 31 | PICOBENCH_SUITE("fluctuating"); 32 | void ComparisonFluctuating(picobench::state& state) { 33 | std::random_device dev; 34 | std::mt19937_64 rng(dev()); 35 | for (auto _ : state) { 36 | // each run, perform a random number of rng calls 37 | auto iterations = rng() & UINT64_C(0xff); 38 | for (uint64_t i = 0; i < iterations; ++i) { 39 | (void)rng(); 40 | } 41 | } 42 | } 43 | PICOBENCH(ComparisonFluctuating); 44 | -------------------------------------------------------------------------------- /src/comparisons/ivafanas_sltbench/main.cpp: -------------------------------------------------------------------------------- 1 | #include // https://github.com/ivafanas/sltbench 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // cmake build as online instructions describes 8 | // 9 | // g++ -O3 -I/home/martinus/git/sltbench/install/include -c main.cpp 10 | // g++ -o m -L/home/martinus/git/sltbench/install/lib main.o -lsltbench 11 | 12 | uint64_t x = 1; 13 | void ComparisonFast() { 14 | sltbench::DoNotOptimize(x += x); 15 | } 16 | 17 | SLTBENCH_FUNCTION(ComparisonFast); 18 | 19 | void ComparisonSlow() { 20 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 21 | } 22 | SLTBENCH_FUNCTION(ComparisonSlow); 23 | 24 | std::random_device dev; 25 | std::mt19937_64 rng(dev()); 26 | 27 | void ComparisonFluctuating() { 28 | // each run, perform a random number of rng calls 29 | auto iterations = rng() & UINT64_C(0xff); 30 | for (uint64_t i = 0; i < iterations; ++i) { 31 | (void)rng(); 32 | } 33 | } 34 | SLTBENCH_FUNCTION(ComparisonFluctuating); 35 | 36 | SLTBENCH_MAIN(); 37 | -------------------------------------------------------------------------------- /src/comparisons/ivafanas_sltbench/out.txt: -------------------------------------------------------------------------------- 1 | benchmark arg status time(ns) 2 | ComparisonFast ok 1 3 | ComparisonFluctuating ok 20 4 | ComparisonSlow ok 10055943 5 | -------------------------------------------------------------------------------- /src/comparisons/libnonius_nonius/.gitignore: -------------------------------------------------------------------------------- 1 | nonius/ -------------------------------------------------------------------------------- /src/comparisons/libnonius_nonius/main.cpp: -------------------------------------------------------------------------------- 1 | #define NONIUS_RUNNER 2 | #include 3 | 4 | // g++ -O2 main.cpp -pthread -I. -o m 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | NONIUS_PARAM(X, UINT64_C(1)) 11 | 12 | template 13 | struct volatilize_fn { 14 | Fn fn; 15 | auto operator()() const -> decltype(fn()) { 16 | volatile auto x = fn(); 17 | return x; 18 | } 19 | }; 20 | 21 | template 22 | auto volatilize(Fn&& fn) -> volatilize_fn::type> { 23 | return {std::forward(fn)}; 24 | } 25 | 26 | NONIUS_BENCHMARK("x += x", [](nonius::chronometer meter) { 27 | auto x = meter.param(); 28 | meter.measure(volatilize([&]() { 29 | return x += x; 30 | })); 31 | }) 32 | 33 | NONIUS_BENCHMARK("sleep 10ms", [] { 34 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 35 | }) 36 | 37 | NONIUS_BENCHMARK("random fluctuations", [](nonius::chronometer meter) { 38 | std::random_device dev; 39 | std::mt19937_64 rng(dev()); 40 | meter.measure([&] { 41 | // each run, perform a random number of rng calls 42 | auto iterations = rng() & UINT64_C(0xff); 43 | for (uint64_t i = 0; i < iterations; ++i) { 44 | (void)rng(); 45 | } 46 | }); 47 | }) 48 | -------------------------------------------------------------------------------- /src/comparisons/libnonius_nonius/out.txt: -------------------------------------------------------------------------------- 1 | clock resolution: mean is 22.0426 ns (20480002 iterations) 2 | 3 | 4 | new round for parameters 5 | X = 1 6 | 7 | benchmarking x += x 8 | collecting 100 samples, 56376 iterations each, in estimated 0 ns 9 | mean: 0.391109 ns, lb 0.391095 ns, ub 0.391135 ns, ci 0.95 10 | std dev: 9.50619e-05 ns, lb 6.25215e-05 ns, ub 0.000167224 ns, ci 0.95 11 | found 4 outliers among 100 samples (4%) 12 | variance is unaffected by outliers 13 | 14 | benchmarking sleep 10ms 15 | collecting 100 samples, 1 iterations each, in estimated 1013.66 ms 16 | mean: 10.1258 ms, lb 10.1189 ms, ub 10.1313 ms, ci 0.95 17 | std dev: 31.1777 μs, lb 26.5814 μs, ub 35.4952 μs, ci 0.95 18 | found 13 outliers among 100 samples (13%) 19 | variance is unaffected by outliers 20 | 21 | benchmarking random fluctuations 22 | collecting 100 samples, 23 iterations each, in estimated 2.2724 ms 23 | mean: 1016.26 ns, lb 991.161 ns, ub 1041.66 ns, ci 0.95 24 | std dev: 128.963 ns, lb 109.803 ns, ub 159.509 ns, ci 0.95 25 | found 2 outliers among 100 samples (2%) 26 | variance is severely inflated by outliers 27 | -------------------------------------------------------------------------------- /src/comparisons/nanobench/main.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/martinus/nanobench 2 | // g++ -O2 -I../../include main.cpp -o m 3 | 4 | #define ANKERL_NANOBENCH_IMPLEMENT 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int, char**) { 12 | uint64_t x = 1; 13 | ankerl::nanobench::Bench().run("x += x", [&]() { 14 | ankerl::nanobench::doNotOptimizeAway(x += x); 15 | }); 16 | 17 | ankerl::nanobench::Bench().run("sleep 10ms", [&]() { 18 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 19 | }); 20 | 21 | std::random_device dev; 22 | std::mt19937_64 rng(dev()); 23 | ankerl::nanobench::Bench().run("random fluctuations", [&]() { 24 | // each run, perform a random number of rng calls 25 | auto iterations = rng() & UINT64_C(0xff); 26 | for (uint64_t i = 0; i < iterations; ++i) { 27 | (void)rng(); 28 | } 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /src/comparisons/nanobench/out.txt: -------------------------------------------------------------------------------- 1 | 2 | | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark 3 | |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- 4 | | 0.31 | 3,192,709,967.58 | 0.0% | 1.00 | 1.00 | 0.999 | 0.00 | 0.0% | 0.00 | `x += x` 5 | | 10,149,086.00 | 98.53 | 0.1% | 45.00 | 2,394.00 | 0.019 | 9.00 | 88.9% | 0.11 | `sleep 10ms` 6 | | 744.50 | 1,343,183.34 | 11.2% | 2,815.05 | 2,375.86 | 1.185 | 524.73 | 12.5% | 0.00 | :wavy_dash: `random fluctuations` (Unstable with ~23.3 iters. Increase `minEpochIterations` to e.g. 233) 7 | -------------------------------------------------------------------------------- /src/comparisons/timer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | 8 | To disable buffering in the input program, do e.g. this: 9 | 10 | 11 | struct InitCout { InitCout() { std::cout.setf(std::ios::unitbuf); } }; 12 | InitCout initCout; 13 | 14 | */ 15 | 16 | int main(int, char**) { 17 | using Clock = std::chrono::high_resolution_clock; 18 | 19 | std::ios_base::sync_with_stdio(false); 20 | std::cout.setf(std::ios::fixed); 21 | std::cout.precision(6); 22 | 23 | auto begin = Clock::now(); 24 | 25 | std::string line; 26 | while (std::getline(std::cin, line)) { 27 | auto now = Clock::now(); 28 | auto t = std::chrono::duration(now - begin).count(); 29 | std::cout << std::right << std::setw(12) << t << " | " << line << "\n"; 30 | } 31 | 32 | auto now = Clock::now(); 33 | auto t = std::chrono::duration(now - begin).count(); 34 | std::cout << "-------------|\n" 35 | << std::right << std::setw(12) << t << " | total" << std::endl; 36 | } 37 | -------------------------------------------------------------------------------- /src/docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | martin.ankerl@gmail.com. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 121 | 122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 123 | enforcement ladder](https://github.com/mozilla/diversity). 124 | 125 | [homepage]: https://www.contributor-covenant.org 126 | 127 | For answers to common questions about this code of conduct, see the FAQ at 128 | https://www.contributor-covenant.org/faq. Translations are available at 129 | https://www.contributor-covenant.org/translations. 130 | -------------------------------------------------------------------------------- /src/docs/_generated/mustache.render.csv: -------------------------------------------------------------------------------- 1 | "title";"name";"unit";"batch";"elapsed";"error %";"instructions";"branches";"branch misses";"total" 2 | "Benchmarking std::mt19937_64 and std::knuth_b";"std::mt19937_64";"op";1;2.54441805225653e-08;0.0236579384033733;125.989678899083;16.7645714285714;0.564133016627078;0.000218811 3 | "Benchmarking std::mt19937_64 and std::knuth_b";"std::knuth_b";"op";1;3.19013867488444e-08;0.00091350764819687;170.013008130081;28;0.0031104199066874;0.000217248 4 | -------------------------------------------------------------------------------- /src/docs/_generated/mustache.render.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/docs/_generated/mustache.template.csv: -------------------------------------------------------------------------------- 1 | "title";"name";"unit";"batch";"elapsed";"error %";"instructions";"branches";"branch misses";"total" 2 | {{#result}}"{{title}}";"{{name}}";"{{unit}}";{{batch}};{{median(elapsed)}};{{medianAbsolutePercentError(elapsed)}};{{median(instructions)}};{{median(branchinstructions)}};{{median(branchmisses)}};{{sumProduct(iterations, elapsed)}} 3 | {{/result}} -------------------------------------------------------------------------------- /src/docs/_generated/mustache.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/docs/_generated/mustache.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | {{#result}} { 4 | "title": "{{title}}", 5 | "name": "{{name}}", 6 | "unit": "{{unit}}", 7 | "batch": {{batch}}, 8 | "complexityN": {{complexityN}}, 9 | "epochs": {{epochs}}, 10 | "clockResolution": {{clockResolution}}, 11 | "clockResolutionMultiple": {{clockResolutionMultiple}}, 12 | "maxEpochTime": {{maxEpochTime}}, 13 | "minEpochTime": {{minEpochTime}}, 14 | "minEpochIterations": {{minEpochIterations}}, 15 | "epochIterations": {{epochIterations}}, 16 | "warmup": {{warmup}}, 17 | "relative": {{relative}}, 18 | "median(elapsed)": {{median(elapsed)}}, 19 | "medianAbsolutePercentError(elapsed)": {{medianAbsolutePercentError(elapsed)}}, 20 | "median(instructions)": {{median(instructions)}}, 21 | "medianAbsolutePercentError(instructions)": {{medianAbsolutePercentError(instructions)}}, 22 | "median(cpucycles)": {{median(cpucycles)}}, 23 | "median(contextswitches)": {{median(contextswitches)}}, 24 | "median(pagefaults)": {{median(pagefaults)}}, 25 | "median(branchinstructions)": {{median(branchinstructions)}}, 26 | "median(branchmisses)": {{median(branchmisses)}}, 27 | "totalTime": {{sumProduct(iterations, elapsed)}}, 28 | "measurements": [ 29 | {{#measurement}} { 30 | "iterations": {{iterations}}, 31 | "elapsed": {{elapsed}}, 32 | "pagefaults": {{pagefaults}}, 33 | "cpucycles": {{cpucycles}}, 34 | "contextswitches": {{contextswitches}}, 35 | "instructions": {{instructions}}, 36 | "branchinstructions": {{branchinstructions}}, 37 | "branchmisses": {{branchmisses}} 38 | }{{^-last}},{{/-last}} 39 | {{/measurement}} ] 40 | }{{^-last}},{{/-last}} 41 | {{/result}} ] 42 | } -------------------------------------------------------------------------------- /src/docs/_generated/mustache.template.pyperf: -------------------------------------------------------------------------------- 1 | { 2 | "benchmarks": [ 3 | { 4 | "runs": [ 5 | { 6 | "values": [ 7 | {{#measurement}} {{elapsed}}{{^-last}}, 8 | {{/last}}{{/measurement}} 9 | ] 10 | } 11 | ] 12 | } 13 | ], 14 | "metadata": { 15 | "loops": {{sum(iterations)}}, 16 | "inner_loops": {{batch}}, 17 | "name": "{{title}}", 18 | "unit": "second" 19 | }, 20 | "version": "1.0" 21 | } -------------------------------------------------------------------------------- /src/docs/_generated/tutorial_render_simple.txt: -------------------------------------------------------------------------------- 1 | "title";"name";"unit";"batch";"elapsed";"error %";"instructions";"branches";"branch misses";"total" 2 | "benchmark";"std::vector";"op";1;6.51982200647249e-09;8.26465858909014e-05;23.0034662045061;5;0.00116867939228672;0.000171959 3 | -------------------------------------------------------------------------------- /src/docs/_static/nanobench-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/src/docs/_static/nanobench-logo.png -------------------------------------------------------------------------------- /src/docs/code/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | set(CMAKE_CXX_STANDARD 17) 3 | 4 | project( 5 | CMakeNanobenchExample 6 | VERSION 1.0 7 | LANGUAGES CXX) 8 | 9 | include(FetchContent) 10 | 11 | FetchContent_Declare( 12 | nanobench 13 | GIT_REPOSITORY https://github.com/martinus/nanobench.git 14 | GIT_TAG v4.1.0 15 | GIT_SHALLOW TRUE) 16 | 17 | FetchContent_MakeAvailable(nanobench) 18 | 19 | add_executable(MyExample my_example.cpp) 20 | target_link_libraries(MyExample PRIVATE nanobench) 21 | -------------------------------------------------------------------------------- /src/docs/code/full_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main() { 6 | int y = 0; 7 | std::atomic x(0); 8 | ankerl::nanobench::Bench().run("compare_exchange_strong", [&] { 9 | x.compare_exchange_strong(y, 0); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/docs/code/full_example_simple.cpp: -------------------------------------------------------------------------------- 1 | #define ANKERL_NANOBENCH_IMPLEMENT 2 | #include 3 | 4 | int main() { 5 | double d = 1.0; 6 | ankerl::nanobench::Bench().run("some double ops", [&] { 7 | d += 1.0 / d; 8 | if (d > 5.0) { 9 | d -= 5.0; 10 | } 11 | ankerl::nanobench::doNotOptimizeAway(d); 12 | }); 13 | } -------------------------------------------------------------------------------- /src/docs/code/pyperf_compare_to.txt: -------------------------------------------------------------------------------- 1 | +-----------+--------------------+------------------------------+ 2 | | Benchmark | pyperf_shuffle_std | pyperf_shuffle_nanobench | 3 | +===========+====================+==============================+ 4 | | benchmark | 36.4 us | 11.2 us: 3.24x faster (-69%) | 5 | +-----------+--------------------+------------------------------+ 6 | -------------------------------------------------------------------------------- /src/docs/code/pyperf_hist.txt: -------------------------------------------------------------------------------- 1 | 35.7 us: 21 ###################################### 2 | 36.0 us: 33 ############################################################ 3 | 36.3 us: 37 ################################################################### 4 | 36.6 us: 5 ######### 5 | 36.9 us: 0 | 6 | 37.2 us: 1 ## 7 | 37.5 us: 0 | 8 | 37.8 us: 0 | 9 | 38.1 us: 0 | 10 | 38.4 us: 0 | 11 | 38.7 us: 0 | 12 | 39.0 us: 0 | 13 | 39.3 us: 0 | 14 | 39.6 us: 1 ## 15 | 39.9 us: 0 | 16 | 40.2 us: 0 | 17 | 40.5 us: 1 ## 18 | 40.8 us: 0 | 19 | 41.1 us: 0 | 20 | 41.5 us: 0 | 21 | 41.8 us: 0 | 22 | 42.1 us: 1 ## 23 | -------------------------------------------------------------------------------- /src/docs/code/pyperf_stats.txt: -------------------------------------------------------------------------------- 1 | Total duration: 364 ms 2 | Raw value minimum: 3.57 ms 3 | Raw value maximum: 4.21 ms 4 | 5 | Number of calibration run: 0 6 | Number of run with values: 1 7 | Total number of run: 1 8 | 9 | Number of warmup per run: 0 10 | Number of value per run: 100 11 | Loop iterations per value: 100 12 | Total number of values: 100 13 | 14 | Minimum: 35.7 us 15 | Median +- MAD: 36.2 us +- 0.2 us 16 | Mean +- std dev: 36.4 us +- 0.9 us 17 | Maximum: 42.1 us 18 | 19 | 0th percentile: 35.7 us (-2% of the mean) -- minimum 20 | 5th percentile: 35.8 us (-2% of the mean) 21 | 25th percentile: 36.1 us (-1% of the mean) -- Q1 22 | 50th percentile: 36.2 us (-0% of the mean) -- median 23 | 75th percentile: 36.4 us (+0% of the mean) -- Q3 24 | 95th percentile: 36.7 us (+1% of the mean) 25 | 100th percentile: 42.1 us (+16% of the mean) -- maximum 26 | 27 | Number of outlier (out of 35.6 us..36.9 us): 4 28 | -------------------------------------------------------------------------------- /src/docs/code/template-csv.txt: -------------------------------------------------------------------------------- 1 | "title";"name";"unit";"batch";"elapsed";"error %";"instructions";"branches";"branch misses";"total" 2 | {{#result}}"{{title}}";"{{name}}";"{{unit}}";{{batch}};{{median(elapsed)}};{{medianAbsolutePercentError(elapsed)}};{{median(instructions)}};{{median(branchinstructions)}};{{median(branchmisses)}};{{sumProduct(iterations, elapsed)}} 3 | {{/result}} -------------------------------------------------------------------------------- /src/docs/code/template-htmlBoxplot.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/docs/code/template-json.txt: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | {{#result}} { 4 | "title": "{{title}}", 5 | "name": "{{name}}", 6 | "unit": "{{unit}}", 7 | "batch": {{batch}}, 8 | "complexityN": {{complexityN}}, 9 | "epochs": {{epochs}}, 10 | "clockResolution": {{clockResolution}}, 11 | "clockResolutionMultiple": {{clockResolutionMultiple}}, 12 | "maxEpochTime": {{maxEpochTime}}, 13 | "minEpochTime": {{minEpochTime}}, 14 | "minEpochIterations": {{minEpochIterations}}, 15 | "epochIterations": {{epochIterations}}, 16 | "warmup": {{warmup}}, 17 | "relative": {{relative}}, 18 | "median(elapsed)": {{median(elapsed)}}, 19 | "medianAbsolutePercentError(elapsed)": {{medianAbsolutePercentError(elapsed)}}, 20 | "median(instructions)": {{median(instructions)}}, 21 | "medianAbsolutePercentError(instructions)": {{medianAbsolutePercentError(instructions)}}, 22 | "median(cpucycles)": {{median(cpucycles)}}, 23 | "median(contextswitches)": {{median(contextswitches)}}, 24 | "median(pagefaults)": {{median(pagefaults)}}, 25 | "median(branchinstructions)": {{median(branchinstructions)}}, 26 | "median(branchmisses)": {{median(branchmisses)}}, 27 | "totalTime": {{sumProduct(iterations, elapsed)}}, 28 | "measurements": [ 29 | {{#measurement}} { 30 | "iterations": {{iterations}}, 31 | "elapsed": {{elapsed}}, 32 | "pagefaults": {{pagefaults}}, 33 | "cpucycles": {{cpucycles}}, 34 | "contextswitches": {{contextswitches}}, 35 | "instructions": {{instructions}}, 36 | "branchinstructions": {{branchinstructions}}, 37 | "branchmisses": {{branchmisses}} 38 | }{{^-last}},{{/-last}} 39 | {{/measurement}} ] 40 | }{{^-last}},{{/-last}} 41 | {{/result}} ] 42 | } -------------------------------------------------------------------------------- /src/docs/code/template-pyperf.txt: -------------------------------------------------------------------------------- 1 | { 2 | "benchmarks": [ 3 | { 4 | "runs": [ 5 | { 6 | "values": [ 7 | {{#measurement}} {{elapsed}}{{^-last}}, 8 | {{/last}}{{/measurement}} 9 | ] 10 | } 11 | ] 12 | } 13 | ], 14 | "metadata": { 15 | "loops": {{sum(iterations)}}, 16 | "inner_loops": {{batch}}, 17 | "name": "{{title}}", 18 | "unit": "second" 19 | }, 20 | "version": "1.0" 21 | } -------------------------------------------------------------------------------- /src/docs/comparison.rst: -------------------------------------------------------------------------------- 1 | -------- 2 | Test Set 3 | -------- 4 | I've implemented the three different benchmarks: Fast, Slow, Fluctuating in several frameworks for comparison. 5 | 6 | Fast 7 | Benchmarks ``x += x``, starting from 1. This is a single instruction, and prone to be optimized away. 8 | 9 | Slow 10 | Benchmarks ``std::this_thread::sleep_for(10ms)``. For a microbenchmark this 11 | is very slow, and it is interesting how the framework's autotuning deals with this. 12 | 13 | Fluctuating 14 | A microbenchmark where each evaluation takes a different time. This randomly fluctuating runtime 15 | is achieved by randomly producing 0-255 random numbers with ``std::mt19937_64``. 16 | 17 | 18 | All benchmarks are run on an i7-8700 CPU locked at 3.2GHz, using 19 | `pyperf system tune `_. 20 | 21 | 22 | ------- 23 | Runtime 24 | ------- 25 | 26 | I wrote a little timing tool that measures how long exactly it takes to print benchmark output to the screen. 27 | With this I have measured the runtimes of major benchmarking frameworks which support automatic tuning of 28 | the number of iterations: :ref:`Google Benchmark `, :ref:`Catch2 `, 29 | :ref:`nonius `, :ref:`sltbench `, and of course :ref:`nanobench `. 30 | 31 | .. figure:: totalruntime.svg 32 | :alt: Total Runtimes 33 | 34 | ====================== ========= ========= =========== ========== ========= 35 | Benchmarking Framework Fast Slow Fluctuating Overhead total 36 | ====================== ========= ========= =========== ========== ========= 37 | Google Benchmark 0.367 11.259 0.825 0.000 12.451 38 | Catch2 1.004 2.074 0.966 1.737 5.782 39 | nonius 0.741 1.815 0.740 1.715 5.010 40 | sltbench 0.202 0.204 0.203 3.001 3.610 41 | **nanobench** **0.079** **0.112** **0.000** **0.001** **0.192** 42 | ====================== ========= ========= =========== ========== ========= 43 | 44 | Nanobench is clearly the fastest autotuning benchmarking framework, by an enormous margin. 45 | 46 | ------------------------ 47 | Implementations & Output 48 | ------------------------ 49 | 50 | .. _`comparison-nanobench`: 51 | 52 | 53 | nanobench 54 | ========= 55 | 56 | 57 | Sourcecode 58 | ---------- 59 | 60 | .. literalinclude:: ../comparisons/nanobench/main.cpp 61 | :language: c++ 62 | :linenos: 63 | 64 | 65 | Results 66 | ------- 67 | 68 | .. literalinclude:: ../comparisons/nanobench/out.txt 69 | :language: text 70 | 71 | 72 | .. _`comparison-google-benchmark`: 73 | 74 | 75 | Google Benchmark 76 | ================ 77 | 78 | Very feature rich, battle proven, but a bit aged. Requires 79 | google test. Get it here: `Google Benchmark `_ 80 | 81 | Sourcecode 82 | ---------- 83 | 84 | .. literalinclude:: ../comparisons/google_benchmark/gbench.cpp 85 | :language: c++ 86 | :linenos: 87 | 88 | Results 89 | ------- 90 | 91 | Compiled & linked with 92 | 93 | .. code-block:: sh 94 | 95 | g++ -O2 main.cpp -L/home/martinus/git/benchmark/build/src -lbenchmark -lpthread -o gbench 96 | 97 | executing it gives this result: 98 | 99 | .. literalinclude:: ../comparisons/google_benchmark/out.txt 100 | :language: text 101 | 102 | 103 | Running the tests individually takes 0.365s, 11.274 sec, 0.828sec. 104 | 105 | 106 | .. _`comparison-nonius`: 107 | 108 | 109 | nonius 110 | ====== 111 | 112 | It gives lots of statistics, but seems a bit complicated to me. Not as straight forward as I'd like it. It shows lots of statistics, which makes the output a bit hard to read. I am not sure if it is still actively maintained. The homepage has been down for a while. 113 | Get it here: `nonius `_ 114 | 115 | Sourcecode 116 | ---------- 117 | 118 | .. literalinclude:: ../comparisons/libnonius_nonius/main.cpp 119 | :language: c++ 120 | :linenos: 121 | 122 | Results 123 | ------- 124 | 125 | .. literalinclude:: ../comparisons/libnonius_nonius/out.txt 126 | :language: text 127 | 128 | The tests individually take 0.713sec, 1.883sec, 0.819sec. Plus a startup overhead of 1.611sec. 129 | 130 | 131 | 132 | Picobench 133 | ========= 134 | 135 | It took me a while to figure out that I have to configure the slow test, otherwise it would 136 | run for a looong time. The number of iterations is hardcoded, this library seems very basic. Get it here: 137 | `picobench `_ 138 | 139 | Sourcecode 140 | ---------- 141 | 142 | .. literalinclude:: ../comparisons/iboB_picobench/picobench.cpp 143 | :language: c++ 144 | :linenos: 145 | 146 | Results 147 | ------- 148 | 149 | .. literalinclude:: ../comparisons/iboB_picobench/out.txt 150 | :language: text 151 | 152 | It doesn't really make sense to provide runtime numbers here, because picobench just executes the given number of iterations, and that's it. No autotuning. 153 | 154 | 155 | .. _`comparison-catch2`: 156 | 157 | 158 | Catch2 159 | ====== 160 | 161 | Catch2 is mostly a unit testing framework, and has recently integrated benchmarking faciliy. It is very easy to use, 162 | but does not seem too configurable. I find the way it writes the output very confusing. Get it here: 163 | `Catch2 `_ 164 | 165 | Sourcecode 166 | ---------- 167 | 168 | .. literalinclude:: ../comparisons/catchorg_Catch2/catch.cpp 169 | :language: c++ 170 | :linenos: 171 | 172 | Results 173 | ------- 174 | 175 | .. literalinclude:: ../comparisons/catchorg_Catch2/out.txt 176 | :language: text 177 | 178 | 179 | 180 | moodycamel::microbench 181 | ====================== 182 | 183 | A very simple benchmarking tool, and an API that's very similar to ``ankerl::nanobench``. No autotuning, 184 | no doNotOptimize, no output formatting. Get it here: `moodycamel::microbench `_ 185 | 186 | Sourcecode 187 | ---------- 188 | 189 | 190 | .. literalinclude:: ../comparisons/cameron317_microbench/microbench.cpp 191 | :language: c++ 192 | :linenos: 193 | 194 | 195 | 196 | Results 197 | ------- 198 | 199 | .. literalinclude:: ../comparisons/cameron317_microbench/out.txt 200 | :language: text 201 | 202 | 203 | .. _`comparison-sltbench`: 204 | 205 | 206 | sltbench 207 | ======== 208 | 209 | C++ benchmark which seems to have similar intentions to nanonbech. It claims to be 4.7 times faster than googlebench. 210 | It requires to be compiled and linked. I initially got a compile error because of missing ```` include. 211 | After that it compiled fine, and I created an example. I didn't like that I had to use global variables for the state 212 | that I needed in my ``ComparisonFast`` and ``ComparisonSlow`` benchmark. Get it 213 | here: `sltbench `_ 214 | 215 | Sourcecode 216 | ---------- 217 | 218 | .. literalinclude:: ../comparisons/ivafanas_sltbench/main.cpp 219 | :language: c++ 220 | :linenos: 221 | 222 | Results 223 | ------- 224 | 225 | .. literalinclude:: ../comparisons/ivafanas_sltbench/out.txt 226 | :language: text 227 | 228 | Interestingly, the executable takes exactly 3 seconds startup time, then each benchmark runs for about 0.2 seconds. 229 | 230 | 231 | Celero 232 | ====== 233 | 234 | Unfortunately I couldn't get it working. I only got segmentation faults for my ``x += x`` benchmarks. 235 | Get it here: `celero `_ 236 | 237 | 238 | folly Benchmark 239 | =============== 240 | 241 | Facebook's folly comes with benchmarking facility. It seems rather basic, but with good ``DoNotOptimizeAway`` 242 | functionality. Honestly, I was too lazy to get this working. Too much installation hazzle. Get it here: 243 | `folly `_ 244 | 245 | -------------------------------------------------------------------------------- /src/docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | #import sphinx_rtd_theme 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'nanobench' 23 | copyright = '2019-2023 Martin Leitner-Ankerl ' 24 | author = 'Martin Leitner-Ankerl' 25 | version = 'v4.3.11' 26 | 27 | # -- General configuration --------------------------------------------------- 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | "sphinx_rtd_theme", 34 | "breathe", 35 | "sphinx.ext.mathjax", 36 | "recommonmark" 37 | ] 38 | 39 | # math 40 | # use a newer mathjax version (see http://docs.mathjax.org/en/latest/web/start.html and 41 | # https://www.sphinx-doc.org/en/master/usage/extensions/math.html), because we had proble 42 | mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" 43 | 44 | breathe_projects = { "nanobench": "./_build/doxygen/xml" } 45 | breathe_default_project = "nanobench" 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ['_templates'] 49 | 50 | # List of patterns, relative to source directory, that match files and 51 | # directories to ignore when looking for source files. 52 | # This pattern also affects html_static_path and html_extra_path. 53 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 54 | 55 | 56 | # -- Options for HTML output ------------------------------------------------- 57 | 58 | # The theme to use for HTML and HTML Help pages. See the documentation for 59 | # a list of builtin themes. 60 | # 61 | html_theme = 'sphinx_rtd_theme' 62 | 63 | # see https://sphinx-rtd-theme.readthedocs.io/en/stable/configuring.html 64 | html_theme_options = { 65 | 'analytics_id': 'UA-36863101-2', 66 | 'display_version': True, 67 | 'sticky_navigation': True, 68 | 'collapse_navigation': False, 69 | 'navigation_depth': 4, 70 | } 71 | 72 | html_context = { 73 | 'display_github': True, 74 | 'github_user': 'martinus', 75 | 'github_repo': 'nanobench', 76 | 'github_version': 'master', 77 | 'conf_py_path': '/src/docs/', 78 | } 79 | 80 | # Add any paths that contain custom static files (such as style sheets) here, 81 | # relative to this directory. They are copied after the builtin static files, 82 | # so a file named "default.css" will overwrite the builtin "default.css". 83 | html_static_path = ['_static'] 84 | html_show_sourcelink = True 85 | html_favicon = 'favicon.ico' 86 | html_logo = 'nanobench-logo.svg' 87 | 88 | # hide ankerl::nanobench:: prefix, especially in index 89 | cpp_index_common_prefix = ['ankerl::nanobench::'] -------------------------------------------------------------------------------- /src/docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/src/docs/favicon.ico -------------------------------------------------------------------------------- /src/docs/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Run from src/docs directory 5 | # 6 | # Install breathe (assuming there's a venv in $HOME/venv/) 7 | # source $HOME/venv/bin/activate 8 | # pip install -r requirements.txt 9 | # 10 | # Or just install these: 11 | # pip install -U sphinx breathe sphinx_rtd_theme recommonmark 12 | 13 | # rm should work, this should copy all files, but for safety reasons I'm not doing it... 14 | #rm -Rvf ../../docs 15 | mkdir -p _build/doxygen 16 | doxygen 17 | #source ~/venv/bin/activate 18 | python ~/venv/bin/sphinx-build -E . ../../docs 19 | -------------------------------------------------------------------------------- /src/docs/genindex.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Index 3 | ===== 4 | -------------------------------------------------------------------------------- /src/docs/index.rst: -------------------------------------------------------------------------------- 1 | .. nanobench documentation master file, created by 2 | sphinx-quickstart on Tue Jun 2 20:02:50 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ========= 7 | nanobench 8 | ========= 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :hidden: 13 | :caption: Getting Started 14 | 15 | tutorial 16 | 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | :hidden: 21 | :caption: Comparison 22 | 23 | comparison 24 | 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | :hidden: 29 | :caption: Reference 30 | 31 | reference 32 | genindex 33 | 34 | .. toctree:: 35 | :maxdepth: 2 36 | :hidden: 37 | :caption: About 38 | 39 | license 40 | CODE_OF_CONDUCT.md 41 | 42 | 43 | .. |badge-release| image:: https://img.shields.io/github/release/martinus/nanobench.svg 44 | :target: https://github.com/martinus/nanobench/releases 45 | :alt: Release 46 | .. |badge-license| image:: https://img.shields.io/github/license/martinus/nanobench.svg 47 | :target: https://raw.githubusercontent.com/martinus/nanobench/master/LICENSE 48 | :alt: License 49 | .. |badge-travis| image:: https://travis-ci.com/martinus/nanobench.svg?branch=master 50 | :target: https://travis-ci.com/martinus/nanobench 51 | :alt: Travis CI Build Status 52 | .. |badge-appveyor| image:: https://ci.appveyor.com/api/projects/status/github/martinus/nanobench?branch=master&svg=true 53 | :target: https://ci.appveyor.com/project/martinus/nanobench 54 | :alt: Appveyor Build Status 55 | .. |badge-gitter| image:: https://badges.gitter.im/nanobench/community.svg 56 | :target: https://gitter.im/nanobench/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge 57 | :alt: Join the chat at https://gitter.im/nanobench/community 58 | 59 | .. |download-button| raw:: html 60 | 61 |
62 |
63 |
64 |
65 | 66 | 69 | 76 |
77 |
78 |
79 |
80 | 81 | 82 | |badge-release| |badge-license| |badge-travis| |badge-appveyor| |badge-gitter| 83 | 84 | .. image:: nanobench-logo-small.svg 85 | :alt: I need a better logo. Currently I use a small bench. Nanobench. Ha ha. 86 | 87 | 88 | .. hint:: 89 | 90 | View and download `nanobench on Github `_. 91 | Get the latest :download:`release `. 92 | 93 | 94 | 95 | ``ankerl::nanobench`` is a platform independent microbenchmarking library for C++11/14/17/20. 96 | 97 | 98 | .. literalinclude:: code/full_example_simple.cpp 99 | :language: c++ 100 | :linenos: 101 | 102 | 103 | The whole executable runs for ~60ms and prints 104 | 105 | 106 | .. code-block:: text 107 | 108 | | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark 109 | |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- 110 | | 7.52 | 132,948,239.79 | 1.1% | 6.65 | 24.07 | 0.276 | 1.00 | 8.9% | 0.00 | `some double ops` 111 | 112 | 113 | Which github renders like 114 | 115 | ====================== ===================== ========= ================= ================= ======== ================ ========= =========== ==================== 116 | ns/op op/s err% ins/op cyc/op IPC bra/op miss% total benchmark 117 | ====================== ===================== ========= ================= ================= ======== ================ ========= =========== ==================== 118 | 7.52 132,948,239.79 1.1% 6.65 24.07 0.276 1.00 8.9% 0.00 ``some double ops`` 119 | ====================== ===================== ========= ================= ================= ======== ================ ========= =========== ==================== 120 | 121 | The benchmarked code takes **7.52** nanoseconds to run, so **~133** million times per seconds. Measurements fluctuate by 122 | **1.1%**. On average **6.65** instructions are executed in **24.07** CPU cycles, resulting in **0.276** instructions per 123 | cycle. A **single** branch is in the code, which branch prediction missed in **8.9%** of the cases. Total runtime of 124 | the benchmark with the name ``some double ops`` is **0.00**, so just a few milliseconds. 125 | 126 | Design Goals 127 | ============ 128 | 129 | Ease of use 130 | Simple but powerful API, fast compile times, easy to integrate anywhere. 131 | Fast 132 | Get accurate results as fast as possible 133 | Accurate 134 | Get deterministic, repeatable, and accurate results that you can make sound decisions on. 135 | Robust 136 | Be robust against outliers, warn if results are not reliable. 137 | 138 | -------------------------------------------------------------------------------- /src/docs/license.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | MIT License 3 | =========== 4 | 5 | .. include:: ../../LICENSE 6 | -------------------------------------------------------------------------------- /src/docs/nanobench-logo-compressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/src/docs/nanobench-logo-compressed.png -------------------------------------------------------------------------------- /src/docs/nanobench-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinus/nanobench/e4327893194f06928012eb81cabc606c4e4791ac/src/docs/nanobench-logo.png -------------------------------------------------------------------------------- /src/docs/reference.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | ``ankerl::nanobench`` Reference 3 | =============================== 4 | 5 | .. How to link: https://breathe.readthedocs.io/en/latest/domains.html 6 | E.g. :cpp:class:`ankerl::nanobench::Bench` 7 | 8 | ---------------------------------------------------------------- 9 | :cpp:class:`Bench ` - Main Entry Point 10 | ---------------------------------------------------------------- 11 | 12 | 13 | .. doxygenclass:: ankerl::nanobench::Bench 14 | :members: 15 | 16 | 17 | 18 | --------------------------------------------------------------- 19 | :cpp:class:`Rng ` - Extremely fast PRNG 20 | --------------------------------------------------------------- 21 | 22 | .. doxygenclass:: ankerl::nanobench::Rng 23 | :members: 24 | 25 | 26 | 27 | ------------------------------------------------------------------- 28 | :cpp:class:`Result ` - Benchmark Results 29 | ------------------------------------------------------------------- 30 | 31 | .. doxygenclass:: ankerl::nanobench::Result 32 | :members: 33 | 34 | 35 | 36 | ---------------------------------------------------------------------- 37 | :cpp:func:`doNotOptimizeAway() ` 38 | ---------------------------------------------------------------------- 39 | 40 | .. doxygenfunction:: ankerl::nanobench::doNotOptimizeAway(Arg&& arg) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------- 45 | :cpp:func:`render() ` - Mustache-like Templates 46 | -------------------------------------------------------------------------- 47 | 48 | 49 | .. doxygenfunction:: ankerl::nanobench::render(char const *mustacheTemplate, Bench const &bench, std::ostream &out) 50 | 51 | 52 | :cpp:func:`templates::csv ` 53 | -------------------------------------------------------------- 54 | 55 | .. doxygenfunction:: ankerl::nanobench::templates::csv 56 | 57 | 58 | 59 | :cpp:func:`templates::htmlBoxplot ` 60 | ------------------------------------------------------------------------------ 61 | 62 | .. doxygenfunction:: ankerl::nanobench::templates::htmlBoxplot 63 | 64 | 65 | 66 | :cpp:func:`templates::json ` 67 | ---------------------------------------------------------------- 68 | 69 | .. doxygenfunction:: ankerl::nanobench::templates::json 70 | 71 | 72 | :cpp:func:`templates::pyperf ` 73 | ------------------------------------------------------------------------------ 74 | 75 | .. doxygenfunction:: ankerl::nanobench::templates::pyperf 76 | 77 | 78 | --------------------- 79 | Environment Variables 80 | --------------------- 81 | 82 | ``NANOBENCH_ENDLESS`` - Run a Specific Test Endlessly 83 | ----------------------------------------------------- 84 | 85 | Sometimes it helps to run a benchmark for a very long time, so that it's possible to attach with a profiler like 86 | `perf `_ and get meaningful statistics. This can be done with the environment variable 87 | ``NANOBENCH_ENDLESS``. E.g. to run the benchmark with the name ``x += x`` endlessly, call the app this way: 88 | 89 | .. code-block:: sh 90 | 91 | NANOBENCH_ENDLESS="x += x" ./yourapp 92 | 93 | When your app runs it will run all benchmark normally, but when it encounters a benchmarked named ``x += x``, it will run this one endlessly. 94 | It will print in nice friendly letters 95 | 96 | .. code-block:: text 97 | 98 | NANOBENCH_ENDLESS set: running 'x += x' endlessly 99 | 100 | once it reaches that state. 101 | 102 | 103 | .. warning:: 104 | 105 | For optimal profiling with ``perf``, you shouldn't use ``pyperf system tune`` in the endless mode. PyPerf dramatically reduces the 106 | number of events that can be captured per second. This is a good to get accurate benchmark numbers from nanobench, but a bad when 107 | you actually want to use perf to analyze hotspots. 108 | 109 | 110 | 111 | ``NANOBENCH_SUPPRESS_WARNINGS`` - No Stability Warnings 112 | ------------------------------------------------------- 113 | 114 | In environments where it is clear that the results will not be stable, e.g. in CI where benchmarks are merely run to check if they don't cause a crash, 115 | the environment variable ``NANOBENCH_SUPPRESS_WARNINGS`` can be used to suppress any warnings. This includes the header warnings like for frequency scaling, 116 | and the ``:wavy_dash:`` warnings for the individual tests. 117 | 118 | Set ``NANOBENCH_SUPPRESS_WARNINGS=1`` to disable all warnings, or set it to 0 to enable warnings (the default mode). 119 | 120 | .. code-block:: sh 121 | 122 | NANOBENCH_SUPPRESS_WARNINGS=1 ./yourapp 123 | 124 | -------------------------------------------------------------------------------- /src/docs/requirements.txt: -------------------------------------------------------------------------------- 1 | breathe==4.35.0 2 | commonmark==0.9.1 3 | recommonmark==0.7.1 4 | Sphinx==6.1.3 5 | sphinx-rtd-theme==1.2.0 6 | sphinxcontrib-applehelp==1.0.4 7 | sphinxcontrib-devhelp==1.0.2 8 | sphinxcontrib-htmlhelp==2.0.1 9 | sphinxcontrib-jquery==4.1 10 | sphinxcontrib-jsmath==1.0.1 11 | sphinxcontrib-qthelp==1.0.3 12 | sphinxcontrib-serializinghtml==1.1.5 13 | -------------------------------------------------------------------------------- /src/include/.clang-format: -------------------------------------------------------------------------------- 1 | # see https://clang.llvm.org/docs/ClangFormatStyleOptions.html 2 | --- 3 | BasedOnStyle: LLVM 4 | Language: Cpp 5 | Standard: Cpp11 6 | 7 | ColumnLimit: 135 8 | 9 | AccessModifierOffset: -4 10 | IndentWidth: 4 11 | UseTab: Never 12 | 13 | AlignEscapedNewlines: Left 14 | AllowShortFunctionsOnASingleLine: Empty 15 | AllowShortLambdasOnASingleLine: Empty 16 | AlwaysBreakTemplateDeclarations: true 17 | BreakConstructorInitializers: BeforeComma 18 | IndentPPDirectives: AfterHash 19 | PointerAlignment: Left 20 | -------------------------------------------------------------------------------- /src/include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources_local(nb PUBLIC nanobench.h) 2 | target_include_directories(nb PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 3 | -------------------------------------------------------------------------------- /src/scripts/all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | ROOTDIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd )" 5 | 6 | 7 | function build() { 8 | #NICE="nice -n20" 9 | NICE="chrt -i 0" 10 | 11 | ORIGINDIR=$(pwd) 12 | 13 | COMPILER=$1 14 | CXX_STANDARD=$2 15 | SANITIZER=$3 16 | CXXFLAGS=$4 17 | 18 | DIRNAME=${COMPILER}_cxx${CXX_STANDARD}_sanitizer${SANITIZER}_${CXXFLAGS} 19 | 20 | rm -Rf ${DIRNAME} 21 | mkdir -p ${DIRNAME} 22 | cd ${DIRNAME} 23 | 24 | CXX=$(which ${COMPILER}) cmake -G Ninja -DCMAKE_CXX_FLAGS=${CXXFLAGS} -DCMAKE_BUILD_TYPE=Release -DNB_cxx_standard=${CXX_STANDARD} -DNB_sanitizer=${SANITIZER} ${ROOTDIR} 25 | ${NICE} cmake --build . 26 | rm -f ubsan.log* 27 | 28 | UBSAN_OPTIONS=print_stacktrace=1:log_path=ubsan.log:suppressions=${ROOTDIR}/ubsan.supp ./nb 29 | if ls ubsan.log* 1> /dev/null 2>&1; then 30 | cat ubsan.log* 31 | exit 1 32 | fi 33 | 34 | cd ${ORIGINDIR} 35 | } 36 | 37 | 38 | # put fi down to skip 39 | #if false; then 40 | #fi 41 | 42 | ## DON'T MODIFY PAST HERE! Just copy&past above this line to test it before other stuff. 43 | 44 | build "clang++" "11" "OFF" 45 | build "g++" "20" "OFF" 46 | build "clang++" "11" "OFF" "-m32" 47 | build "clang++" "11" "ON" 48 | #build "clang++" "11" "ON" "-m32" # linker error in chrono 49 | 50 | build "clang++" "14" "OFF" 51 | build "clang++" "14" "OFF" "-m32" 52 | build "clang++" "14" "ON" 53 | #build "clang++" "14" "ON" "-m32" # linker error in chrono 54 | 55 | build "clang++" "17" "OFF" 56 | build "clang++" "17" "OFF" "-m32" 57 | build "clang++" "17" "ON" 58 | #build "clang++" "17" "ON" "-m32" # linker error in chrono 59 | 60 | build "clang++" "20" "OFF" 61 | build "clang++" "20" "OFF" "-m32" 62 | build "clang++" "20" "ON" 63 | #build "clang++" "20" "ON" "-m32" # linker error in chrono 64 | 65 | build "clang++-6" "11" "OFF" 66 | build "clang++-6" "11" "OFF" "-m32" 67 | #build "clang++-6" "11" "ON" # can't find asan lib 68 | #build "clang++-6" "11" "ON" "-m32" # can't find asan lib 69 | 70 | build "clang++-6" "14" "OFF" 71 | build "clang++-6" "14" "OFF" "-m32" 72 | #build "clang++-6" "14" "ON" # can't find asan lib 73 | #build "clang++-6" "14" "ON" "-m32" # can't find asan lib 74 | 75 | build "clang++-6" "17" "OFF" 76 | build "clang++-6" "17" "OFF" "-m32" 77 | #build "clang++-6" "17" "ON" # can't find asan lib 78 | #build "clang++-6" "17" "ON" "-m32" # can't find asan lib 79 | 80 | #build "clang++-6" "20" "OFF" 81 | #build "clang++-6" "20" "OFF" "-m32" 82 | #build "clang++-6" "20" "ON" # can't find asan lib 83 | #build "clang++-6" "20" "ON" "-m32" # can't find asan lib 84 | 85 | build "g++" "11" "OFF" 86 | build "g++" "11" "OFF" "-m32" 87 | build "g++" "11" "ON" 88 | build "g++" "11" "ON" "-m32" 89 | 90 | build "g++" "14" "OFF" 91 | build "g++" "14" "OFF" "-m32" 92 | build "g++" "14" "ON" 93 | build "g++" "14" "ON" "-m32" 94 | 95 | build "g++" "17" "OFF" 96 | build "g++" "17" "OFF" "-m32" 97 | build "g++" "17" "ON" 98 | build "g++" "17" "ON" "-m32" 99 | 100 | build "g++" "20" "OFF" 101 | build "g++" "20" "OFF" "-m32" 102 | build "g++" "20" "ON" 103 | build "g++" "20" "ON" "-m32" 104 | 105 | build "g++-4.8" "11" "OFF" 106 | #build "g++-4.8" "11" "OFF" "-m32" # skipping incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.5/libstdc++.so when searching for -lstdc++ 107 | #build "g++-4.8" "11" "ON" # no -fsanitize=undefined,float-divide-by-zero,float-cast-overflow 108 | #build "g++-4.8" "11" "ON" "-m32" # skipping incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.5/libstdc++.so when searching for -lstdc++ 109 | 110 | build "g++-4.9" "11" "OFF" 111 | build "g++-4.9" "11" "OFF" "-m32" 112 | #build "g++-4.9" "11" "ON" # unrecognized fsanitize options 113 | #build "g++-4.9" "11" "ON" "-m32" # unrecognized fsanitize options 114 | 115 | build "g++-4.9" "14" "OFF" 116 | build "g++-4.9" "14" "OFF" "-m32" 117 | #build "g++-4.9" "14" "ON" # unrecognized fsanitize options 118 | #build "g++-4.9" "14" "ON" "-m32" # unrecognized fsanitize options 119 | 120 | build "g++-5" "11" "OFF" 121 | #build "g++-5" "11" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 122 | #build "g++-5" "11" "ON" # unrecognized fsanitize options 123 | #build "g++-5" "11" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 124 | 125 | build "g++-5" "14" "OFF" 126 | #build "g++-5" "14" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 127 | #build "g++-5" "14" "ON" # unrecognized fsanitize options 128 | #build "g++-5" "14" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 129 | 130 | build "g++-5" "17" "OFF" 131 | #build "g++-5" "17" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 132 | #build "g++-5" "17" "ON" # unrecognized fsanitize options 133 | #build "g++-5" "17" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 134 | 135 | build "g++-6" "11" "OFF" 136 | #build "g++-6" "11" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 137 | #build "g++-6" "11" "ON" # unrecognized fsanitize options 138 | #build "g++-6" "11" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 139 | 140 | build "g++-6" "14" "OFF" 141 | #build "g++-6" "14" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 142 | #build "g++-6" "14" "ON" # unrecognized fsanitize options 143 | #build "g++-6" "14" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 144 | 145 | build "g++-6" "17" "OFF" 146 | #build "g++-6" "17" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 147 | #build "g++-6" "17" "ON" # unrecognized fsanitize options 148 | #build "g++-6" "17" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 149 | 150 | build "g++-7" "11" "OFF" 151 | #build "g++-7" "11" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 152 | #build "g++-7" "11" "ON" # unrecognized fsanitize options 153 | #build "g++-7" "11" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 154 | 155 | build "g++-7" "14" "OFF" 156 | #build "g++-7" "14" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 157 | #build "g++-7" "14" "ON" # unrecognized fsanitize options 158 | #build "g++-7" "14" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 159 | 160 | build "g++-7" "17" "OFF" 161 | #build "g++-7" "17" "OFF" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 162 | #build "g++-7" "17" "ON" # unrecognized fsanitize options 163 | #build "g++-7" "17" "ON" "-m32" # incompatible /usr/lib/gcc/x86_64-pc-linux-gnu/5.5.0/libstdc++.so 164 | -------------------------------------------------------------------------------- /src/scripts/asymptotes-google.cpp: -------------------------------------------------------------------------------- 1 | // g++ -O2 asymptotes-google.cpp -isystem ~/git/benchmark/include/ -L/home/martinus/git/benchmark/build/src -lbenchmark -lpthread -o 2 | // asymptotes 3 | #include 4 | 5 | #include 6 | 7 | static void BM_StringCreation(benchmark::State& state) { 8 | for (auto _ : state) { 9 | std::string empty_string; 10 | } 11 | } 12 | // Register the function as a benchmark 13 | BENCHMARK(BM_StringCreation); 14 | 15 | // Define another benchmark 16 | static void BM_StringCopy(benchmark::State& state) { 17 | std::string x = "hello"; 18 | for (auto _ : state) { 19 | std::string copy(x); 20 | } 21 | } 22 | BENCHMARK(BM_StringCopy); 23 | 24 | static void BM_set(benchmark::State& state) { 25 | std::set s; 26 | for (size_t i = 0; i < state.range(0); ++i) { 27 | s.insert(i); 28 | } 29 | size_t i = 0; 30 | for (auto _ : state) { 31 | benchmark::DoNotOptimize(s.find(i)); 32 | if (i++ == state.range(0)) { 33 | i = 0; 34 | } 35 | } 36 | state.SetComplexityN(state.range(0)); 37 | } 38 | BENCHMARK(BM_set)->RangeMultiplier(2)->Range(1 << 10, 1 << 18)->Complexity(); 39 | 40 | static void BM_StringCompare(benchmark::State& state) { 41 | std::string s1(state.range(0), '-'); 42 | std::string s2(state.range(0), '-'); 43 | for (auto _ : state) { 44 | benchmark::DoNotOptimize(s1.compare(s2)); 45 | } 46 | state.SetComplexityN(state.range(0)); 47 | } 48 | BENCHMARK(BM_StringCompare)->RangeMultiplier(2)->Range(1 << 5, 1 << 22)->Complexity(); 49 | 50 | BENCHMARK_MAIN(); 51 | -------------------------------------------------------------------------------- /src/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ROOT_SRC_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd )" 4 | CXX=clang++ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ${ROOT_SRC_DIR} 5 | -------------------------------------------------------------------------------- /src/scripts/clang-analyze-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | rm -Rvf CMakeCache.txt session.data CMakeFiles 5 | ccache -C 6 | 7 | CXX=clang++ cmake -G Ninja -DNB_cxx_standard=17 -DCMAKE_CXX_FLAGS="-O2 -ftime-trace" -DCMAKE_BUILD_TYPE=Release ../.. 8 | # CXX=clang++ cmake -G Ninja -DCMAKE_CXX_FLAGS="-ftime-trace" -DCMAKE_BUILD_TYPE=Debug ../.. 9 | 10 | ~/git/ClangBuildAnalyzer/bin/ClangBuildAnalyzer --start . 11 | time ninja 12 | ~/git/ClangBuildAnalyzer/bin/ClangBuildAnalyzer --stop . session.data 13 | ~/git/ClangBuildAnalyzer/bin/ClangBuildAnalyzer --analyze session.data 14 | -------------------------------------------------------------------------------- /src/scripts/doctest-no-output.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT 2 | #include 3 | 4 | int main(int argc, char** argv) { 5 | doctest::Context context; 6 | context.setOption("out", "/dev/null"); 7 | context.setOption("no-version", true); 8 | context.applyCommandLine(argc, argv); 9 | return context.run(); 10 | } 11 | -------------------------------------------------------------------------------- /src/scripts/gh-md-toc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Steps: 5 | # 6 | # 1. Download corresponding html file for some README.md: 7 | # curl -s $1 8 | # 9 | # 2. Discard rows where no substring 'user-content-' (github's markup): 10 | # awk '/user-content-/ { ... 11 | # 12 | # 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) 21 | # 22 | # 5. Find anchor and insert it inside "(...)": 23 | # substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) 24 | # 25 | 26 | gh_toc_version="0.6.1" 27 | 28 | gh_user_agent="gh-md-toc v$gh_toc_version" 29 | 30 | # 31 | # Download rendered into html README.md by its url. 32 | # 33 | # 34 | gh_toc_load() { 35 | local gh_url=$1 36 | 37 | if type curl &>/dev/null; then 38 | curl --user-agent "$gh_user_agent" -s "$gh_url" 39 | elif type wget &>/dev/null; then 40 | wget --user-agent="$gh_user_agent" -qO- "$gh_url" 41 | else 42 | echo "Please, install 'curl' or 'wget' and try again." 43 | exit 1 44 | fi 45 | } 46 | 47 | # 48 | # Converts local md file into html by GitHub 49 | # 50 | # ➥ curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown 51 | #

Hello world github/linguist#1 cool, and #1!

'" 52 | gh_toc_md2html() { 53 | local gh_file_md=$1 54 | URL=https://api.github.com/markdown/raw 55 | if [ ! -z "$GH_TOC_TOKEN" ]; then 56 | TOKEN=$GH_TOC_TOKEN 57 | else 58 | TOKEN="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" 59 | fi 60 | if [ -f "$TOKEN" ]; then 61 | URL="$URL?access_token=$(cat $TOKEN)" 62 | fi 63 | # echo $URL 1>&2 64 | OUTPUT="$(curl -s --user-agent "$gh_user_agent" \ 65 | --data-binary @"$gh_file_md" -H "Content-Type:text/plain" \ 66 | $URL)" 67 | 68 | if [ "$?" != "0" ]; then 69 | echo "XXNetworkErrorXX" 70 | fi 71 | if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then 72 | echo "XXRateLimitXX" 73 | else 74 | echo "${OUTPUT}" 75 | fi 76 | } 77 | 78 | 79 | # 80 | # Is passed string url 81 | # 82 | gh_is_url() { 83 | case $1 in 84 | https* | http*) 85 | echo "yes";; 86 | *) 87 | echo "no";; 88 | esac 89 | } 90 | 91 | # 92 | # TOC generator 93 | # 94 | gh_toc(){ 95 | local gh_src=$1 96 | local gh_src_copy=$1 97 | local gh_ttl_docs=$2 98 | local need_replace=$3 99 | 100 | if [ "$gh_src" = "" ]; then 101 | echo "Please, enter URL or local path for a README.md" 102 | exit 1 103 | fi 104 | 105 | 106 | # Show "TOC" string only if working with one document 107 | if [ "$gh_ttl_docs" = "1" ]; then 108 | 109 | echo "Table of Contents" 110 | echo "=================" 111 | echo "" 112 | gh_src_copy="" 113 | 114 | fi 115 | 116 | if [ "$(gh_is_url "$gh_src")" == "yes" ]; then 117 | gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" 118 | if [ "${PIPESTATUS[0]}" != "0" ]; then 119 | echo "Could not load remote document." 120 | echo "Please check your url or network connectivity" 121 | exit 1 122 | fi 123 | if [ "$need_replace" = "yes" ]; then 124 | echo 125 | echo "!! '$gh_src' is not a local file" 126 | echo "!! Can't insert the TOC into it." 127 | echo 128 | fi 129 | else 130 | local rawhtml=$(gh_toc_md2html "$gh_src") 131 | if [ "$rawhtml" == "XXNetworkErrorXX" ]; then 132 | echo "Parsing local markdown file requires access to github API" 133 | echo "Please make sure curl is installed and check your network connectivity" 134 | exit 1 135 | fi 136 | if [ "$rawhtml" == "XXRateLimitXX" ]; then 137 | echo "Parsing local markdown file requires access to github API" 138 | echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting" 139 | TOKEN="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" 140 | echo "or place github auth token here: $TOKEN" 141 | exit 1 142 | fi 143 | local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy"` 144 | echo "$toc" 145 | if [ "$need_replace" = "yes" ]; then 146 | if grep -Fxq "" $gh_src && grep -Fxq "" $gh_src; then 147 | echo "Found markers" 148 | else 149 | echo "You don't have or in your file...exiting" 150 | exit 1 151 | fi 152 | local ts="<\!--ts-->" 153 | local te="<\!--te-->" 154 | local dt=`date +'%F_%H%M%S'` 155 | local ext=".orig.${dt}" 156 | local toc_path="${gh_src}.toc.${dt}" 157 | #local toc_footer="" 158 | # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html 159 | # clear old TOC 160 | sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" 161 | # create toc file 162 | echo "${toc}" > "${toc_path}" 163 | # echo -e "\n${toc_footer}\n" >> "$toc_path" 164 | # insert toc file 165 | if [[ "`uname`" == "Darwin" ]]; then 166 | sed -i "" "/${ts}/r ${toc_path}" "$gh_src" 167 | else 168 | sed -i "/${ts}/r ${toc_path}" "$gh_src" 169 | fi 170 | echo 171 | echo "!! TOC was added into: '$gh_src'" 172 | echo "!! Origin version of the file: '${gh_src}${ext}'" 173 | echo "!! TOC added into a separate file: '${toc_path}'" 174 | echo 175 | fi 176 | fi 177 | } 178 | 179 | # 180 | # Grabber of the TOC from rendered html 181 | # 182 | # $1 — a source url of document. 183 | # It's need if TOC is generated for multiple documents. 184 | # 185 | gh_toc_grab() { 186 | # if closed is on the new line, then move it on the prev line 187 | # for example: 188 | # was: The command foo1 189 | # 190 | # became: The command foo1 191 | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | 192 | # find strings that corresponds to template 193 | grep -E -o '//g' | sed 's/<\/code>//g' | 196 | # now all rows are like: 197 | # ... .*<\/h/)+2, RLENGTH-5) 204 | href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7) 205 | print sprintf("%*s", level*3, " ") "* [" text "](" gh_url href ")" }' | 206 | sed 'y/+/ /; s/%/\\x/g')" 207 | } 208 | 209 | # 210 | # Returns filename only from full path or url 211 | # 212 | gh_toc_get_filename() { 213 | echo "${1##*/}" 214 | } 215 | 216 | # 217 | # Options hendlers 218 | # 219 | gh_toc_app() { 220 | local need_replace="no" 221 | 222 | if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then 223 | local app_name=$(basename "$0") 224 | echo "GitHub TOC generator ($app_name): $gh_toc_version" 225 | echo "" 226 | echo "Usage:" 227 | echo " $app_name [--insert] src [src] Create TOC for a README file (url or local path)" 228 | echo " $app_name - Create TOC for markdown from STDIN" 229 | echo " $app_name --help Show help" 230 | echo " $app_name --version Show version" 231 | return 232 | fi 233 | 234 | if [ "$1" = '--version' ]; then 235 | echo "$gh_toc_version" 236 | echo 237 | echo "os: `lsb_release -d | cut -f 2`" 238 | echo "kernel: `cat /proc/version`" 239 | echo "shell: `$SHELL --version`" 240 | echo 241 | for tool in curl wget grep awk sed; do 242 | printf "%-5s: " $tool 243 | echo `$tool --version | head -n 1` 244 | done 245 | return 246 | fi 247 | 248 | if [ "$1" = "-" ]; then 249 | if [ -z "$TMPDIR" ]; then 250 | TMPDIR="/tmp" 251 | elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then 252 | mkdir -p "$TMPDIR" 253 | fi 254 | local gh_tmp_md 255 | gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) 256 | while read input; do 257 | echo "$input" >> "$gh_tmp_md" 258 | done 259 | gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" 260 | return 261 | fi 262 | 263 | if [ "$1" = '--insert' ]; then 264 | need_replace="yes" 265 | shift 266 | fi 267 | 268 | for md in "$@" 269 | do 270 | echo "" 271 | gh_toc "$md" "$#" "$need_replace" 272 | done 273 | 274 | echo "" 275 | echo "Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)" 276 | } 277 | 278 | # 279 | # Entry point 280 | # 281 | gh_toc_app "$@" 282 | -------------------------------------------------------------------------------- /src/scripts/lint/lint-all.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from glob import glob 4 | from pathlib import Path 5 | from subprocess import run 6 | from os import path 7 | from time import time 8 | 9 | 10 | time_start = time() 11 | 12 | exit_code = 0 13 | num_linters = 0 14 | mod_path = Path(__file__).parent 15 | for lint in glob(f"{mod_path}/lint-*"): 16 | lint = path.abspath(lint) 17 | if lint == path.abspath(__file__): 18 | continue 19 | 20 | num_linters += 1 21 | result = run([lint]) 22 | if result.returncode == 0: 23 | continue 24 | 25 | print(f"^---- failure from {lint.split('/')[-1]}") 26 | exit_code |= result.returncode 27 | 28 | time_end = time() 29 | print(f"{num_linters} linters in {time_end - time_start:0.2}s") 30 | exit(exit_code) 31 | -------------------------------------------------------------------------------- /src/scripts/lint/lint-clang-format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from glob import glob 4 | from pathlib import Path 5 | from subprocess import run 6 | from os import path 7 | import subprocess 8 | import sys 9 | from time import time 10 | import re 11 | 12 | root_path = path.abspath(Path(__file__).parent.parent.parent.parent) 13 | 14 | globs = [ 15 | f"{root_path}/src/include/**/*.h", 16 | f"{root_path}/src/test/**/*.h", 17 | f"{root_path}/src/test/**/*.cpp", 18 | ] 19 | exclusions = [ 20 | '/thirdparty/', 21 | '/scripts/', 22 | ] 23 | 24 | files = [] 25 | for g in globs: 26 | r = glob(g, recursive=True) 27 | files.extend(r) 28 | 29 | # filter out exclusions 30 | for exclusion in exclusions: 31 | l = filter(lambda file: re.search(exclusion, file) == None, files) 32 | files = list(l) 33 | 34 | if len(files) == 0: 35 | print("could not find any files!") 36 | sys.exit(1) 37 | 38 | command = ['clang-format', '--dry-run', '-Werror'] + files 39 | p = subprocess.Popen(command, 40 | stdout=subprocess.PIPE, 41 | stderr=None, 42 | stdin=subprocess.PIPE, 43 | universal_newlines=True) 44 | 45 | stdout, stderr = p.communicate() 46 | 47 | print(f"clang-format checked {len(files)} files") 48 | 49 | if p.returncode != 0: 50 | sys.exit(p.returncode) 51 | -------------------------------------------------------------------------------- /src/scripts/lint/lint-version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import pathlib 5 | import re 6 | 7 | 8 | root = os.path.abspath(pathlib.Path(__file__).parent.parent.parent.parent) 9 | 10 | # filename, pattern, number of occurrences 11 | file_pattern_count = [ 12 | ( 13 | f"{root}/src/docs/conf.py", 14 | r"version = 'v(\d+)\.(\d+)\.(\d+)'", 15 | 1), 16 | (f"{root}/docs/CODE_OF_CONDUCT.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 17 | (f"{root}/docs/comparison.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 18 | (f"{root}/docs/genindex.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 19 | (f"{root}/docs/index.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 20 | (f"{root}/docs/license.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 21 | (f"{root}/docs/reference.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 22 | (f"{root}/docs/search.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 23 | (f"{root}/docs/tutorial.html", r"^\s*v(\d+)\.(\d+)\.(\d+)$", 1), 24 | ] 25 | 26 | # let's parse the reference from svector.h 27 | major = "??" 28 | minor = "??" 29 | patch = "??" 30 | with open(f"{root}/src/include/nanobench.h", "r") as f: 31 | for line in f: 32 | r = re.search( 33 | r"#define ANKERL_NANOBENCH_VERSION_([A-Z]+) (\d+)", line) 34 | if not r: 35 | continue 36 | 37 | if "MAJOR" == r.group(1): 38 | major = r.group(2) 39 | elif "MINOR" == r.group(1): 40 | minor = r.group(2) 41 | elif "PATCH" == r.group(1): 42 | patch = r.group(2) 43 | else: 44 | "match but with something else!" 45 | exit(1) 46 | 47 | is_ok = True 48 | for (filename, pattern, count) in file_pattern_count: 49 | num_found = 0 50 | with open(filename, "r") as f: 51 | for line in f: 52 | r = re.search(pattern, line) 53 | if r: 54 | num_found += 1 55 | if major != r.group(1) or minor != r.group(2) or patch != r.group(3): 56 | is_ok = False 57 | print( 58 | f"ERROR in {filename}: got '{line.strip()}' but version should be '{major}.{minor}.{patch}'") 59 | if num_found != count: 60 | is_ok = False 61 | print( 62 | f"ERROR in {filename}: expected {count} occurrences but found it {num_found} times") 63 | 64 | if not is_ok: 65 | exit(1) 66 | -------------------------------------------------------------------------------- /src/scripts/toc-insert.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ROOT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd )" 3 | 4 | find ${ROOT_DIR} -name "*.md" -exec ${ROOT_DIR}/src/scripts/gh-md-toc --insert \{\} \; 5 | find ${ROOT_DIR} -name "*.md.orig.*" -exec rm \{\} \; 6 | find ${ROOT_DIR} -name "*.md.toc.*" -exec rm \{\} \; 7 | -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(app) 2 | add_subdirectory(thirdparty) 3 | 4 | target_include_directories(nb PRIVATE ${CMAKE_CURRENT_LIST_DIR}) 5 | 6 | target_sources_local(nb PRIVATE 7 | example_always_the_same.cpp 8 | example_atomic.cpp 9 | example_branch_misses.cpp 10 | example_complexity.cpp 11 | example_containers.cpp 12 | example_csv.cpp 13 | example_hide_output.cpp 14 | example_preconfigured_name.cpp 15 | example_pyperf.cpp 16 | example_random_number_generators.cpp 17 | example_random_uniform01.cpp 18 | example_random2.cpp 19 | example_shuffle.cpp 20 | tutorial_complexity_set.cpp 21 | tutorial_complexity_sort.cpp 22 | tutorial_context.cpp 23 | tutorial_fast_v1.cpp 24 | tutorial_fast_v2.cpp 25 | tutorial_fluctuating_v1.cpp 26 | tutorial_fluctuating_v2.cpp 27 | tutorial_mustache.cpp 28 | tutorial_render_simple.cpp 29 | tutorial_slow_v1.cpp 30 | tutorial_slow_v2.cpp 31 | unit_api.cpp 32 | unit_cold.cpp 33 | unit_exact_iters_and_epochs.cpp 34 | unit_romutrio.cpp 35 | unit_templates.cpp 36 | unit_timeunit.cpp 37 | unit_to_s.cpp 38 | ) 39 | -------------------------------------------------------------------------------- /src/test/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources_local(nb PRIVATE 2 | nanobench.cpp 3 | doctest.cpp 4 | ) 5 | -------------------------------------------------------------------------------- /src/test/app/doctest.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | -------------------------------------------------------------------------------- /src/test/app/nanobench.cpp: -------------------------------------------------------------------------------- 1 | #define ANKERL_NANOBENCH_IMPLEMENT 2 | #include 3 | -------------------------------------------------------------------------------- /src/test/example_always_the_same.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // NOLINTNEXTLINE 7 | TEST_CASE("always_the_same") { 8 | ankerl::nanobench::Bench bench; 9 | 10 | std::string const shortString = "hello World!"; 11 | 12 | ankerl::nanobench::Rng rng; 13 | for (int i = 0; i < 40; ++i) { 14 | bench 15 | .run("rng() " + std::to_string(i), 16 | [&] { 17 | rng(); 18 | }) 19 | .doNotOptimizeAway(rng()); 20 | } 21 | 22 | std::ofstream html("always_the_same.html"); 23 | bench.render(ankerl::nanobench::templates::htmlBoxplot(), html); 24 | 25 | std::ofstream json("always_the_same.json"); 26 | bench.render(ankerl::nanobench::templates::json(), json); 27 | } 28 | -------------------------------------------------------------------------------- /src/test/example_atomic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // Demonstrates a very simple benchmark that evalutes the performance of a CAS 7 | // operation using std::atomic. On my system, this prints: 8 | // 9 | // | relative | ns/op | op/s | MdAPE | benchmark 10 | // |---------:|--------------------:|--------------------:|--------:|:---------------------------------------------- 11 | // | | 5.63 | 177,553,749.61 | 0.0% | 12 | // `compare_exchange_strong` 13 | // 14 | // example from https://github.com/cameron314/microbench 15 | // NOLINTNEXTLINE 16 | TEST_CASE("performance_counters") { 17 | ankerl::nanobench::Bench bench; 18 | 19 | bench.run("start & stop counting", [&] { 20 | std::vector const v = {{11, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7}}; 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /src/test/example_branch_misses.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // This example should show about one branch per op, and about 50% branch misses 7 | // since it is completely unpredictable. 8 | // NOLINTNEXTLINE 9 | TEST_CASE("example_branch_misses") { 10 | ankerl::nanobench::Rng rng; 11 | 12 | ankerl::nanobench::Bench bench; 13 | bench.title("evaluating branch misses"); 14 | 15 | // on average, rng() is called 1.5 times per loop. We ignore the & 1U check. 16 | bench.batch(1.5) 17 | .run("50% forced misspredictions", 18 | [&] { 19 | if ((rng() & 1U) != 0U) { 20 | rng(); 21 | } 22 | }) 23 | .doNotOptimizeAway(rng); 24 | 25 | bench.batch(1) 26 | .run("no forced misspredictions", 27 | [&] { 28 | rng(); 29 | }) 30 | .doNotOptimizeAway(rng); 31 | 32 | std::ofstream fout("example_branch_misses.json"); 33 | bench.render(ankerl::nanobench::templates::json(), fout); 34 | } 35 | -------------------------------------------------------------------------------- /src/test/example_complexity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // NOLINTNEXTLINE 11 | TEST_CASE("example_complexity_quadratic") { 12 | // create an ankerl::nanobench::Config object that is used in all the 13 | // benchmarks 14 | ankerl::nanobench::Bench bench; 15 | ankerl::nanobench::Rng rng{123}; 16 | 17 | // run the same benchmark multiple times with different ranges 18 | for (size_t range = 10; range <= 1000; range *= 2) { 19 | // create vector with random data 20 | std::vector vec(range, 0.0); 21 | for (auto& x : vec) { 22 | x = rng.uniform01(); 23 | } 24 | 25 | // each run is configured with complexityN(range) to specify the run's 26 | // input N 27 | bench.complexityN(range).run( 28 | "minimum pair " + std::to_string(range), [&] { 29 | // Actual algorithm we want to evaluate 30 | double minVal = std::numeric_limits::max(); 31 | for (size_t i = 0; i < vec.size() - 1; ++i) { 32 | for (size_t j = i + 1; j < vec.size(); ++j) { 33 | auto diff = vec[i] - vec[j]; 34 | minVal = std::min(minVal, diff * diff); 35 | } 36 | } 37 | ankerl::nanobench::doNotOptimizeAway(minVal); 38 | }); 39 | } 40 | 41 | // after all the runs are done, calculate the BigO, and show the results 42 | std::cout << bench.complexityBigO() << std::endl; 43 | } 44 | -------------------------------------------------------------------------------- /src/test/example_containers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // Benchmarks insertion and removal in multiple different containers. 8 | // This uses a very fast random generator. 9 | // NOLINTNEXTLINE 10 | TEST_CASE("example_containers") { 11 | ankerl::nanobench::Bench bench; 12 | bench.title("random insert & erase in containers"); 13 | 14 | // creates a random number generator to use in the benchmarks 15 | ankerl::nanobench::Rng rng; 16 | std::set s; 17 | uint64_t const bitmask = UINT64_C(0xff); 18 | 19 | bench 20 | .run("std::set", 21 | [&] { 22 | s.insert(rng() & bitmask); 23 | s.erase(rng() & bitmask); 24 | }) 25 | .doNotOptimizeAway(&s); 26 | 27 | std::unordered_set us; 28 | bench 29 | .run("std::unordered_set", 30 | [&] { 31 | us.insert(rng() & bitmask); 32 | us.erase(rng() & bitmask); 33 | }) 34 | .doNotOptimizeAway(&us); 35 | } 36 | -------------------------------------------------------------------------------- /src/test/example_csv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace { 10 | 11 | template 12 | void testBenchSet(char const* label, int n, ankerl::nanobench::Bench& bench) { 13 | bench.run(label, [&] { 14 | ContainerT items; 15 | for (int i = 0; i < n; ++i) { 16 | // NOLINTNEXTLINE(performance-inefficient-vector-operation) 17 | items.push_back(i); 18 | } 19 | ankerl::nanobench::doNotOptimizeAway(&items); 20 | }); 21 | } 22 | 23 | void exampleCsv(bool useCsv) { 24 | ankerl::nanobench::Bench bench; 25 | if (useCsv) { 26 | bench.output(nullptr); 27 | } 28 | 29 | for (int n = 100; n <= 10000; n *= 10) { 30 | bench.title("Size " + std::to_string(n)); 31 | testBenchSet>("std::vector", n, bench); 32 | testBenchSet>("std::deque", n, bench); 33 | testBenchSet>("std::list", n, bench); 34 | if (useCsv) { 35 | // could also use ankerl::nanobench::templates::csv() which contains 36 | // a header 37 | bench.render(ankerl::nanobench::templates::csv(), std::cout); 38 | } 39 | } 40 | } 41 | 42 | } // namespace 43 | 44 | // NOLINTNEXTLINE 45 | TEST_CASE("example_csv_csv") { 46 | exampleCsv(true); 47 | } 48 | 49 | // NOLINTNEXTLINE 50 | TEST_CASE("example_csv_md") { 51 | exampleCsv(false); 52 | } 53 | -------------------------------------------------------------------------------- /src/test/example_hide_output.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // Demonstrates a very simple benchmark that evalutes the performance of a CAS 8 | // operation using std::atomic. On my system, this prints: 9 | // 10 | // | relative | ns/op | op/s | MdAPE | benchmark 11 | // |---------:|--------------------:|--------------------:|--------:|:---------------------------------------------- 12 | // | | 5.63 | 177,553,749.61 | 0.0% | 13 | // `compare_exchange_strong` 14 | // 15 | // example from https://github.com/cameron314/microbench 16 | // NOLINTNEXTLINE 17 | TEST_CASE("example_atomic") { 18 | int y = 0; 19 | std::atomic x(0); 20 | ankerl::nanobench::Bench bench; 21 | 22 | // don't write anything 23 | bench.output(nullptr); 24 | 25 | bench.run("compare_exchange_strong", [&] { 26 | x.compare_exchange_strong(y, 0); 27 | }); 28 | auto const& r = bench.results().front(); 29 | std::cout << "result: " 30 | << r.median(ankerl::nanobench::Result::Measure::elapsed) / 31 | r.config().mBatch 32 | << "s/" << r.config().mUnit << std::endl; 33 | } 34 | -------------------------------------------------------------------------------- /src/test/example_preconfigured_name.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // NOLINTNEXTLINE 7 | TEST_CASE("preconfigured_name") { 8 | ankerl::nanobench::Bench bench; 9 | bench.name("start & stop 1").run([] { 10 | std::vector const v = {{11, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7}}; 11 | }); 12 | 13 | bench.name("run2"); 14 | bench.run([] { 15 | std::vector const v = {{11, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7}}; 16 | }); 17 | } 18 | 19 | // NOLINTNEXTLINE 20 | TEST_CASE("keep_only_results") { 21 | 22 | std::vector results; 23 | ankerl::nanobench::Bench bench; 24 | ankerl::nanobench::Rng rng(123); 25 | 26 | bench.run("a", [&] { 27 | rng(); 28 | }); 29 | results.insert(results.end(), bench.results().begin(), 30 | bench.results().end()); 31 | } 32 | -------------------------------------------------------------------------------- /src/test/example_pyperf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // NOLINTNEXTLINE 9 | TEST_CASE("shuffle_pyperf") { 10 | std::vector data(500, 0); // input data for shuffling 11 | 12 | // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp) 13 | std::default_random_engine defaultRng(123); 14 | std::ofstream fout1("pyperf_shuffle_std.json"); 15 | ankerl::nanobench::Bench() 16 | .epochs(100) 17 | .run("std::shuffle with std::default_random_engine", 18 | [&]() { 19 | std::shuffle(data.begin(), data.end(), defaultRng); 20 | }) 21 | .render(ankerl::nanobench::templates::pyperf(), fout1); 22 | 23 | std::ofstream fout2("pyperf_shuffle_nanobench.json"); 24 | ankerl::nanobench::Rng rng(123); 25 | ankerl::nanobench::Bench() 26 | .epochs(100) 27 | .run("ankerl::nanobench::Rng::shuffle", 28 | [&]() { 29 | rng.shuffle(data); 30 | }) 31 | .render(ankerl::nanobench::templates::pyperf(), fout2); 32 | } 33 | -------------------------------------------------------------------------------- /src/test/example_random2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // Source: http://quick-bench.com/2dBt6SOQTSlztlqmlo0w7pv6iNM 8 | // https://www.reddit.com/r/prng/comments/fchmfd/romu_fast_nonlinear_pseudorandom_number_generators/fl6lfw9/ 9 | 10 | namespace { 11 | 12 | #define ROTL(d, lrot) (((d) << (lrot)) | ((d) >> (8 * sizeof(d) - (lrot)))) 13 | 14 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,readability-static-definition-in-anonymous-namespace) 15 | static uint64_t xState = 1U, yState = 1U, zState = 1U; 16 | 17 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t romuTrio_random() { 18 | uint64_t const xp = xState; 19 | uint64_t const yp = yState; 20 | uint64_t const zp = zState; 21 | xState = 15241094284759029579U * zp; 22 | yState = yp - xp; 23 | yState = ROTL(yState, 12U); 24 | zState = zp - yp; 25 | zState = ROTL(zState, 44U); 26 | return xp; 27 | } 28 | 29 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t romuDuo_random() { 30 | uint64_t const xp = xState; 31 | xState = 15241094284759029579U * yState; 32 | yState = ROTL(yState, 36U) + ROTL(yState, 15U) - xp; 33 | return xp; 34 | } 35 | 36 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t romuDuoJr_random() { 37 | uint64_t const xp = xState; 38 | xState = 15241094284759029579U * yState; 39 | yState = yState - xp; 40 | yState = ROTL(yState, 27U); 41 | return xp; 42 | } 43 | 44 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables,readability-static-definition-in-anonymous-namespace) 45 | static uint64_t stateA = 1U, stateB = 1U; 46 | 47 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t tangle() { 48 | uint64_t const s = (stateA += 0xC6BC279692B5C323U); 49 | uint64_t const t = (stateB += 0x9E3779B97F4A7C16U); 50 | uint64_t const z = (s ^ s >> 31U) * t; 51 | return z ^ z >> 26U; 52 | } 53 | 54 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t orbit() { 55 | uint64_t const s = (stateA += 0xC6BC279692B5C323U); 56 | uint64_t const t = ((s == 0U) ? stateB : (stateB += 0x9E3779B97F4A7C15U)); 57 | uint64_t const z = (s ^ s >> 31U) * ((t ^ t >> 22U) | 1U); 58 | return z ^ z >> 26U; 59 | } 60 | 61 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t splitmix64() { 62 | uint64_t z = (stateA += 0x9E3779B97F4A7C15U); 63 | z = (z ^ z >> 30U) * 0xbf58476d1ce4e5b9U; 64 | z = (z ^ z >> 27U) * 0x94d049bb133111ebU; 65 | return z ^ z >> 31U; 66 | } 67 | 68 | // NOLINTNEXTLINE 69 | static uint64_t s[4]; 70 | 71 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t xoshiroStarStar() { 72 | const uint64_t result = ROTL(s[1] * 5, 7U) * 9U; 73 | 74 | const uint64_t t = s[1] << 17U; 75 | 76 | s[2] ^= s[0]; 77 | s[3] ^= s[1]; 78 | s[1] ^= s[2]; 79 | s[0] ^= s[3]; 80 | 81 | s[2] ^= t; 82 | 83 | s[3] = ROTL(s[3], 45U); 84 | 85 | return result; 86 | } 87 | 88 | // NOLINTNEXTLINE 89 | static uint64_t sr[4]; 90 | 91 | ANKERL_NANOBENCH_NO_SANITIZE("integer") uint64_t xoroshiroPlus() { 92 | const uint64_t s0 = sr[0]; 93 | uint64_t s1 = sr[1]; 94 | const uint64_t result = s0 + s1; 95 | 96 | s1 ^= s0; 97 | sr[0] = ROTL(s0, 24U) ^ s1 ^ (s1 << 16U); // a, b 98 | sr[1] = ROTL(s1, 37U); // c 99 | 100 | return result; 101 | } 102 | 103 | } // namespace 104 | 105 | // NOLINTNEXTLINE 106 | TEST_CASE("example_random2") { 107 | auto bench = ankerl::nanobench::Bench().relative(true); 108 | 109 | // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp) 110 | std::mt19937_64 mt{}; 111 | bench.run("std::mt19937_64", [&] { 112 | ankerl::nanobench::doNotOptimizeAway(mt()); 113 | }); 114 | 115 | bench.run("RomuTrio", [] { 116 | ankerl::nanobench::doNotOptimizeAway(romuTrio_random()); 117 | }); 118 | 119 | bench.run("RomuDuo", [] { 120 | ankerl::nanobench::doNotOptimizeAway(romuDuo_random()); 121 | }); 122 | 123 | bench.run("RomuDuoJr", [] { 124 | ankerl::nanobench::doNotOptimizeAway(romuDuoJr_random()); 125 | }); 126 | 127 | bench.run("Tangle", [] { 128 | ankerl::nanobench::doNotOptimizeAway(tangle()); 129 | }); 130 | 131 | bench.run("Orbit", [] { 132 | ankerl::nanobench::doNotOptimizeAway(orbit()); 133 | }); 134 | 135 | bench.run("SplitMix", [] { 136 | ankerl::nanobench::doNotOptimizeAway(splitmix64()); 137 | }); 138 | 139 | bench.run("XoshiroStarStar", [] { 140 | ankerl::nanobench::doNotOptimizeAway(xoshiroStarStar()); 141 | }); 142 | 143 | bench.run("XoroshiroPlus", [] { 144 | ankerl::nanobench::doNotOptimizeAway(xoroshiroPlus()); 145 | }); 146 | } 147 | 148 | class RomuMono32 { 149 | public: 150 | explicit RomuMono32(uint32_t seed) 151 | : mState{(seed & UINT32_C(0x1fffffff)) + UINT32_C(1156979152)} {} 152 | 153 | uint16_t operator()() noexcept { 154 | auto const result = static_cast(mState >> 16U); 155 | mState *= UINT32_C(3611795771); 156 | mState = ROTL(mState, 12U); 157 | return result; 158 | } 159 | 160 | ANKERL_NANOBENCH(NODISCARD) uint32_t state() const noexcept { 161 | return mState; 162 | } 163 | 164 | private: 165 | uint32_t mState; 166 | }; 167 | 168 | // NOLINTNEXTLINE 169 | TEST_CASE("romumono32_all_states" * doctest::skip()) { 170 | 171 | uint32_t n = 0; 172 | RomuMono32 rm(123); 173 | auto initialState = rm.state(); 174 | do { 175 | rm(); 176 | ++n; 177 | } while (rm.state() != initialState); 178 | 179 | std::cout << n << std::endl; 180 | } 181 | -------------------------------------------------------------------------------- /src/test/example_random_uniform01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // NOLINTNEXTLINE 9 | TEST_CASE("example_random_uniform01") { 10 | ankerl::nanobench::Bench bench; 11 | bench.title("random double in [0, 1(").relative(true); 12 | 13 | std::random_device dev; 14 | std::default_random_engine defaultRng(dev()); 15 | double d = 0; 16 | bench 17 | .run("std::default_random_engine & std::uniform_real_distribution", 18 | [&] { 19 | d += std::uniform_real_distribution<>{}(defaultRng); 20 | }) 21 | .doNotOptimizeAway(d); 22 | 23 | d = 0; 24 | bench 25 | .run("std::default_random_engine && std::generate_canonical", 26 | [&] { 27 | d += std::generate_canonical< 28 | double, std::numeric_limits::digits>(defaultRng); 29 | }) 30 | .doNotOptimizeAway(d); 31 | 32 | ankerl::nanobench::Rng nanobenchRng{123}; 33 | d = 0; 34 | bench 35 | .run("ankerl::nanobench::Rng & std::uniform_real_distribution", 36 | [&] { 37 | d += std::uniform_real_distribution<>{}(nanobenchRng); 38 | }) 39 | .doNotOptimizeAway(d); 40 | 41 | bench 42 | .run("ankerl::nanobench::Rng & std::generate_canonical", 43 | [&] { 44 | d += std::generate_canonical< 45 | double, std::numeric_limits::digits>(nanobenchRng); 46 | }) 47 | .doNotOptimizeAway(d); 48 | 49 | d = 0; 50 | bench 51 | .run("ankerl::nanobench::Rng::uniform01()", 52 | [&] { 53 | d += nanobenchRng.uniform01(); 54 | }) 55 | .doNotOptimizeAway(d); 56 | 57 | std::ofstream fout("example_random_uniform01.json"); 58 | bench.render(ankerl::nanobench::templates::json(), fout); 59 | } 60 | -------------------------------------------------------------------------------- /src/test/example_shuffle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // NOLINTNEXTLINE 8 | TEST_CASE("shuffle") { 9 | // input data for shuffling 10 | std::vector data(10000, 0); 11 | 12 | ankerl::nanobench::Bench bench; 13 | bench.relative(true).batch(data.size()).unit("elem"); 14 | 15 | // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp) 16 | std::default_random_engine defaultRng(123); 17 | bench.run("std::shuffle with std::default_random_engine", [&]() { 18 | std::shuffle(data.begin(), data.end(), defaultRng); 19 | }); 20 | 21 | ankerl::nanobench::Rng rng(123); 22 | bench.run("std::shuffle with ankerl::nanobench::Rng", [&]() { 23 | std::shuffle(data.begin(), data.end(), rng); 24 | }); 25 | 26 | bench.run("ankerl::nanobench::Rng::shuffle", [&]() { 27 | rng.shuffle(data); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/test/thirdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources_local(nb PRIVATE doctest/doctest.h) 2 | -------------------------------------------------------------------------------- /src/test/tutorial_complexity_set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // NOLINTNEXTLINE 8 | TEST_CASE("tutorial_complexity_set_find") { 9 | // Create a single benchmark instance that is used in multiple benchmark 10 | // runs, with different settings for complexityN. 11 | ankerl::nanobench::Bench bench; 12 | 13 | // a RNG to generate input data 14 | ankerl::nanobench::Rng rng; 15 | 16 | std::set set; 17 | 18 | // Running the benchmark multiple times, with different number of elements 19 | for (auto setSize : 20 | {10U, 20U, 50U, 100U, 200U, 500U, 1000U, 2000U, 5000U, 10000U}) { 21 | 22 | // fill up the set with random data 23 | while (set.size() < setSize) { 24 | set.insert(rng()); 25 | } 26 | 27 | // Run the benchmark, provide setSize as the scaling variable. 28 | bench.complexityN(set.size()).run("std::set find", [&] { 29 | ankerl::nanobench::doNotOptimizeAway(set.find(rng())); 30 | }); 31 | } 32 | 33 | // calculate BigO complexy best fit and print the results 34 | std::cout << bench.complexityBigO() << std::endl; 35 | } 36 | -------------------------------------------------------------------------------- /src/test/tutorial_complexity_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // NOLINTNEXTLINE 9 | TEST_CASE("tutorial_complexity_sort") { 10 | ankerl::nanobench::Rng rng{123}; 11 | ankerl::nanobench::Bench bench; 12 | 13 | std::vector data; 14 | 15 | for (size_t n = 10; n < 100000; n *= 2) { 16 | // prepare a set with range number of elements 17 | while (data.size() < n) { 18 | data.push_back(rng()); 19 | } 20 | 21 | // sort should be O(n log n), shuffle is O(n), so we expect O(n log n). 22 | bench.complexityN(n).run("std::sort", [&] { 23 | // we use our own shuffle method instead of std::shuffle, because it 24 | // is *much* faster. Performance is very important, because we 25 | // actually want to benchmark std::sort and not shuffling. 26 | rng.shuffle(data); 27 | 28 | // The actual code under benchmark 29 | std::sort(data.begin(), data.end()); 30 | }); 31 | } 32 | 33 | // calculates bigO of all preconfigured complexity functions 34 | std::cout << bench.complexityBigO() << std::endl; 35 | 36 | // calculates bigO for a custom function 37 | auto logLogN = bench.complexityBigO("O(log log n)", [](double n) { 38 | return std::log2(std::log2(n)); 39 | }); 40 | std::cout << logLogN << std::endl; 41 | } 42 | -------------------------------------------------------------------------------- /src/test/tutorial_context.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace { 8 | 9 | template 10 | void fma() { 11 | T x(1); 12 | T y(2); 13 | T z(3); 14 | z = std::fma(x, y, z); 15 | ankerl::nanobench::doNotOptimizeAway(z); 16 | } 17 | 18 | template 19 | void plus_eq() { 20 | T x(1); 21 | T y(2); 22 | T z(3); 23 | z += x * y; 24 | ankerl::nanobench::doNotOptimizeAway(z); 25 | } 26 | 27 | char const* csv() { 28 | return R"DELIM("title";"name";"scalar";"foo";"elapsed";"total" 29 | {{#result}}"{{title}}";"{{name}}";"{{context(scalar)}}";"{{context(foo)}}";{{median(elapsed)}};{{sumProduct(iterations, elapsed)}} 30 | {{/result}})DELIM"; 31 | } 32 | 33 | } // namespace 34 | 35 | // NOLINTNEXTLINE 36 | TEST_CASE("tutorial_context") { 37 | ankerl::nanobench::Bench bench; 38 | bench.title("Addition").output(nullptr); 39 | bench.context("scalar", "f32") 40 | .context("foo", "bar") 41 | .run("+=", plus_eq) 42 | .run("fma", fma); 43 | bench.context("scalar", "f64") 44 | .context("foo", "baz") 45 | .run("+=", plus_eq) 46 | .run("fma", fma); 47 | bench.render(csv(), std::cout); 48 | // Changing the title resets the results, but not the context: 49 | bench.title("New Title"); 50 | bench.run("+=", plus_eq); 51 | bench.render(csv(), std::cout); 52 | CHECK_EQ(bench.results().front().context("foo"), "baz"); // != bar 53 | // The context has to be reset manually, which causes render to fail: 54 | bench.title("Yet Another Title").clearContext(); 55 | bench.run("+=", plus_eq); 56 | 57 | // NOLINTNEXTLINE(llvm-else-after-return,readability-else-after-return) 58 | CHECK_THROWS(bench.render(csv(), std::cout)); 59 | } 60 | -------------------------------------------------------------------------------- /src/test/tutorial_fast_v1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // NOLINTNEXTLINE 5 | TEST_CASE("tutorial_fast_v1") { 6 | uint64_t x = 1; 7 | ankerl::nanobench::Bench().run("++x", [&]() { 8 | ++x; 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/test/tutorial_fast_v2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // NOLINTNEXTLINE 5 | TEST_CASE("tutorial_fast_v2") { 6 | uint64_t x = 1; 7 | ankerl::nanobench::Bench().run("++x", [&]() { 8 | ankerl::nanobench::doNotOptimizeAway(x += 1); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/test/tutorial_fluctuating_v1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // NOLINTNEXTLINE 7 | TEST_CASE("tutorial_fluctuating_v1") { 8 | std::random_device dev; 9 | std::mt19937_64 rng(dev()); 10 | ankerl::nanobench::Bench().run("random fluctuations", [&] { 11 | // each run, perform a random number of rng calls 12 | auto iterations = rng() & UINT64_C(0xff); 13 | for (uint64_t i = 0; i < iterations; ++i) { 14 | ankerl::nanobench::doNotOptimizeAway(rng()); 15 | } 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /src/test/tutorial_fluctuating_v2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // NOLINTNEXTLINE 7 | TEST_CASE("tutorial_fluctuating_v2") { 8 | std::random_device dev; 9 | std::mt19937_64 rng(dev()); 10 | ankerl::nanobench::Bench().minEpochIterations(5000).run( 11 | "random fluctuations", [&] { 12 | // each run, perform a random number of rng calls 13 | auto iterations = rng() & UINT64_C(0xff); 14 | for (uint64_t i = 0; i < iterations; ++i) { 15 | ankerl::nanobench::doNotOptimizeAway(rng()); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/test/tutorial_mustache.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace { 8 | 9 | void gen(std::string const& typeName, char const* mustacheTemplate, 10 | ankerl::nanobench::Bench const& bench) { 11 | 12 | std::ofstream templateOut("mustache.template." + typeName); 13 | templateOut << mustacheTemplate; 14 | 15 | std::ofstream renderOut("mustache.render." + typeName); 16 | ankerl::nanobench::render(mustacheTemplate, bench, renderOut); 17 | } 18 | 19 | } // namespace 20 | 21 | // NOLINTNEXTLINE 22 | TEST_CASE("tutorial_mustache") { 23 | ankerl::nanobench::Bench bench; 24 | bench.title("Benchmarking std::mt19937_64 and std::knuth_b"); 25 | 26 | // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp) 27 | std::mt19937_64 rng1; 28 | bench.run("std::mt19937_64", [&] { 29 | ankerl::nanobench::doNotOptimizeAway(rng1()); 30 | }); 31 | 32 | // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp) 33 | std::knuth_b rng2; 34 | bench.run("std::knuth_b", [&] { 35 | ankerl::nanobench::doNotOptimizeAway(rng2()); 36 | }); 37 | 38 | gen("json", ankerl::nanobench::templates::json(), bench); 39 | gen("html", ankerl::nanobench::templates::htmlBoxplot(), bench); 40 | gen("csv", ankerl::nanobench::templates::csv(), bench); 41 | } 42 | -------------------------------------------------------------------------------- /src/test/tutorial_render_simple.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // NOLINTNEXTLINE 8 | TEST_CASE("tutorial_render_simple") { 9 | std::atomic x(0); 10 | 11 | ankerl::nanobench::Bench() 12 | .output(nullptr) 13 | .run("std::vector", 14 | [&] { 15 | ++x; 16 | }) 17 | .render(ankerl::nanobench::templates::csv(), std::cout); 18 | } 19 | -------------------------------------------------------------------------------- /src/test/tutorial_slow_v1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // NOLINTNEXTLINE 8 | TEST_CASE("tutorial_slow_v1") { 9 | ankerl::nanobench::Bench().run("sleep 100ms, auto", [&] { 10 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /src/test/tutorial_slow_v2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // NOLINTNEXTLINE 8 | TEST_CASE("tutorial_slow_v2") { 9 | ankerl::nanobench::Bench().epochs(3).run("sleep 100ms", [&] { 10 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /src/test/unit_api.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // NOLINTNEXTLINE 7 | TEST_CASE("string") { 8 | std::string shortString = "hello"; 9 | ankerl::nanobench::Bench bench; 10 | bench.run("short string", [&] { 11 | (void)std::string(shortString); 12 | }); 13 | 14 | std::string longString = "0123456789abcdefghijklmnopqrstuvwxyz"; 15 | bench.run("long string", [&] { 16 | (void)std::string(longString); 17 | }); 18 | } 19 | 20 | // NOLINTNEXTLINE 21 | TEST_CASE("incorrect1") { 22 | // compiler optimizes sin() away, because it is unused 23 | ankerl::nanobench::Bench bench; 24 | bench.run("std::sin(2.32)", [&] { 25 | (void)std::sin(2.32); 26 | }); 27 | } 28 | 29 | // NOLINTNEXTLINE 30 | TEST_CASE("incorrect2") { 31 | // compiler can still calculate sin(2.32) at compile time and replace it 32 | // with the number. So we get a result, but it's still not what we want 33 | ankerl::nanobench::Bench bench; 34 | bench.run("std::sin(2.32)", [&] { 35 | ankerl::nanobench::doNotOptimizeAway(std::sin(2.32)); 36 | }); 37 | } 38 | 39 | // NOLINTNEXTLINE 40 | TEST_CASE("incorrect3") { 41 | // we produce a side effect by always modifying x, but the result is never 42 | // used so the compiler might still optimize it away 43 | double x = 123.4; 44 | ankerl::nanobench::Bench bench; 45 | bench.run("x = std::sin(x)", [&] { 46 | x = std::sin(x); 47 | }); 48 | } 49 | 50 | // NOLINTNEXTLINE 51 | TEST_CASE("simplest_api") { 52 | // correct: std::sin() produces a side effect, and after benchmark the 53 | // result is checked. 54 | double x = 123.4; 55 | ankerl::nanobench::Bench bench; 56 | bench 57 | .run("x = std::sin(x) noop afterwards", 58 | [&] { 59 | x = std::sin(x); 60 | }) 61 | .doNotOptimizeAway(x); 62 | bench.run("x = std::sin(x) always noop", [&] { 63 | ankerl::nanobench::doNotOptimizeAway(x = std::sin(x)); 64 | }); 65 | } 66 | 67 | // NOLINTNEXTLINE 68 | TEST_CASE("comparison") { 69 | double x = 1.321; 70 | 71 | auto bench = 72 | ankerl::nanobench::Bench().title("relative comparisons").relative(true); 73 | bench 74 | .run("x += x double", 75 | [&] { 76 | x += x; 77 | }) 78 | .doNotOptimizeAway(x); 79 | 80 | uint64_t n = 1; 81 | bench 82 | .run("n *= 0x42ad44f557ff4d43", 83 | [&]() ANKERL_NANOBENCH_NO_SANITIZE("integer") { 84 | n *= UINT64_C(0x42ad44f557ff4d43); 85 | }) 86 | .doNotOptimizeAway(n); 87 | 88 | x = 1.123; 89 | bench 90 | .run("std::sin(x)", 91 | [&] { 92 | x += std::sin(x); 93 | }) 94 | .doNotOptimizeAway(x); 95 | 96 | // The compiler might be smart enough to optimize this away, since log(1) 97 | // = 1. 98 | x = 1.123; 99 | bench 100 | .run("std::log(x)", 101 | [&] { 102 | x += std::log(x); 103 | }) 104 | .doNotOptimizeAway(x); 105 | 106 | x = 1.123; 107 | bench 108 | .run("1/x", 109 | [&] { 110 | x += 1 / x; 111 | }) 112 | .doNotOptimizeAway(x); 113 | 114 | bench.run("noop", [&] {}); 115 | 116 | x = 1.123; 117 | bench 118 | .run("std::sqrt(x)", 119 | [&] { 120 | x += std::sqrt(x); 121 | }) 122 | .doNotOptimizeAway(x); 123 | } 124 | 125 | // NOLINTNEXTLINE 126 | TEST_CASE("unit_api") { 127 | std::string str(200000, 'x'); 128 | 129 | size_t h = 0; 130 | ankerl::nanobench::Bench bench; 131 | bench.batch(str.size()) 132 | .unit("B") 133 | .run("std::hash", 134 | [&]() ANKERL_NANOBENCH_NO_SANITIZE("integer") { 135 | h += std::hash{}(str); 136 | ++str[11]; 137 | }) 138 | .doNotOptimizeAway(h); 139 | } 140 | -------------------------------------------------------------------------------- /src/test/unit_cold.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // Benchmarks insertion and removal in multiple different containers. 7 | // This uses a very fast random generator. 8 | // NOLINTNEXTLINE 9 | TEST_CASE("unit_cold") { 10 | int x = 0; 11 | ankerl::nanobench::Bench().epochs(1).epochIterations(1).run("x and sleep", 12 | [&] { 13 | ++x; 14 | // std::this_thread::sleep_for(std::chrono::milliseconds(10)); 15 | }); 16 | 17 | REQUIRE(x == 1); 18 | } 19 | -------------------------------------------------------------------------------- /src/test/unit_exact_iters_and_epochs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // Benchmarks insertion and removal in multiple different containers. 7 | // This uses a very fast random generator. 8 | // NOLINTNEXTLINE 9 | TEST_CASE("test_exact_iters_and_epochs") { 10 | 11 | ankerl::nanobench::Bench bench; 12 | size_t x = 0; 13 | bench.epochs(7).epochIterations(123).run("++x", [&] { 14 | ++x; 15 | }); 16 | REQUIRE(x == bench.epochs() * bench.epochIterations()); 17 | 18 | int y = 0; 19 | bench.epochs(1).epochIterations(77).run("++y", [&] { 20 | ++y; 21 | }); 22 | REQUIRE(y == bench.epochs() * bench.epochIterations()); 23 | 24 | // one benchmark 25 | REQUIRE(bench.results().size() == 2); 26 | 27 | { 28 | // check first result 29 | auto const& r = bench.results()[0]; 30 | REQUIRE(r.size() == 7U); // each epoch has a result 31 | for (size_t i = 0; i < r.size(); ++i) { 32 | REQUIRE(static_cast(std::lround(r.get( 33 | i, ankerl::nanobench::Result::Measure::iterations))) == 34 | 123); 35 | } 36 | REQUIRE(static_cast(std::lround( 37 | r.sum(ankerl::nanobench::Result::Measure::iterations))) == 38 | 7 * 123); 39 | } 40 | 41 | { 42 | // check second result 43 | auto const& r = bench.results()[1]; 44 | REQUIRE(r.size() == 1U); // each epoch has a result 45 | for (size_t i = 0; i < r.size(); ++i) { 46 | REQUIRE(static_cast(std::lround(r.get( 47 | i, ankerl::nanobench::Result::Measure::iterations))) == 48 | 77); 49 | } 50 | REQUIRE(static_cast(std::lround( 51 | r.sum(ankerl::nanobench::Result::Measure::iterations))) == 52 | 1 * 77); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/unit_romutrio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace { 5 | 6 | ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined") 7 | constexpr uint64_t rotl(uint64_t x, unsigned k) noexcept { 8 | return (x << k) | (x >> (64U - k)); 9 | } 10 | 11 | // Romu generators, by Mark Overton, 2020-2-7. 12 | // 13 | // This code is not copyrighted and comes with no warranty of any kind, so it is 14 | // as-is. You are free to modify and/or distribute it as you wish. You are only 15 | // required to give credit where credit is due by: (1) not renaming a generator 16 | // having an unmodified algorithm and constants; (2) prefixing the name of a 17 | // generator having a modified algorithm or constants with "Romu"; (3) 18 | // attributing the original invention to Mark Overton. 19 | 20 | // Copy and paste the generator you want from those below. 21 | // To compile, you will need to #include 22 | // Website: romu-random.org 23 | 24 | //===== RomuTrio 25 | //================================================================================== 26 | // 27 | // Great for general purpose work, including huge jobs. 28 | // Est. capacity = 2^75 bytes. Register pressure = 6. State size = 192 bits. 29 | 30 | // NOLINTNEXTLINE 31 | uint64_t xState, yState, zState; // set to nonzero seed 32 | 33 | ANKERL_NANOBENCH_NO_SANITIZE("integer") 34 | uint64_t romuTrio_random() { 35 | // NOLINTNEXTLINE 36 | uint64_t xp = xState, yp = yState, zp = zState; 37 | xState = 15241094284759029579U * zp; 38 | yState = yp - xp; 39 | yState = rotl(yState, 12); 40 | zState = zp - yp; 41 | zState = rotl(zState, 44); 42 | return xp; 43 | } 44 | 45 | void romuTrio_seed(uint64_t seed) { 46 | xState = seed; 47 | yState = UINT64_C(0xcc2d6b0743b14800); 48 | zState = UINT64_C(0xd932cff2dd2324a7); 49 | 50 | for (int i = 0; i < 10; ++i) { 51 | romuTrio_random(); 52 | } 53 | } 54 | 55 | } // namespace 56 | 57 | #include 58 | #include 59 | #include 60 | 61 | // NOLINTNEXTLINE 62 | TEST_CASE("unit_romutrio_seed") { 63 | std::ostringstream out; 64 | for (int skip = 0; skip < 10; ++skip) { 65 | for (uint64_t i = 0; i < 10; ++i) { 66 | romuTrio_seed(i); 67 | for (auto s = 0; s < skip; ++s) { 68 | romuTrio_random(); 69 | } 70 | out << std::setw(16) << std::setfill('0') << std::hex 71 | << romuTrio_random() << " "; 72 | } 73 | out << std::endl; 74 | } 75 | std::cout << out.str() << std::endl; 76 | } 77 | 78 | // NOLINTNEXTLINE 79 | TEST_CASE("unit_rng_state") { 80 | ankerl::nanobench::Rng rng{}; 81 | for (size_t i = 0; i < 10; ++i) { 82 | rng(); 83 | } 84 | auto state = rng.state(); 85 | ankerl::nanobench::Rng rngLoaded(state); 86 | 87 | for (size_t i = 0; i < 100; ++i) { 88 | REQUIRE(rng() == rngLoaded()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/unit_templates.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace { 9 | 10 | std::string readFile(std::string const& filename) { 11 | std::ifstream fin{filename}; // NOLINT(misc-const-correctness) 12 | std::ostringstream buf; 13 | buf << fin.rdbuf(); 14 | return buf.str(); 15 | } 16 | 17 | // path where the template files are 18 | std::string tplPath() { 19 | // not using std::filesystem becase we have to be compatible to c++11 :-/ 20 | auto path = std::string{__FILE__}; 21 | auto idx = path.find("unit_templates.cpp"); 22 | return path.substr(0, idx) + "../docs/_generated/"; 23 | } 24 | 25 | } // namespace 26 | 27 | // NOLINTNEXTLINE 28 | TEST_CASE("unit_templates_generate" * doctest::skip()) { 29 | { 30 | std::ofstream fout{tplPath() + "mustache.template.json"}; 31 | fout << ankerl::nanobench::templates::json(); 32 | } 33 | 34 | { 35 | std::ofstream fout{tplPath() + "mustache.template.html"}; 36 | fout << ankerl::nanobench::templates::htmlBoxplot(); 37 | } 38 | 39 | { 40 | std::ofstream fout{tplPath() + "mustache.template.csv"}; 41 | fout << ankerl::nanobench::templates::csv(); 42 | } 43 | 44 | { 45 | std::ofstream fout{tplPath() + "mustache.template.pyperf"}; 46 | fout << ankerl::nanobench::templates::pyperf(); 47 | } 48 | } 49 | 50 | // NOLINTNEXTLINE 51 | TEST_CASE("unit_templates") { 52 | REQUIRE(readFile(tplPath() + "mustache.template.json") == 53 | std::string{ankerl::nanobench::templates::json()}); 54 | 55 | REQUIRE(readFile(tplPath() + "mustache.template.html") == 56 | std::string{ankerl::nanobench::templates::htmlBoxplot()}); 57 | 58 | REQUIRE(readFile(tplPath() + "mustache.template.csv") == 59 | std::string{ankerl::nanobench::templates::csv()}); 60 | 61 | REQUIRE(readFile(tplPath() + "mustache.template.pyperf") == 62 | std::string{ankerl::nanobench::templates::pyperf()}); 63 | } 64 | -------------------------------------------------------------------------------- /src/test/unit_timeunit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // Benchmarks insertion and removal in multiple different containers. 7 | // This uses a very fast random generator. 8 | // NOLINTNEXTLINE 9 | TEST_CASE("unit_timeunits") { 10 | auto bench = ankerl::nanobench::Bench(); 11 | 12 | bench.timeUnit(std::chrono::milliseconds(1), "ms"); 13 | bench.run("sleep 2ms", [] { 14 | std::this_thread::sleep_for(std::chrono::milliseconds(2)); 15 | }); 16 | 17 | bench.run("sleep 1ms", [] { 18 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 19 | }); 20 | 21 | // changing the unit should create a new header 22 | 23 | bench.timeUnit(std::chrono::hours(24 * 14), "fortnight"); 24 | bench.run("sleep 1ms", [] { 25 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 26 | }); 27 | 28 | bench.timeUnit(std::chrono::duration(1), "ps"); 29 | bench.run("sleep 1ms", [] { 30 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/test/unit_to_s.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include // needed in this test, see https://github.com/onqtam/doctest/issues/126#issuecomment-381746547 7 | #include 8 | 9 | namespace { 10 | 11 | std::string to_s(uint64_t n) { 12 | std::string str; 13 | do { 14 | str += static_cast('0' + static_cast(n % 10)); 15 | n /= 10; 16 | } while (n != 0); 17 | std::reverse(str.begin(), str.end()); 18 | return str; 19 | } 20 | 21 | } // namespace 22 | 23 | // NOLINTNEXTLINE 24 | TEST_CASE("to_s") { 25 | REQUIRE(to_s(UINT64_C(123)) == "123"); 26 | REQUIRE(to_s(UINT64_C(0)) == "0"); 27 | REQUIRE(to_s(UINT64_C(10)) == "10"); 28 | REQUIRE(to_s(UINT64_C(123456789)) == "123456789"); 29 | REQUIRE(to_s(UINT64_C(9876543210)) == "9876543210"); 30 | REQUIRE(to_s(UINT64_C(18446744073709551615)) == "18446744073709551615"); 31 | } 32 | -------------------------------------------------------------------------------- /ubsan.supp: -------------------------------------------------------------------------------- 1 | # https://github.com/gcc-mirror/gcc/blob/master/libsanitizer/ubsan/ubsan_checks.inc 2 | implicit-integer-sign-change:random.tcc 3 | signed-integer-overflow:random.tcc 4 | unsigned-integer-overflow:random.tcc 5 | shift-base:random.tcc 6 | shift-exponent:random.tcc 7 | unsigned-integer-overflow:random.h 8 | unsigned-integer-overflow:example_random_number_generators.cpp 9 | --------------------------------------------------------------------------------