├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── benchpress_logo.png ├── cmake ├── EnableStdCXX11.cmake └── FindICU.cmake ├── docs ├── command_line.md ├── getting_started.md ├── macros.md ├── reference.md └── why.md └── src ├── CMakeLists.txt ├── benchpress ├── CMakeLists.txt ├── benchpress.hpp └── cxxopts.hpp └── examples ├── CMakeLists.txt ├── example_01.cpp ├── example_02.cpp ├── example_03.cpp └── example_04.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | *.o 3 | build 4 | 5 | # Hideous eclipse 6 | .autotools 7 | .cproject 8 | .project 9 | .settings/ 10 | 11 | # Vim - YouCompleteMe 12 | .ycm_extra_conf.py 13 | .ycm_extra_conf.pyc 14 | 15 | # CLion 16 | .idea 17 | 18 | bin 19 | lib -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Christopher Gilbert. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | # 22 | # Project Properties 23 | # 24 | CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2) 25 | PROJECT (benchpress) 26 | 27 | # 28 | # Debugging Options 29 | # 30 | SET (CMAKE_VERBOSE_MAKEFILE 0) # Use 1 for debugging, 0 for release 31 | 32 | # 33 | # Project Output Paths 34 | # 35 | SET (MAINFOLDER ${PROJECT_SOURCE_DIR}) 36 | 37 | # 38 | # Project Search Paths 39 | # 40 | LIST (APPEND CMAKE_PREFIX_PATH "${MAINFOLDER}") 41 | SET (CMAKE_MODULE_PATH "${MAINFOLDER}/cmake") 42 | INCLUDE_DIRECTORIES ("${MAINFOLDER}/src") 43 | 44 | # 45 | # Enable C++11 46 | # 47 | INCLUDE (EnableStdCXX11) 48 | ENABLE_STDCXX11 () 49 | 50 | # 51 | # Do not omit frame pointers (this is required for perf to function correctly) 52 | # 53 | SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") 54 | 55 | # 56 | # libicu is required for command line argument parsing 57 | # 58 | FIND_PACKAGE(ICU REQUIRED) 59 | include_directories(${ICU_INCLUDE_DIRS}) 60 | 61 | FIND_PACKAGE (Threads) 62 | 63 | # 64 | # Recurse source sub-directories 65 | # 66 | ADD_SUBDIRECTORY (src) 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Christopher Gilbert 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Benchpress Logo](benchpress_logo.png) 2 | 3 | ## Get your code ripped! 4 | 5 | Benchpress is a C++11 header only micro-benchmark framework. 6 | 7 | ## How to use it 8 | 9 | - [Why Benchpress](docs/why.md) 10 | - [Getting Started](docs/getting_started.md) 11 | - [Reference](docs/reference.md) 12 | 13 | ## Author 14 | 15 | Christopher Gilbert 16 | 17 | * Twitter: [@bigdatadev](https://twitter.com/bigdatadev) 18 | * Linkedin: [/in/christopherjohngilbert](https://www.linkedin.com/in/christopherjohngilbert) 19 | 20 | ## Copyright 21 | 22 | See [LICENSE](LICENSE) document -------------------------------------------------------------------------------- /benchpress_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjgdev/benchpress/56d5687dbfb18ee038d4b59705e31fcab4faea85/benchpress_logo.png -------------------------------------------------------------------------------- /cmake/EnableStdCXX11.cmake: -------------------------------------------------------------------------------- 1 | # ==================================================================== 2 | # Copyright (C) 2015 Christopher Gilbert. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | # ==================================================================== 22 | 23 | include(CheckCXXCompilerFlag) 24 | 25 | macro(ENABLE_STDCXX11) 26 | CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) 27 | CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) 28 | 29 | if(COMPILER_SUPPORTS_CXX11) 30 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 31 | elseif(COMPILER_SUPPORTS_CXX0X) 32 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 33 | else() 34 | message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") 35 | endif() 36 | endmacro() 37 | -------------------------------------------------------------------------------- /cmake/FindICU.cmake: -------------------------------------------------------------------------------- 1 | # This module can find the International Components for Unicode (ICU) Library 2 | # 3 | # Requirements: 4 | # - CMake >= 2.8.3 (for new version of find_package_handle_standard_args) 5 | # 6 | # The following variables will be defined for your use: 7 | # - ICU_FOUND : were all of your specified components found (include dependencies)? 8 | # - ICU_INCLUDE_DIRS : ICU include directory 9 | # - ICU_LIBRARIES : ICU libraries 10 | # - ICU_VERSION : complete version of ICU (x.y.z) 11 | # - ICU_MAJOR_VERSION : major version of ICU 12 | # - ICU_MINOR_VERSION : minor version of ICU 13 | # - ICU_PATCH_VERSION : patch version of ICU 14 | # - ICU__FOUND : were found? (FALSE for non specified component if it is not a dependency) 15 | # 16 | # For windows or non standard installation, define ICU_ROOT variable to point to the root installation of ICU. Two ways: 17 | # - run cmake with -DICU_ROOT= 18 | # - define an environment variable with the same name before running cmake 19 | # With cmake-gui, before pressing "Configure": 20 | # 1) Press "Add Entry" button 21 | # 2) Add a new entry defined as: 22 | # - Name: ICU_ROOT 23 | # - Type: choose PATH in the selection list 24 | # - Press "..." button and select the root installation of ICU 25 | # 26 | # Example Usage: 27 | # 28 | # 1. Copy this file in the root of your project source directory 29 | # 2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt: 30 | # set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) 31 | # 3. Finally call find_package() once, here are some examples to pick from 32 | # 33 | # Require ICU 4.4 or later 34 | # find_package(ICU 4.4 REQUIRED) 35 | # 36 | # if(ICU_FOUND) 37 | # include_directories(${ICU_INCLUDE_DIRS}) 38 | # add_executable(myapp myapp.c) 39 | # target_link_libraries(myapp ${ICU_LIBRARIES}) 40 | # endif(ICU_FOUND) 41 | 42 | #============================================================================= 43 | # Copyright (c) 2011-2013, julp 44 | # 45 | # Distributed under the OSI-approved BSD License 46 | # 47 | # This software is distributed WITHOUT ANY WARRANTY; without even the 48 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 49 | #============================================================================= 50 | 51 | find_package(PkgConfig QUIET) 52 | 53 | ########## Private ########## 54 | if(NOT DEFINED ICU_PUBLIC_VAR_NS) 55 | set(ICU_PUBLIC_VAR_NS "ICU") # Prefix for all ICU relative public variables 56 | endif(NOT DEFINED ICU_PUBLIC_VAR_NS) 57 | if(NOT DEFINED ICU_PRIVATE_VAR_NS) 58 | set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}") # Prefix for all ICU relative internal variables 59 | endif(NOT DEFINED ICU_PRIVATE_VAR_NS) 60 | if(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) 61 | set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables 62 | endif(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) 63 | 64 | function(icudebug _VARNAME) 65 | if(${ICU_PUBLIC_VAR_NS}_DEBUG) 66 | if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) 67 | message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}") 68 | else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) 69 | message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ") 70 | endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) 71 | endif(${ICU_PUBLIC_VAR_NS}_DEBUG) 72 | endfunction(icudebug) 73 | 74 | set(${ICU_PRIVATE_VAR_NS}_ROOT "") 75 | if(DEFINED ENV{ICU_ROOT}) 76 | set(${ICU_PRIVATE_VAR_NS}_ROOT "$ENV{ICU_ROOT}") 77 | endif(DEFINED ENV{ICU_ROOT}) 78 | if (DEFINED ICU_ROOT) 79 | set(${ICU_PRIVATE_VAR_NS}_ROOT "${ICU_ROOT}") 80 | endif(DEFINED ICU_ROOT) 81 | 82 | set(${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES ) 83 | set(${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES ) 84 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 85 | list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin64") 86 | list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib64") 87 | endif(CMAKE_SIZEOF_VOID_P EQUAL 8) 88 | list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin") 89 | list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib") 90 | 91 | set(${ICU_PRIVATE_VAR_NS}_COMPONENTS ) 92 | # ... 93 | macro(icu_declare_component _NAME) 94 | list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME}) 95 | set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN}) 96 | endmacro(icu_declare_component) 97 | 98 | icu_declare_component(data icudata) 99 | icu_declare_component(uc icuuc) # Common and Data libraries 100 | icu_declare_component(i18n icui18n icuin) # Internationalization library 101 | icu_declare_component(io icuio ustdio) # Stream and I/O Library 102 | icu_declare_component(le icule) # Layout library 103 | icu_declare_component(lx iculx) # Paragraph Layout library 104 | 105 | ########## Public ########## 106 | set(${ICU_PUBLIC_VAR_NS}_FOUND TRUE) 107 | set(${ICU_PUBLIC_VAR_NS}_LIBRARIES ) 108 | set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS ) 109 | set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "") 110 | set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "") 111 | set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "") 112 | set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "") 113 | set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "") 114 | set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "") 115 | foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS}) 116 | string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) 117 | set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the icu_declare_component macro 118 | endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) 119 | 120 | # Check components 121 | if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least 122 | set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) 123 | else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) 124 | list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) 125 | list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) 126 | foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) 127 | if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) 128 | message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}") 129 | endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) 130 | endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) 131 | endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) 132 | 133 | # Includes 134 | find_path( 135 | ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS 136 | NAMES unicode/utypes.h utypes.h 137 | HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} 138 | PATH_SUFFIXES "include" 139 | DOC "Include directories for ICU" 140 | ) 141 | 142 | if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) 143 | ########## ########## 144 | if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h") # ICU >= 4 145 | file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) 146 | elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h") # ICU [2;4[ 147 | file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) 148 | elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h") # ICU [1.4;2[ 149 | file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) 150 | elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h") # ICU 1.3 151 | file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) 152 | else() 153 | message(FATAL_ERROR "ICU version header not found") 154 | endif() 155 | 156 | if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3 157 | # [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0) 158 | set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "1") 159 | set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_1}") 160 | set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") 161 | elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*") 162 | # 163 | # Since version 4.9.1, ICU release version numbering was totaly changed, see: 164 | # - http://site.icu-project.org/download/49 165 | # - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU 166 | # 167 | set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") 168 | string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") 169 | string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") 170 | elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[ 171 | # [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.1(?:\.\d)? have releasing error and appears as 1.4.0 172 | set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if 173 | if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$") 174 | set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") 175 | set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") 176 | set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") 177 | elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") 178 | set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") 179 | set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") 180 | set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${CMAKE_MATCH_3}") 181 | endif() 182 | else() 183 | message(FATAL_ERROR "failed to detect ICU version") 184 | endif() 185 | set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_MINOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_PATCH_VERSION}") 186 | ########## ########## 187 | 188 | # Check dependencies (implies pkg-config) 189 | if(PKG_CONFIG_FOUND) 190 | set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) 191 | foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP}) 192 | pkg_check_modules(PC_ICU_PRIVATE_VAR_NS "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET) 193 | 194 | if(${PC_ICU_PRIVATE_VAR_NS}_FOUND) 195 | foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${PC_ICU_LIBRARIES}) 196 | string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY}) 197 | list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY}) 198 | endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY) 199 | endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND) 200 | endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) 201 | list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) 202 | endif(PKG_CONFIG_FOUND) 203 | 204 | # Check libraries 205 | foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) 206 | set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES ) 207 | set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES ) 208 | foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) 209 | list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}") 210 | list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d") 211 | list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}") 212 | list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}d") 213 | endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME) 214 | 215 | find_library( 216 | ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} 217 | NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES} 218 | HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} 219 | PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} 220 | DOC "Release libraries for ICU" 221 | ) 222 | find_library( 223 | ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT} 224 | NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES} 225 | HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} 226 | PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} 227 | DOC "Debug libraries for ICU" 228 | ) 229 | 230 | string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) 231 | if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # both not found 232 | set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) 233 | set("${ICU_PUBLIC_VAR_NS}_FOUND" FALSE) 234 | else(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # one or both found 235 | set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE) 236 | if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # release not found => we are in debug 237 | set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") 238 | elseif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # debug not found => we are in release 239 | set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") 240 | else() # both found 241 | set( 242 | ${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} 243 | optimized ${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} 244 | debug ${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} 245 | ) 246 | endif() 247 | list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) 248 | endif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) 249 | endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) 250 | 251 | # Try to find out compiler flags 252 | find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE icu-config HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}) 253 | if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) 254 | execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 255 | execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 256 | execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 257 | 258 | execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 259 | execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 260 | execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) 261 | endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) 262 | 263 | # Check find_package arguments 264 | include(FindPackageHandleStandardArgs) 265 | if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) 266 | find_package_handle_standard_args( 267 | ${ICU_PUBLIC_VAR_NS} 268 | REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS 269 | VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION 270 | ) 271 | else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) 272 | find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "ICU not found" ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) 273 | endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) 274 | else(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) 275 | if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) 276 | message(FATAL_ERROR "Could not find ICU include directory") 277 | endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) 278 | endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) 279 | 280 | mark_as_advanced( 281 | ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS 282 | ${ICU_PUBLIC_VAR_NS}_LIBRARIES 283 | ) 284 | 285 | # IN (args) 286 | icudebug("FIND_COMPONENTS") 287 | icudebug("FIND_REQUIRED") 288 | icudebug("FIND_QUIETLY") 289 | icudebug("FIND_VERSION") 290 | # OUT 291 | # Found 292 | icudebug("FOUND") 293 | icudebug("UC_FOUND") 294 | icudebug("I18N_FOUND") 295 | icudebug("IO_FOUND") 296 | icudebug("LE_FOUND") 297 | icudebug("LX_FOUND") 298 | icudebug("DATA_FOUND") 299 | # Flags 300 | icudebug("C_FLAGS") 301 | icudebug("CPP_FLAGS") 302 | icudebug("CXX_FLAGS") 303 | icudebug("C_SHARED_FLAGS") 304 | icudebug("CPP_SHARED_FLAGS") 305 | icudebug("CXX_SHARED_FLAGS") 306 | # Linking 307 | icudebug("INCLUDE_DIRS") 308 | icudebug("LIBRARIES") 309 | # Version 310 | icudebug("MAJOR_VERSION") 311 | icudebug("MINOR_VERSION") 312 | icudebug("PATCH_VERSION") 313 | icudebug("VERSION") -------------------------------------------------------------------------------- /docs/command_line.md: -------------------------------------------------------------------------------- 1 | # Command Line Options 2 | 3 | ## -bench _n_ 4 | 5 | Provide a regular expression selecting which benchmarks to run. The default value is `.*`, however any valid regex is 6 | accepted by the runtime. 7 | 8 | ## -benchtime _n_ 9 | 10 | Provide a time value in seconds to choose how long benchmarks should run for. The default value is `1`, however larger 11 | values provide increasingly significant statistical timing data. 12 | 13 | ## -cpu _n_ 14 | 15 | Provide a hint to the runtime to choose the number of threads to use in conjunction with `run_parallel`. The default is 16 | the number of threads reported by the OS. 17 | 18 | ## -list 19 | 20 | Print a newline delimited list of all registered benchmarks and exit. -------------------------------------------------------------------------------- /docs/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Writing benchmarks 4 | 5 | A simple benchmark looks like this: 6 | 7 | ```cpp 8 | BENCHMARK("example", [](benchpress::context* ctx) { 9 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 10 | std::cout << "hello" << std::endl; 11 | } 12 | }) 13 | ``` 14 | 15 | Every benchmark function is passed a parameter `benchpress::context*`. The target code must be executed n 16 | (`benchpress::context::num_iterations()`) times. The value n is adjusted until the benchmark runs long enough to be 17 | timed reliably. 18 | 19 | The example will produce the following output: 20 | 21 | ``` 22 | example 1000000 1165 ns/op 23 | ``` 24 | 25 | The output shows that the code was run 1000000 times, and each cycle lasted 1165 nanoseconds. 26 | 27 | If a benchmark performs some expensive setup before running the timer may be reset: 28 | 29 | ```cpp 30 | BENCHMARK("expensive setup", [](benchpress::context* ctx) { 31 | setup_expensive(); 32 | ctx->reset_timer(); 33 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 34 | test_func(); 35 | } 36 | }) 37 | ``` 38 | 39 | If a benchmark needs to check performance in a parallel setting, it may use the run_parallel helper function, which is 40 | intended to be used with the -cpu command line option. 41 | 42 | ```cpp 43 | BENCHMARK("parallel benchmark", [](benchpress::context* ctx) { 44 | ctx->run_parallel([](benchpress::parallel_context* pctx) { 45 | while (pctx->next()) { 46 | std::this_thread::sleep_for(std::chrono::seconds(1)); 47 | } 48 | } 49 | }) 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/macros.md: -------------------------------------------------------------------------------- 1 | # Macros 2 | 3 | ## BENCHMARK(x, f) 4 | 5 | 6 | 7 | ## DISABLE_REDUNDANT_CODE_OPT() 8 | 9 | -------------------------------------------------------------------------------- /docs/reference.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | ## benchpress::context 4 | 5 | ### size_t num_iterations() 6 | 7 | Get the number of iterations to run the benchmark function. It is important for a benchmark to execute the benchmark 8 | function this many times. 9 | 10 | ```cpp 11 | BENCHMARK("example") { 12 | for (size_t i = 0; i < b->num_iterations(); ++i) { 13 | // run benchmark function ... 14 | } 15 | } 16 | ``` 17 | 18 | ### void set_num_threads(size_t n) 19 | 20 | Set the number of threads to be used with `run_parallel`. 21 | 22 | ### size_t num_threads() 23 | 24 | Get the number of threads spawned by `run_parallel`. 25 | 26 | ### void start_timer() 27 | 28 | Starts timing. If the timer is already running then this function will do nothing. 29 | 30 | ### void stop_timer() 31 | 32 | Stops timing. If the timer is already stopped then this function will do nothing. Can be useful for only timing certain 33 | parts of code. 34 | 35 | ### void reset_timer() 36 | 37 | Resets the timer. Can be useful for excluding expensive setup code. 38 | 39 | ### void set_bytes(int64_t bytes) 40 | 41 | When called, benchpress will collect information about how much data the benchmark function processes per second, and 42 | print additional result data for the benchmark function. 43 | 44 | ### size_t get_ns_per_op() 45 | 46 | Returns the average number of nanoseconds elapsed per cycle of the benchmark function. 47 | 48 | ### void run_parallel(std::function f) 49 | 50 | When called, benchpress will launch `num_threads()` threads and execute the function `f`. 51 | 52 | ## benchpress::parallel_context 53 | 54 | ### bool next() 55 | 56 | Returns true until there is no more work to do, when the function will return false. Thread safe. Example: 57 | 58 | ```cpp 59 | BENCHMARK("multi-threaded example") { 60 | b->run_parallel([](benchpress::parallel_context* pb) { 61 | while (pb->next()) { 62 | std::this_thread::sleep_for(std::chrono::seconds(1)); 63 | } 64 | }); 65 | } 66 | ``` 67 | 68 | ## void benchpress::escape(void *p) 69 | 70 | This function can be used to keep variables on the stack that would normally be optimised away 71 | by the compiler, without introducing any additional instructions or changing the behaviour of 72 | the program. 73 | 74 | ```cpp 75 | std::vector v; 76 | v.reserve(10); 77 | escape(v.data()); 78 | ``` 79 | 80 | ## void benchpress::clobber() 81 | 82 | This function can be used to disable the optimiser. It has the effect of creating a read / write 83 | memory barrier for the compiler, meaning it does not assume that any values read from memory before 84 | the asm remain unchanged after that asm; it reloads them as needed. 85 | 86 | ```cpp 87 | std::vector v; 88 | v.reserve(10); 89 | escape(v.data()); 90 | v.push_back(42); 91 | clobber(); // Ensure the integer pushed is read 92 | ``` -------------------------------------------------------------------------------- /docs/why.md: -------------------------------------------------------------------------------- 1 | # Why benchpress? 2 | 3 | The modern developer is faced with a rich variety of language alternatives to C++; however, C++ is still the choice of professionals facing demanding performance requirements. Despite this fact, there are few (if any) popular performance benchmarking frameworks. 4 | 5 | Benchpress is inspired by Catch and by Golang's benchmark functionality. 6 | 7 | ## Key Features 8 | 9 | - Easy to get started. Just download benchpress.hpp, #include it and you're ready. 10 | - No external dependencies, just a C++11 compatible compiler. 11 | - Benchmarks are named using free-form strings. 12 | - Easily benchmark single-threaded and multi-threaded performance. 13 | - Reliably capture key performance metrics such as time and throughput. -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Christopher Gilbert. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | ADD_SUBDIRECTORY (benchpress) 22 | ADD_SUBDIRECTORY (examples) -------------------------------------------------------------------------------- /src/benchpress/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Christopher Gilbert. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | # 22 | # Locate project sources 23 | # 24 | FILE (GLOB_RECURSE ts_HDRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 25 | 26 | # 27 | # Install target 28 | # 29 | FOREACH (H_FILE ${ts_HDRS}) 30 | SET (H_INSTALL_DIR "benchpress") 31 | STRING (REGEX MATCH "(.*)[/\\]" DIR ${H_FILE}) 32 | INSTALL (FILES ${H_FILE} DESTINATION include/${H_INSTALL_DIR}/${DIR}) 33 | ENDFOREACH (H_FILE ${ts_HDRS}) 34 | -------------------------------------------------------------------------------- /src/benchpress/benchpress.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Christopher Gilbert. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef BENCHPRESS_HPP 23 | #define BENCHPRESS_HPP 24 | 25 | #include // max, min 26 | #include // atomic_intmax_t 27 | #include // high_resolution_timer, duration 28 | #include // function 29 | #include // setw 30 | #include // cout 31 | #include // regex, regex_match 32 | #include // stringstream 33 | #include // string 34 | #include // thread 35 | #include // vector 36 | 37 | namespace benchpress { 38 | 39 | /* 40 | * The options class encapsulates all options for running benchmarks. 41 | * 42 | * When including benchpress, a main function can be emitted which includes a command-line parser for building an 43 | * options object. However from time-to-time it may be necessary for the developer to have to build their own main 44 | * stub and construct the options object manually. 45 | * 46 | * options opts; 47 | * opts 48 | * .bench(".*") 49 | * .benchtime(1) 50 | * .cpu(4); 51 | */ 52 | class options { 53 | std::string d_bench; 54 | size_t d_benchtime; 55 | size_t d_cpu; 56 | public: 57 | options() 58 | : d_bench(".*") 59 | , d_benchtime(1) 60 | , d_cpu(std::thread::hardware_concurrency()) 61 | {} 62 | options& bench(const std::string& bench) { 63 | d_bench = bench; 64 | return *this; 65 | } 66 | options& benchtime(size_t benchtime) { 67 | d_benchtime = benchtime; 68 | return *this; 69 | } 70 | options& cpu(size_t cpu) { 71 | d_cpu = cpu; 72 | return *this; 73 | } 74 | std::string get_bench() const { 75 | return d_bench; 76 | } 77 | size_t get_benchtime() const { 78 | return d_benchtime; 79 | } 80 | size_t get_cpu() const { 81 | return d_cpu; 82 | } 83 | }; 84 | 85 | class context; 86 | 87 | /* 88 | * The benchmark_info class is used to store a function name / pointer pair. 89 | * 90 | * benchmark_info bi("example", [](benchpress::context* b) { 91 | * // benchmark function 92 | * }); 93 | */ 94 | class benchmark_info { 95 | std::string d_name; 96 | std::function d_func; 97 | 98 | public: 99 | benchmark_info(std::string name, std::function func) 100 | : d_name(name) 101 | , d_func(func) 102 | {} 103 | 104 | std::string get_name() const { return d_name; } 105 | std::function get_func() const { return d_func; } 106 | }; 107 | 108 | /* 109 | * The registration class is responsible for providing a single global point of reference for registering 110 | * benchmark functions. 111 | * 112 | * registration::get_ptr()->register_benchmark(info); 113 | */ 114 | class registration { 115 | static registration* d_this; 116 | std::vector d_benchmarks; 117 | 118 | public: 119 | static registration* get_ptr() { 120 | if (nullptr == d_this) { 121 | d_this = new registration(); 122 | } 123 | return d_this; 124 | } 125 | 126 | void register_benchmark(benchmark_info& info) { 127 | d_benchmarks.push_back(info); 128 | } 129 | 130 | std::vector get_benchmarks() { return d_benchmarks; } 131 | }; 132 | 133 | /* 134 | * The auto_register class is a helper used to register benchmarks. 135 | */ 136 | class auto_register { 137 | public: 138 | auto_register(const std::string& name, std::function func) { 139 | benchmark_info info(name, func); 140 | registration::get_ptr()->register_benchmark(info); 141 | } 142 | }; 143 | 144 | #define CONCAT(x, y) x ## y 145 | #define CONCAT2(x, y) CONCAT(x, y) 146 | 147 | // The BENCHMARK macro is a helper for creating benchmark functions and automatically registering them with the 148 | // registration class. 149 | #define BENCHMARK(x, f) benchpress::auto_register CONCAT2(register_, __LINE__)((x), (f)); 150 | 151 | /* 152 | * This function can be used to keep variables on the stack that would normally be optimised away 153 | * by the compiler, without introducing any additional instructions or changing the behaviour of 154 | * the program. 155 | * 156 | * This function uses the Extended Asm syntax of GCC. The volatile keyword indicates that the 157 | * following instructions have some unknowable side-effect, and ensures that the code will neither 158 | * be moved, nor optimised away. 159 | * 160 | * AssemblerTemplate: No operands. 161 | * 162 | * OutputOperands: None. 163 | * 164 | * InputOperands: The "g" is a wildcard constraint which tells the compiler that it may choose what 165 | * to use for p (eg. a register OR a memory reference). 166 | * 167 | * Clobbers: The "memory" clobber tells the compiler that the assembly code performs reads or writes 168 | * to the memory pointed to by one of the input parameters. 169 | * 170 | * Example usage: 171 | * std::vector v; 172 | * v.reserve(10); 173 | * escape(v.data()); 174 | */ 175 | #ifdef _MSC_VER 176 | 177 | #pragma optimize("", off) 178 | 179 | template 180 | void escape(T&& datum) 181 | { 182 | // see here: http://stackoverflow.com/questions/28287064/how-not-to-optimize-away-mechanics-of-a-folly-function 183 | datum = datum; 184 | } 185 | 186 | #pragma optimize("", on) 187 | 188 | #else 189 | 190 | inline void escape(void *p) 191 | { 192 | asm volatile("" : : "g"(p) : "memory"); 193 | } 194 | 195 | #endif 196 | 197 | /* 198 | * This function can be used to disable the optimiser. It has the effect of creating a read / write 199 | * memory barrier for the compiler, meaning it does not assume that any values read from memory before 200 | * the asm remain unchanged after that asm; it reloads them as needed. 201 | * 202 | * Example usage: 203 | * std::vector v; 204 | * v.reserve(10); 205 | * escape(v.data()); 206 | * v.push_back(42); 207 | * clobber(); // Ensure the integer pushed is read 208 | */ 209 | #ifdef _MSC_VER 210 | 211 | inline void clobber() 212 | { 213 | // see here: http://stackoverflow.com/questions/14449141/the-difference-between-asm-asm-volatile-and-clobbering-memory 214 | _ReadWriteBarrier(); 215 | } 216 | 217 | #else 218 | 219 | inline void clobber() 220 | { 221 | asm volatile("" : : : "memory"); 222 | } 223 | 224 | #endif 225 | 226 | /* 227 | * The result class is responsible for producing a printable string representation of a benchmark run. 228 | */ 229 | class result { 230 | size_t d_num_iterations; 231 | std::chrono::nanoseconds d_duration; 232 | size_t d_num_bytes; 233 | 234 | public: 235 | result(size_t num_iterations, std::chrono::nanoseconds duration, size_t num_bytes) 236 | : d_num_iterations(num_iterations) 237 | , d_duration(duration) 238 | , d_num_bytes(num_bytes) 239 | {} 240 | 241 | size_t get_ns_per_op() const { 242 | if (d_num_iterations <= 0) { 243 | return 0; 244 | } 245 | return d_duration.count() / d_num_iterations; 246 | } 247 | 248 | double get_mb_per_s() const { 249 | if (d_num_iterations <= 0 || d_duration.count() <= 0 || d_num_bytes <= 0) { 250 | return 0; 251 | } 252 | return ((double(d_num_bytes) * double(d_num_iterations) / double(1e6)) / 253 | double(std::chrono::duration_cast(d_duration).count())); 254 | } 255 | 256 | std::string to_string() const { 257 | std::stringstream tmp; 258 | tmp << std::setw(12) << std::right << d_num_iterations; 259 | size_t npo = get_ns_per_op(); 260 | tmp << std::setw(12) << std::right << npo << std::setw(0) << " ns/op"; 261 | double mbs = get_mb_per_s(); 262 | if (mbs > 0.0) { 263 | tmp << std::setw(12) << std::right << mbs << std::setw(0) << " MB/s"; 264 | } 265 | return std::string(tmp.str()); 266 | } 267 | }; 268 | 269 | /* 270 | * The parallel_context class is responsible for providing a thread-safe context for parallel benchmark code. 271 | */ 272 | class parallel_context { 273 | std::atomic_intmax_t d_num_iterations; 274 | public: 275 | parallel_context(size_t num_iterations) 276 | : d_num_iterations(num_iterations) 277 | {} 278 | 279 | bool next() { 280 | return (d_num_iterations.fetch_sub(1) > 0); 281 | } 282 | }; 283 | 284 | /* 285 | * The context class is responsible for providing an interface for capturing benchmark metrics to benchmark functions. 286 | */ 287 | class context { 288 | bool d_timer_on; 289 | std::chrono::high_resolution_clock::time_point d_start; 290 | std::chrono::nanoseconds d_duration; 291 | std::chrono::seconds d_benchtime; 292 | size_t d_num_iterations; 293 | size_t d_num_threads; 294 | size_t d_num_bytes; 295 | benchmark_info d_benchmark; 296 | 297 | public: 298 | context(const benchmark_info& info, const options& opts) 299 | : d_timer_on(false) 300 | , d_start() 301 | , d_duration() 302 | , d_benchtime(std::chrono::seconds(opts.get_benchtime())) 303 | , d_num_iterations(1) 304 | , d_num_threads(opts.get_cpu()) 305 | , d_num_bytes(0) 306 | , d_benchmark(info) 307 | {} 308 | 309 | size_t num_iterations() const { return d_num_iterations; } 310 | 311 | void set_num_threads(size_t n) { d_num_threads = n; } 312 | size_t num_threads() const { return d_num_threads; } 313 | 314 | void start_timer() { 315 | if (!d_timer_on) { 316 | d_start = std::chrono::high_resolution_clock::now(); 317 | d_timer_on = true; 318 | } 319 | } 320 | void stop_timer() { 321 | if (d_timer_on) { 322 | d_duration += std::chrono::high_resolution_clock::now() - d_start; 323 | d_timer_on = false; 324 | } 325 | } 326 | void reset_timer() { 327 | if (d_timer_on) { 328 | d_start = std::chrono::high_resolution_clock::now(); 329 | } 330 | d_duration = std::chrono::nanoseconds::zero(); 331 | } 332 | 333 | void set_bytes(int64_t bytes) { d_num_bytes = bytes; } 334 | 335 | size_t get_ns_per_op() { 336 | if (d_num_iterations <= 0) { 337 | return 0; 338 | } 339 | return d_duration.count() / d_num_iterations; 340 | } 341 | 342 | void run_n(size_t n) { 343 | d_num_iterations = n; 344 | reset_timer(); 345 | start_timer(); 346 | d_benchmark.get_func()(this); 347 | stop_timer(); 348 | } 349 | 350 | void run_parallel(std::function f) { 351 | parallel_context pc(d_num_iterations); 352 | std::vector threads; 353 | for (size_t i = 0; i < d_num_threads; ++i) { 354 | threads.push_back(std::thread([&pc,&f]() -> void { 355 | f(&pc); 356 | })); 357 | } 358 | for(auto& thread : threads){ 359 | thread.join(); 360 | } 361 | } 362 | 363 | result run() { 364 | size_t n = 1; 365 | run_n(n); 366 | while (d_duration < d_benchtime && n < 1e9) { 367 | size_t last = n; 368 | if (get_ns_per_op() == 0) { 369 | n = static_cast(1e9); 370 | } else { 371 | n = d_duration.count() / get_ns_per_op(); 372 | } 373 | n = std::max(std::min(n+n/2, 100*last), last+1); 374 | n = round_up(n); 375 | run_n(n); 376 | } 377 | return result(n, d_duration, d_num_bytes); 378 | } 379 | 380 | private: 381 | template 382 | T round_down_10(T n) { 383 | int tens = 0; 384 | while (n > 10) { 385 | n /= 10; 386 | tens++; 387 | } 388 | int result = 1; 389 | for (int i = 0; i < tens; ++i) { 390 | result *= 10; 391 | } 392 | return result; 393 | } 394 | 395 | template 396 | T round_up(T n) { 397 | T base = round_down_10(n); 398 | if (n < (2 * base)) { 399 | return 2 * base; 400 | } 401 | if (n < (5 * base)) { 402 | return 5 * base; 403 | } 404 | return 10 * base; 405 | } 406 | }; 407 | 408 | #ifdef BENCHPRESS_CONFIG_MAIN 409 | /* 410 | * The run_benchmarks function will run the registered benchmarks. 411 | */ 412 | void run_benchmarks(const options& opts) { 413 | std::regex match_r(opts.get_bench()); 414 | auto benchmarks = registration::get_ptr()->get_benchmarks(); 415 | for (auto& info : benchmarks) { 416 | if (std::regex_match(info.get_name(), match_r)) { 417 | context c(info, opts); 418 | auto r = c.run(); 419 | std::cout << std::setw(35) << std::left << info.get_name() << r.to_string() << std::endl; 420 | } 421 | } 422 | } 423 | #endif 424 | 425 | } // namespace benchpress 426 | 427 | /* 428 | * If BENCHPRESS_CONFIG_MAIN is defined when the file is included then a main function will be emitted which provides a 429 | * command-line parser and then executes run_benchmarks. 430 | */ 431 | #ifdef BENCHPRESS_CONFIG_MAIN 432 | #include "cxxopts.hpp" 433 | benchpress::registration* benchpress::registration::d_this; 434 | int main(int argc, char** argv) { 435 | std::chrono::high_resolution_clock::time_point bp_start = std::chrono::high_resolution_clock::now(); 436 | benchpress::options bench_opts; 437 | try { 438 | cxxopts::Options cmd_opts(argv[0], " - command line options"); 439 | cmd_opts.add_options() 440 | ("bench", "run benchmarks matching the regular expression", cxxopts::value() 441 | ->default_value(".*")) 442 | ("benchtime", "run enough iterations of each benchmark to take t seconds", cxxopts::value() 443 | ->default_value("1")) 444 | ("cpu", "specify the number of threads to use for parallel benchmarks", cxxopts::value() 445 | ->default_value(std::to_string(std::thread::hardware_concurrency()))) 446 | ("list", "list all available benchmarks") 447 | ("help", "print help") 448 | ; 449 | cmd_opts.parse(argc, argv); 450 | if (cmd_opts.count("help")) { 451 | std::cout << cmd_opts.help({""}) << std::endl; 452 | exit(0); 453 | } 454 | if (cmd_opts.count("bench")) { 455 | bench_opts.bench(cmd_opts["bench"].as()); 456 | } 457 | if (cmd_opts.count("benchtime")) { 458 | bench_opts.benchtime(cmd_opts["benchtime"].as()); 459 | } 460 | if (cmd_opts.count("cpu")) { 461 | bench_opts.cpu(cmd_opts["cpu"].as()); 462 | } 463 | if (cmd_opts.count("list")) { 464 | auto benchmarks = benchpress::registration::get_ptr()->get_benchmarks(); 465 | for (auto& info : benchmarks) { 466 | std::cout << info.get_name() << std::endl; 467 | } 468 | exit(EXIT_SUCCESS); 469 | } 470 | } catch (const cxxopts::OptionException& e) { 471 | std::cout << "error parsing options: " << e.what() << std::endl; 472 | exit(1); 473 | } 474 | benchpress::run_benchmarks(bench_opts); 475 | float duration = std::chrono::duration_cast( 476 | std::chrono::high_resolution_clock::now() - bp_start 477 | ).count() / 1000.f; 478 | std::cout << argv[0] << " " << duration << "s" << std::endl; 479 | return 0; 480 | } 481 | #endif 482 | 483 | #endif // BENCHPRESS_HPP -------------------------------------------------------------------------------- /src/benchpress/cxxopts.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2014 Jarryd Beck 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef CXX_OPTS_HPP 26 | #define CXX_OPTS_HPP 27 | 28 | #if defined(__GNUC__) 29 | #pragma GCC diagnostic push 30 | #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" 31 | #endif 32 | 33 | #ifdef _MSC_VER 34 | #define BENCH_NOEXCEPT _NOEXCEPT 35 | #define BENCH_CONSTEXPR 36 | #else 37 | #define BENCH_NOEXCEPT noexcept 38 | #define BENCH_CONSTEXPR constexpr 39 | #endif 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | //when we ask cxxopts to use Unicode, help strings are processed using ICU, 51 | //which results in the correct lengths being computed for strings when they 52 | //are formatted for the help output 53 | //it is necessary to make sure that can be found by the 54 | //compiler, and that icu-uc is linked in to the binary. 55 | 56 | #ifdef CXXOPTS_USE_UNICODE 57 | #include 58 | 59 | namespace cxxopts 60 | { 61 | typedef icu::UnicodeString String; 62 | 63 | inline 64 | String 65 | toLocalString(std::string s) 66 | { 67 | return icu::UnicodeString::fromUTF8(s); 68 | } 69 | 70 | class UnicodeStringIterator : public 71 | std::iterator 72 | { 73 | public: 74 | 75 | UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos) 76 | : s(s) 77 | , i(pos) 78 | { 79 | } 80 | 81 | value_type 82 | operator*() const 83 | { 84 | return s->char32At(i); 85 | } 86 | 87 | bool 88 | operator==(const UnicodeStringIterator& rhs) const 89 | { 90 | return s == rhs.s && i == rhs.i; 91 | } 92 | 93 | bool 94 | operator!=(const UnicodeStringIterator& rhs) const 95 | { 96 | return !(*this == rhs); 97 | } 98 | 99 | UnicodeStringIterator& 100 | operator++() 101 | { 102 | ++i; 103 | return *this; 104 | } 105 | 106 | UnicodeStringIterator 107 | operator+(int32_t v) 108 | { 109 | return UnicodeStringIterator(s, i + v); 110 | } 111 | 112 | private: 113 | const icu::UnicodeString* s; 114 | int32_t i; 115 | }; 116 | 117 | inline 118 | String& 119 | stringAppend(String&s, String a) 120 | { 121 | return s.append(std::move(a)); 122 | } 123 | 124 | inline 125 | String& 126 | stringAppend(String& s, int n, UChar32 c) 127 | { 128 | for (int i = 0; i != n; ++i) 129 | { 130 | s.append(c); 131 | } 132 | 133 | return s; 134 | } 135 | 136 | template 137 | String& 138 | stringAppend(String& s, Iterator begin, Iterator end) 139 | { 140 | while (begin != end) 141 | { 142 | s.append(*begin); 143 | ++begin; 144 | } 145 | 146 | return s; 147 | } 148 | 149 | inline 150 | size_t 151 | stringLength(const String& s) 152 | { 153 | return s.length(); 154 | } 155 | 156 | inline 157 | std::string 158 | toUTF8String(const String& s) 159 | { 160 | std::string result; 161 | s.toUTF8String(result); 162 | 163 | return result; 164 | } 165 | } 166 | 167 | namespace std 168 | { 169 | cxxopts::UnicodeStringIterator 170 | begin(const icu::UnicodeString& s) 171 | { 172 | return cxxopts::UnicodeStringIterator(&s, 0); 173 | } 174 | 175 | cxxopts::UnicodeStringIterator 176 | end(const icu::UnicodeString& s) 177 | { 178 | return cxxopts::UnicodeStringIterator(&s, s.length()); 179 | } 180 | } 181 | 182 | //ifdef CXXOPTS_USE_UNICODE 183 | #else 184 | 185 | namespace cxxopts 186 | { 187 | typedef std::string String; 188 | 189 | template 190 | T 191 | toLocalString(T&& t) 192 | { 193 | return t; 194 | } 195 | 196 | inline 197 | size_t 198 | stringLength(const String& s) 199 | { 200 | return s.length(); 201 | } 202 | 203 | inline 204 | String& 205 | stringAppend(String&s, String a) 206 | { 207 | return s.append(std::move(a)); 208 | } 209 | 210 | inline 211 | String& 212 | stringAppend(String& s, size_t n, char c) 213 | { 214 | return s.append(n, c); 215 | } 216 | 217 | template 218 | String& 219 | stringAppend(String& s, Iterator begin, Iterator end) 220 | { 221 | return s.append(begin, end); 222 | } 223 | 224 | template 225 | std::string 226 | toUTF8String(T&& t) 227 | { 228 | return std::forward(t); 229 | } 230 | 231 | } 232 | 233 | //ifdef CXXOPTS_USE_UNICODE 234 | #endif 235 | 236 | namespace cxxopts 237 | { 238 | class Value : public std::enable_shared_from_this 239 | { 240 | public: 241 | 242 | virtual void 243 | parse(const std::string& text) const = 0; 244 | 245 | virtual void 246 | parse() const = 0; 247 | 248 | virtual bool 249 | has_arg() const = 0; 250 | 251 | virtual bool 252 | has_default() const = 0; 253 | 254 | virtual bool 255 | has_implicit() const = 0; 256 | 257 | virtual std::string 258 | get_default_value() const = 0; 259 | 260 | virtual std::string 261 | get_implicit_value() const = 0; 262 | 263 | virtual std::shared_ptr 264 | default_value(const std::string& value) = 0; 265 | 266 | virtual std::shared_ptr 267 | implicit_value(const std::string& value) = 0; 268 | }; 269 | 270 | class OptionException : public std::exception 271 | { 272 | public: 273 | OptionException(const std::string& message) 274 | : m_message(message) 275 | { 276 | } 277 | 278 | virtual const char* 279 | what() const BENCH_NOEXCEPT 280 | { 281 | return m_message.c_str(); 282 | } 283 | 284 | private: 285 | std::string m_message; 286 | }; 287 | 288 | class OptionSpecException : public OptionException 289 | { 290 | public: 291 | 292 | OptionSpecException(const std::string& message) 293 | : OptionException(message) 294 | { 295 | } 296 | }; 297 | 298 | class OptionParseException : public OptionException 299 | { 300 | public: 301 | OptionParseException(const std::string& message) 302 | : OptionException(message) 303 | { 304 | } 305 | }; 306 | 307 | class option_exists_error : public OptionSpecException 308 | { 309 | public: 310 | option_exists_error(const std::string& option) 311 | : OptionSpecException("Option ‘" + option + "’ already exists") 312 | { 313 | } 314 | }; 315 | 316 | class invalid_option_format_error : public OptionSpecException 317 | { 318 | public: 319 | invalid_option_format_error(const std::string& format) 320 | : OptionSpecException("Invalid option format ‘" + format + "’") 321 | { 322 | } 323 | }; 324 | 325 | class option_not_exists_exception : public OptionParseException 326 | { 327 | public: 328 | option_not_exists_exception(const std::string& option) 329 | : OptionParseException("Option ‘" + option + "’ does not exist") 330 | { 331 | } 332 | }; 333 | 334 | class missing_argument_exception : public OptionParseException 335 | { 336 | public: 337 | missing_argument_exception(const std::string& option) 338 | : OptionParseException("Option ‘" + option + "’ is missing an argument") 339 | { 340 | } 341 | }; 342 | 343 | class option_requires_argument_exception : public OptionParseException 344 | { 345 | public: 346 | option_requires_argument_exception(const std::string& option) 347 | : OptionParseException("Option ‘" + option + "’ requires an argument") 348 | { 349 | } 350 | }; 351 | 352 | class option_not_has_argument_exception : public OptionParseException 353 | { 354 | public: 355 | option_not_has_argument_exception 356 | ( 357 | const std::string& option, 358 | const std::string& arg 359 | ) 360 | : OptionParseException( 361 | "Option ‘" + option + "’ does not take an argument, but argument‘" 362 | + arg + "’ given") 363 | { 364 | } 365 | }; 366 | 367 | class option_not_present_exception : public OptionParseException 368 | { 369 | public: 370 | option_not_present_exception(const std::string& option) 371 | : OptionParseException("Option ‘" + option + "’ not present") 372 | { 373 | } 374 | }; 375 | 376 | class argument_incorrect_type : public OptionParseException 377 | { 378 | public: 379 | argument_incorrect_type 380 | ( 381 | const std::string& arg 382 | ) 383 | : OptionParseException( 384 | "Argument ‘" + arg + "’ failed to parse" 385 | ) 386 | { 387 | } 388 | }; 389 | 390 | namespace values 391 | { 392 | template 393 | void 394 | parse_value(const std::string& text, T& value) 395 | { 396 | std::istringstream is(text); 397 | if (!(is >> value)) 398 | { 399 | std::cerr << "cannot parse empty value" << std::endl; 400 | throw argument_incorrect_type(text); 401 | } 402 | 403 | if (is.rdbuf()->in_avail() != 0) 404 | { 405 | throw argument_incorrect_type(text); 406 | } 407 | } 408 | 409 | template 410 | void 411 | parse_value(const std::string& text, std::vector& value) 412 | { 413 | T v; 414 | parse_value(text, v); 415 | value.push_back(v); 416 | } 417 | 418 | inline 419 | void 420 | parse_value(const std::string& /*text*/, bool& value) 421 | { 422 | //TODO recognise on, off, yes, no, enable, disable 423 | //so that we can write --long=yes explicitly 424 | value = true; 425 | } 426 | 427 | inline 428 | void 429 | parse_value(const std::string& text, std::string& value) 430 | { 431 | value = text; 432 | } 433 | 434 | template 435 | struct value_has_arg : public std::true_type 436 | {}; 437 | 438 | template <> 439 | struct value_has_arg : public std::false_type 440 | {}; 441 | 442 | template 443 | class standard_value : public Value 444 | { 445 | public: 446 | standard_value() 447 | : m_result(std::make_shared()) 448 | , m_store(m_result.get()) 449 | { 450 | } 451 | 452 | standard_value(T* t) 453 | : m_store(t) 454 | { 455 | } 456 | 457 | void 458 | parse(const std::string& text) const 459 | { 460 | if (m_implicit && text.empty()) 461 | { 462 | parse_value(m_implicit_value, *m_store); 463 | } 464 | else 465 | { 466 | parse_value(text, *m_store); 467 | } 468 | } 469 | 470 | void 471 | parse() const 472 | { 473 | parse_value(m_default_value, *m_store); 474 | } 475 | 476 | bool 477 | has_arg() const 478 | { 479 | return value_has_arg::value; 480 | } 481 | 482 | bool 483 | has_default() const 484 | { 485 | return m_default; 486 | } 487 | 488 | bool 489 | has_implicit() const 490 | { 491 | return m_implicit; 492 | } 493 | 494 | virtual std::shared_ptr 495 | default_value(const std::string& value){ 496 | m_default = true; 497 | m_default_value = value; 498 | return shared_from_this(); 499 | } 500 | 501 | virtual std::shared_ptr 502 | implicit_value(const std::string& value){ 503 | m_implicit = true; 504 | m_implicit_value = value; 505 | return shared_from_this(); 506 | } 507 | 508 | std::string 509 | get_default_value() const 510 | { 511 | return m_default_value; 512 | } 513 | 514 | std::string 515 | get_implicit_value() const 516 | { 517 | return m_implicit_value; 518 | } 519 | 520 | const T& 521 | get() const 522 | { 523 | if (m_store == nullptr) 524 | { 525 | return *m_result; 526 | } 527 | else 528 | { 529 | return *m_store; 530 | } 531 | } 532 | 533 | protected: 534 | std::shared_ptr m_result; 535 | T* m_store; 536 | bool m_default = false; 537 | std::string m_default_value; 538 | bool m_implicit = false; 539 | std::string m_implicit_value; 540 | }; 541 | } 542 | 543 | template 544 | std::shared_ptr 545 | value() 546 | { 547 | return std::make_shared>(); 548 | } 549 | 550 | template 551 | std::shared_ptr 552 | value(T& t) 553 | { 554 | return std::make_shared>(&t); 555 | } 556 | 557 | class OptionAdder; 558 | 559 | class OptionDetails 560 | { 561 | public: 562 | OptionDetails 563 | ( 564 | const String& description, 565 | std::shared_ptr value 566 | ) 567 | : m_desc(description) 568 | , m_value(value) 569 | , m_count(0) 570 | { 571 | } 572 | 573 | const String& 574 | description() const 575 | { 576 | return m_desc; 577 | } 578 | 579 | bool 580 | has_arg() const 581 | { 582 | return m_value->has_arg(); 583 | } 584 | 585 | void 586 | parse(const std::string& text) 587 | { 588 | m_value->parse(text); 589 | ++m_count; 590 | } 591 | 592 | void 593 | parse_default() 594 | { 595 | m_value->parse(); 596 | ++m_count; 597 | } 598 | 599 | int 600 | count() const 601 | { 602 | return m_count; 603 | } 604 | 605 | const Value& value() const { 606 | return *m_value; 607 | } 608 | 609 | template 610 | const T& 611 | as() const 612 | { 613 | #ifdef CXXOPTS_NO_RTTI 614 | return static_cast&>(*m_value).get(); 615 | #else 616 | return dynamic_cast&>(*m_value).get(); 617 | #endif 618 | } 619 | 620 | private: 621 | String m_desc; 622 | std::shared_ptr m_value; 623 | int m_count; 624 | }; 625 | 626 | struct HelpOptionDetails 627 | { 628 | std::string s; 629 | std::string l; 630 | String desc; 631 | bool has_arg; 632 | bool has_default; 633 | std::string default_value; 634 | bool has_implicit; 635 | std::string implicit_value; 636 | std::string arg_help; 637 | }; 638 | 639 | struct HelpGroupDetails 640 | { 641 | std::string name; 642 | std::string description; 643 | std::vector options; 644 | }; 645 | 646 | class Options 647 | { 648 | public: 649 | 650 | Options(std::string program, std::string help_string = "") 651 | : m_program(std::move(program)) 652 | , m_help_string(toLocalString(std::move(help_string))) 653 | { 654 | } 655 | 656 | inline 657 | void 658 | parse(int& argc, char**& argv); 659 | 660 | inline 661 | OptionAdder 662 | add_options(std::string group = ""); 663 | 664 | inline 665 | void 666 | add_option 667 | ( 668 | const std::string& group, 669 | const std::string& s, 670 | const std::string& l, 671 | std::string desc, 672 | std::shared_ptr value, 673 | std::string arg_help 674 | ); 675 | 676 | int 677 | count(const std::string& o) const 678 | { 679 | auto iter = m_options.find(o); 680 | if (iter == m_options.end()) 681 | { 682 | return 0; 683 | } 684 | 685 | return iter->second->count(); 686 | } 687 | 688 | const OptionDetails& 689 | operator[](const std::string& option) const 690 | { 691 | auto iter = m_options.find(option); 692 | 693 | if (iter == m_options.end()) 694 | { 695 | throw option_not_present_exception(option); 696 | } 697 | 698 | return *iter->second; 699 | } 700 | 701 | //parse positional arguments into the given option 702 | inline 703 | void 704 | parse_positional(std::string option); 705 | 706 | inline 707 | std::string 708 | help(const std::vector& groups = {""}) const; 709 | 710 | inline 711 | const std::vector 712 | groups() const; 713 | 714 | inline 715 | const HelpGroupDetails& 716 | group_help(const std::string& group) const; 717 | 718 | private: 719 | 720 | inline 721 | void 722 | add_one_option 723 | ( 724 | const std::string& option, 725 | std::shared_ptr details 726 | ); 727 | 728 | inline 729 | bool 730 | consume_positional(std::string a); 731 | 732 | inline 733 | void 734 | add_to_option(const std::string& option, const std::string& arg); 735 | 736 | inline 737 | void 738 | parse_option 739 | ( 740 | std::shared_ptr value, 741 | const std::string& name, 742 | const std::string& arg = "" 743 | ); 744 | 745 | inline 746 | void 747 | checked_parse_arg 748 | ( 749 | int argc, 750 | char* argv[], 751 | int& current, 752 | std::shared_ptr value, 753 | const std::string& name 754 | ); 755 | 756 | inline 757 | String 758 | help_one_group(const std::string& group) const; 759 | 760 | std::string m_program; 761 | String m_help_string; 762 | 763 | std::map> m_options; 764 | std::string m_positional; 765 | 766 | //mapping from groups to help options 767 | std::map m_help; 768 | }; 769 | 770 | class OptionAdder 771 | { 772 | public: 773 | 774 | OptionAdder(Options& options, std::string group) 775 | : m_options(options), m_group(std::move(group)) 776 | { 777 | } 778 | 779 | inline 780 | OptionAdder& 781 | operator() 782 | ( 783 | const std::string& opts, 784 | const std::string& desc, 785 | std::shared_ptr value 786 | = ::cxxopts::value(), 787 | std::string arg_help = "" 788 | ); 789 | 790 | private: 791 | Options& m_options; 792 | std::string m_group; 793 | }; 794 | 795 | } 796 | 797 | namespace cxxopts 798 | { 799 | 800 | namespace 801 | { 802 | 803 | BENCH_CONSTEXPR int OPTION_LONGEST = 30; 804 | BENCH_CONSTEXPR int OPTION_DESC_GAP = 2; 805 | 806 | std::basic_regex option_matcher 807 | ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)"); 808 | 809 | std::basic_regex option_specifier 810 | ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)"); 811 | 812 | String 813 | format_option 814 | ( 815 | const HelpOptionDetails& o 816 | ) 817 | { 818 | auto& s = o.s; 819 | auto& l = o.l; 820 | 821 | String result = " "; 822 | 823 | if (s.size() > 0) 824 | { 825 | result += "-" + toLocalString(s) + ","; 826 | } 827 | else 828 | { 829 | result += " "; 830 | } 831 | 832 | if (l.size() > 0) 833 | { 834 | result += " --" + toLocalString(l); 835 | } 836 | 837 | if (o.has_arg) 838 | { 839 | auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; 840 | 841 | if (o.has_implicit) 842 | { 843 | result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; 844 | } 845 | else 846 | { 847 | result += " " + arg; 848 | } 849 | } 850 | 851 | return result; 852 | } 853 | 854 | String 855 | format_description 856 | ( 857 | const HelpOptionDetails& o, 858 | size_t start, 859 | size_t width 860 | ) 861 | { 862 | auto desc = o.desc; 863 | 864 | if (o.has_default) 865 | { 866 | desc += toLocalString(" (default:" + o.default_value + ")"); 867 | } 868 | 869 | String result; 870 | 871 | auto current = std::begin(desc); 872 | auto startLine = current; 873 | auto lastSpace = current; 874 | 875 | auto size = size_t{}; 876 | 877 | while (current != std::end(desc)) 878 | { 879 | if (*current == ' ') 880 | { 881 | lastSpace = current; 882 | } 883 | 884 | if (size > width) 885 | { 886 | if (lastSpace == startLine) 887 | { 888 | stringAppend(result, startLine, current + 1); 889 | stringAppend(result, "\n"); 890 | stringAppend(result, start, ' '); 891 | startLine = current + 1; 892 | lastSpace = startLine; 893 | } 894 | else 895 | { 896 | stringAppend(result, startLine, lastSpace); 897 | stringAppend(result, "\n"); 898 | stringAppend(result, start, ' '); 899 | startLine = lastSpace + 1; 900 | } 901 | size = 0; 902 | } 903 | else 904 | { 905 | ++size; 906 | } 907 | 908 | ++current; 909 | } 910 | 911 | //append whatever is left 912 | stringAppend(result, startLine, current); 913 | 914 | return result; 915 | } 916 | } 917 | 918 | OptionAdder 919 | Options::add_options(std::string group) 920 | { 921 | return OptionAdder(*this, std::move(group)); 922 | } 923 | 924 | OptionAdder& 925 | OptionAdder::operator() 926 | ( 927 | const std::string& opts, 928 | const std::string& desc, 929 | std::shared_ptr value, 930 | std::string arg_help 931 | ) 932 | { 933 | std::match_results result; 934 | std::regex_match(opts.c_str(), result, option_specifier); 935 | 936 | if (result.empty()) 937 | { 938 | throw invalid_option_format_error(opts); 939 | } 940 | 941 | const auto& s = result[2]; 942 | const auto& l = result[3]; 943 | 944 | m_options.add_option(m_group, s.str(), l.str(), desc, value, 945 | std::move(arg_help)); 946 | 947 | return *this; 948 | } 949 | 950 | void 951 | Options::parse_option 952 | ( 953 | std::shared_ptr value, 954 | const std::string& /*name*/, 955 | const std::string& arg 956 | ) 957 | { 958 | value->parse(arg); 959 | } 960 | 961 | void 962 | Options::checked_parse_arg 963 | ( 964 | int argc, 965 | char* argv[], 966 | int& current, 967 | std::shared_ptr value, 968 | const std::string& name 969 | ) 970 | { 971 | if (current + 1 >= argc) 972 | { 973 | if (value->value().has_implicit()) 974 | { 975 | parse_option(value, name, ""); 976 | } 977 | else 978 | { 979 | throw missing_argument_exception(name); 980 | } 981 | } 982 | else 983 | { 984 | if (argv[current + 1][0] == '-' && value->value().has_implicit()) 985 | { 986 | parse_option(value, name, ""); 987 | } 988 | else 989 | { 990 | parse_option(value, name, argv[current + 1]); 991 | ++current; 992 | } 993 | } 994 | } 995 | 996 | void 997 | Options::add_to_option(const std::string& option, const std::string& arg) 998 | { 999 | auto iter = m_options.find(option); 1000 | 1001 | if (iter == m_options.end()) 1002 | { 1003 | throw option_not_exists_exception(option); 1004 | } 1005 | 1006 | parse_option(iter->second, option, arg); 1007 | } 1008 | 1009 | bool 1010 | Options::consume_positional(std::string a) 1011 | { 1012 | if (m_positional.size() > 0) 1013 | { 1014 | add_to_option(m_positional, a); 1015 | return true; 1016 | } 1017 | else 1018 | { 1019 | return false; 1020 | } 1021 | } 1022 | 1023 | void 1024 | Options::parse_positional(std::string option) 1025 | { 1026 | m_positional = std::move(option); 1027 | } 1028 | 1029 | void 1030 | Options::parse(int& argc, char**& argv) 1031 | { 1032 | int current = 1; 1033 | 1034 | int nextKeep = 1; 1035 | 1036 | while (current != argc) 1037 | { 1038 | std::match_results result; 1039 | std::regex_match(argv[current], result, option_matcher); 1040 | 1041 | if (result.empty()) 1042 | { 1043 | //not a flag 1044 | 1045 | //if true is returned here then it was consumed, otherwise it is 1046 | //ignored 1047 | if (consume_positional(argv[current])) 1048 | { 1049 | } 1050 | else 1051 | { 1052 | argv[nextKeep] = argv[current]; 1053 | ++nextKeep; 1054 | } 1055 | //if we return from here then it was parsed successfully, so continue 1056 | } 1057 | else 1058 | { 1059 | //short or long option? 1060 | if (result[4].length() != 0) 1061 | { 1062 | const std::string& s = result[4]; 1063 | 1064 | for (std::size_t i = 0; i != s.size(); ++i) 1065 | { 1066 | std::string name(1, s[i]); 1067 | auto iter = m_options.find(name); 1068 | 1069 | if (iter == m_options.end()) 1070 | { 1071 | throw option_not_exists_exception(name); 1072 | } 1073 | 1074 | auto value = iter->second; 1075 | 1076 | //if no argument then just add it 1077 | if (!value->has_arg()) 1078 | { 1079 | parse_option(value, name); 1080 | } 1081 | else 1082 | { 1083 | //it must be the last argument 1084 | if (i + 1 == s.size()) 1085 | { 1086 | checked_parse_arg(argc, argv, current, value, name); 1087 | } 1088 | else if (value->value().has_implicit()) 1089 | { 1090 | parse_option(value, name, ""); 1091 | } 1092 | else 1093 | { 1094 | //error 1095 | throw option_requires_argument_exception(name); 1096 | } 1097 | } 1098 | } 1099 | } 1100 | else if (result[1].length() != 0) 1101 | { 1102 | const std::string& name = result[1]; 1103 | 1104 | auto iter = m_options.find(name); 1105 | 1106 | if (iter == m_options.end()) 1107 | { 1108 | throw option_not_exists_exception(name); 1109 | } 1110 | 1111 | auto opt = iter->second; 1112 | 1113 | //equals provided for long option? 1114 | if (result[3].length() != 0) 1115 | { 1116 | //parse the option given 1117 | 1118 | //but if it doesn't take an argument, this is an error 1119 | if (!opt->has_arg()) 1120 | { 1121 | throw option_not_has_argument_exception(name, result[3]); 1122 | } 1123 | 1124 | parse_option(opt, name, result[3]); 1125 | } 1126 | else 1127 | { 1128 | if (opt->has_arg()) 1129 | { 1130 | //parse the next argument 1131 | checked_parse_arg(argc, argv, current, opt, name); 1132 | } 1133 | else 1134 | { 1135 | //parse with empty argument 1136 | parse_option(opt, name); 1137 | } 1138 | } 1139 | } 1140 | 1141 | } 1142 | 1143 | ++current; 1144 | } 1145 | 1146 | for (auto& opt : m_options) 1147 | { 1148 | auto& detail = opt.second; 1149 | auto& value = detail->value(); 1150 | 1151 | if(!detail->count() && value.has_default()){ 1152 | detail->parse_default(); 1153 | } 1154 | } 1155 | 1156 | argc = nextKeep; 1157 | } 1158 | 1159 | void 1160 | Options::add_option 1161 | ( 1162 | const std::string& group, 1163 | const std::string& s, 1164 | const std::string& l, 1165 | std::string desc, 1166 | std::shared_ptr value, 1167 | std::string arg_help 1168 | ) 1169 | { 1170 | auto stringDesc = toLocalString(std::move(desc)); 1171 | auto option = std::make_shared(stringDesc, value); 1172 | 1173 | if (s.size() > 0) 1174 | { 1175 | add_one_option(s, option); 1176 | } 1177 | 1178 | if (l.size() > 0) 1179 | { 1180 | add_one_option(l, option); 1181 | } 1182 | 1183 | //add the help details 1184 | auto& options = m_help[group]; 1185 | 1186 | options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, 1187 | value->has_arg(), 1188 | value->has_default(), value->get_default_value(), 1189 | value->has_implicit(), value->get_implicit_value(), 1190 | std::move(arg_help)}); 1191 | } 1192 | 1193 | void 1194 | Options::add_one_option 1195 | ( 1196 | const std::string& option, 1197 | std::shared_ptr details 1198 | ) 1199 | { 1200 | auto in = m_options.emplace(option, details); 1201 | 1202 | if (!in.second) 1203 | { 1204 | throw option_exists_error(option); 1205 | } 1206 | } 1207 | 1208 | String 1209 | Options::help_one_group(const std::string& g) const 1210 | { 1211 | typedef std::vector> OptionHelp; 1212 | 1213 | auto group = m_help.find(g); 1214 | if (group == m_help.end()) 1215 | { 1216 | return ""; 1217 | } 1218 | 1219 | OptionHelp format; 1220 | 1221 | size_t longest = 0; 1222 | 1223 | String result; 1224 | 1225 | if (!g.empty()) 1226 | { 1227 | result += toLocalString(" " + g + " options:\n\n"); 1228 | } 1229 | 1230 | for (const auto& o : group->second.options) 1231 | { 1232 | auto s = format_option(o); 1233 | longest = std::max(longest, stringLength(s)); 1234 | format.push_back(std::make_pair(s, String())); 1235 | } 1236 | 1237 | longest = std::min(longest, static_cast(OPTION_LONGEST)); 1238 | 1239 | //widest allowed description 1240 | auto allowed = size_t{76} - longest - OPTION_DESC_GAP; 1241 | 1242 | auto fiter = format.begin(); 1243 | for (const auto& o : group->second.options) 1244 | { 1245 | auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); 1246 | 1247 | result += fiter->first; 1248 | if (stringLength(fiter->first) > longest) 1249 | { 1250 | result += "\n"; 1251 | result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); 1252 | } 1253 | else 1254 | { 1255 | result += toLocalString(std::string(longest + OPTION_DESC_GAP - 1256 | stringLength(fiter->first), 1257 | ' ')); 1258 | } 1259 | result += d; 1260 | result += "\n"; 1261 | 1262 | ++fiter; 1263 | } 1264 | 1265 | return result; 1266 | } 1267 | 1268 | std::string 1269 | Options::help(const std::vector& groups) const 1270 | { 1271 | String result = "Usage:\n " + toLocalString(m_program) + " [OPTION...]" 1272 | + m_help_string + "\n\n"; 1273 | 1274 | for (std::size_t i = 0; i < groups.size(); ++i) 1275 | { 1276 | result += help_one_group(groups[i]); 1277 | if (i < groups.size() - 1) 1278 | { 1279 | result += "\n"; 1280 | } 1281 | } 1282 | 1283 | return toUTF8String(result); 1284 | } 1285 | 1286 | const std::vector 1287 | Options::groups() const 1288 | { 1289 | std::vector g; 1290 | 1291 | std::transform( 1292 | m_help.begin(), 1293 | m_help.end(), 1294 | std::back_inserter(g), 1295 | [] (const std::map::value_type& pair) 1296 | { 1297 | return pair.first; 1298 | } 1299 | ); 1300 | 1301 | return g; 1302 | } 1303 | 1304 | const HelpGroupDetails& 1305 | Options::group_help(const std::string& group) const 1306 | { 1307 | return m_help.at(group); 1308 | } 1309 | 1310 | } 1311 | 1312 | #if defined(__GNU__) 1313 | #pragma GCC diagnostic pop 1314 | #endif 1315 | 1316 | #endif //CXX_OPTS_HPP -------------------------------------------------------------------------------- /src/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Christopher Gilbert. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | # 22 | # Locate example sources 23 | # 24 | FILE (GLOB_RECURSE example_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 25 | 26 | # 27 | # Example build rules 28 | # 29 | IF (NOT CMAKE_CROSSCOMPILING) 30 | FOREACH (T_FILE ${example_SRCS}) 31 | GET_FILENAME_COMPONENT (T_NAME ${T_FILE} NAME_WE) 32 | ADD_EXECUTABLE (${T_NAME} ${T_FILE}) 33 | TARGET_LINK_LIBRARIES(${T_NAME} ${ICU_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 34 | ENDFOREACH (T_FILE ${example_SRCS}) 35 | ENDIF (NOT CMAKE_CROSSCOMPILING) -------------------------------------------------------------------------------- /src/examples/example_01.cpp: -------------------------------------------------------------------------------- 1 | #define BENCHPRESS_CONFIG_MAIN 2 | #include "benchpress/benchpress.hpp" 3 | 4 | #include 5 | #include 6 | 7 | BENCHMARK("example", [](benchpress::context* ctx) { 8 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 9 | std::cout << "hello" << std::endl; 10 | std::this_thread::sleep_for(std::chrono::seconds(1)); 11 | } 12 | }) -------------------------------------------------------------------------------- /src/examples/example_02.cpp: -------------------------------------------------------------------------------- 1 | #define BENCHPRESS_CONFIG_MAIN 2 | #include "benchpress/benchpress.hpp" 3 | 4 | unsigned int fib(unsigned int n) { 5 | return n < 2 ? n : fib(n-1) + fib(n-2); 6 | } 7 | 8 | BENCHMARK("fib 0", [](benchpress::context* ctx) { 9 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 10 | auto f = fib(0); 11 | benchpress::escape(&f); 12 | } 13 | }) 14 | 15 | BENCHMARK("fib 5", [](benchpress::context* ctx) { 16 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 17 | auto f = fib(5); 18 | benchpress::escape(&f); 19 | } 20 | }) 21 | 22 | BENCHMARK("fib 10", [](benchpress::context* ctx) { 23 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 24 | auto f = fib(10); 25 | benchpress::escape(&f); 26 | } 27 | }) 28 | 29 | BENCHMARK("fib 15", [](benchpress::context* ctx) { 30 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 31 | auto f = fib(15); 32 | benchpress::escape(&f); 33 | } 34 | }) 35 | 36 | BENCHMARK("fib 20", [](benchpress::context* ctx) { 37 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 38 | auto f = fib(20); 39 | benchpress::escape(&f); 40 | } 41 | }) -------------------------------------------------------------------------------- /src/examples/example_03.cpp: -------------------------------------------------------------------------------- 1 | #define BENCHPRESS_CONFIG_MAIN 2 | #include "benchpress/benchpress.hpp" 3 | 4 | #include 5 | 6 | BENCHMARK("multi-threaded example", [](benchpress::context* ctx) { 7 | ctx->run_parallel([](benchpress::parallel_context* pctx) { 8 | while (pctx->next()) { 9 | std::this_thread::sleep_for(std::chrono::seconds(1)); 10 | } 11 | }); 12 | }) -------------------------------------------------------------------------------- /src/examples/example_04.cpp: -------------------------------------------------------------------------------- 1 | #define BENCHPRESS_CONFIG_MAIN 2 | #include "benchpress/benchpress.hpp" 3 | 4 | #include 5 | 6 | BENCHMARK("example bytes per second", [](benchpress::context* ctx) { 7 | ctx->set_bytes(1234567890); 8 | for (size_t i = 0; i < ctx->num_iterations(); ++i) { 9 | std::this_thread::sleep_for(std::chrono::seconds(1)); 10 | } 11 | }) --------------------------------------------------------------------------------