mirror of
https://github.com/boostorg/parser.git
synced 2026-01-19 16:32:13 +00:00
Compare commits
209 Commits
xform_repl
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c54307b95c | ||
|
|
5b3f122aa7 | ||
|
|
6db68bc7f7 | ||
|
|
00fe33d2f3 | ||
|
|
bb183cf5ee | ||
|
|
885595f7bd | ||
|
|
6826f957a1 | ||
|
|
647cec6683 | ||
|
|
c2ddd6e116 | ||
|
|
f7246de9db | ||
|
|
703a8afafe | ||
|
|
0eacce6080 | ||
|
|
5e61ba4e9e | ||
|
|
dc6144eeb4 | ||
|
|
d873d7ea80 | ||
|
|
1f5303c756 | ||
|
|
41e891dc95 | ||
|
|
159472ac6e | ||
|
|
bfa3e33372 | ||
|
|
8d7a64f7fe | ||
|
|
8c23054e07 | ||
|
|
11a5d9a872 | ||
|
|
d241bd7853 | ||
|
|
086241cbd9 | ||
|
|
e3a3cc8bf2 | ||
|
|
f789199743 | ||
|
|
f20c4cfb02 | ||
|
|
fcbc53ddce | ||
|
|
fed0a883ad | ||
|
|
bfc61fa963 | ||
|
|
68c306bf66 | ||
|
|
d5d080b9f2 | ||
|
|
03341ba32d | ||
|
|
7e69b27d7c | ||
|
|
17d76bc158 | ||
|
|
050b9ba800 | ||
|
|
125f12407b | ||
|
|
e4ba7c7a17 | ||
|
|
9a138a20f6 | ||
|
|
c32d594d64 | ||
|
|
c674e94c3d | ||
|
|
84ee288b02 | ||
|
|
39faa9ddbe | ||
|
|
b2927abc6c | ||
|
|
5d6d2f7b84 | ||
|
|
fd6c56df1b | ||
|
|
af41e6a7c2 | ||
|
|
0b93a586f1 | ||
|
|
ed9a06123b | ||
|
|
8ff46f394a | ||
|
|
8c9ad7bdb3 | ||
|
|
d8abe8f29e | ||
|
|
810adb43f6 | ||
|
|
5788fb6967 | ||
|
|
ec7df8a0af | ||
|
|
a93a1d2647 | ||
|
|
927f35f115 | ||
|
|
87617fdec0 | ||
|
|
ead639e630 | ||
|
|
a3ca1193b2 | ||
|
|
07153117ff | ||
|
|
6414f99e04 | ||
|
|
78bc141d5f | ||
|
|
b253d9ca53 | ||
|
|
0a34acc42a | ||
|
|
56c81c0b57 | ||
|
|
821d1d4c08 | ||
|
|
57cdd78210 | ||
|
|
3993efb692 | ||
|
|
74bc8fc1bb | ||
|
|
b42b052df4 | ||
|
|
42c9d82419 | ||
|
|
958ac38256 | ||
|
|
354586dd76 | ||
|
|
305bba875b | ||
|
|
f468d529fe | ||
|
|
b5d4339f2c | ||
|
|
6d7fa6f105 | ||
|
|
c975f57908 | ||
|
|
063291b78c | ||
|
|
3eb827dcd6 | ||
|
|
6d796287b6 | ||
|
|
bb0fb885b8 | ||
|
|
94a9daec40 | ||
|
|
4344dd3f47 | ||
|
|
a7c7470bc1 | ||
|
|
b273133fd2 | ||
|
|
3a7ddcf936 | ||
|
|
d79efb0daa | ||
|
|
9b03f3c1f9 | ||
|
|
446d09dc5d | ||
|
|
f2aa039282 | ||
|
|
1e64b6e416 | ||
|
|
41fb3997ba | ||
|
|
23017af526 | ||
|
|
274e4e36ea | ||
|
|
9d3647db3d | ||
|
|
568494346a | ||
|
|
f12e6a4882 | ||
|
|
912bfbded6 | ||
|
|
8f5b84ded5 | ||
|
|
8accdd922f | ||
|
|
71eeb36272 | ||
|
|
7ae86c1a0c | ||
|
|
e35d066f44 | ||
|
|
b1b7b221c7 | ||
|
|
d45b964cb1 | ||
|
|
2a9687f22f | ||
|
|
edd802ea8f | ||
|
|
75c29c0378 | ||
|
|
6b0194d7d6 | ||
|
|
4cea9c03d6 | ||
|
|
058fb68314 | ||
|
|
5864eee3be | ||
|
|
6eeed0dce9 | ||
|
|
d25207c0ee | ||
|
|
d7593f7960 | ||
|
|
57e796e2ae | ||
|
|
d774edc0e6 | ||
|
|
a37a1223ff | ||
|
|
5f50c371af | ||
|
|
1cb67e7728 | ||
|
|
12de839618 | ||
|
|
7d6682d536 | ||
|
|
6f28211166 | ||
|
|
294d84f2d5 | ||
|
|
dbfc8da474 | ||
|
|
939502e358 | ||
|
|
ce38d43657 | ||
|
|
0bbf5a8df6 | ||
|
|
5dce167bf9 | ||
|
|
88842dc789 | ||
|
|
072a812bcc | ||
|
|
a49c9521b5 | ||
|
|
7980c98222 | ||
|
|
843867944b | ||
|
|
56d043b3a4 | ||
|
|
62f4646c55 | ||
|
|
f39d119d44 | ||
|
|
06c49a428e | ||
|
|
d34cc345bb | ||
|
|
d88e47cf17 | ||
|
|
8e39ad067b | ||
|
|
55dcb7a8ad | ||
|
|
abbab9017f | ||
|
|
889be318f8 | ||
|
|
be8a9591f2 | ||
|
|
317248843b | ||
|
|
d5eba27273 | ||
|
|
f461b38850 | ||
|
|
744fd1bdc0 | ||
|
|
24288a03a2 | ||
|
|
824a208133 | ||
|
|
48d5cceb8f | ||
|
|
262c19e441 | ||
|
|
f113d302de | ||
|
|
6d253beaac | ||
|
|
2fa916530e | ||
|
|
3ff6a575ad | ||
|
|
a19af1b5ce | ||
|
|
4c0377a5ae | ||
|
|
f55d3779de | ||
|
|
87b50154cf | ||
|
|
1e011a1fac | ||
|
|
456a81155a | ||
|
|
947fd7782d | ||
|
|
cc9b74cb2d | ||
|
|
e129193296 | ||
|
|
a07efbbd72 | ||
|
|
9f46597ea9 | ||
|
|
5dc826c89d | ||
|
|
374cad3c35 | ||
|
|
4a9a45f856 | ||
|
|
b7f6cd8dbf | ||
|
|
aea0ff8dab | ||
|
|
9af9b5e373 | ||
|
|
f05b25ea1d | ||
|
|
f57de243a4 | ||
|
|
02c140a251 | ||
|
|
0e2936e20d | ||
|
|
4235706764 | ||
|
|
0056bd25d1 | ||
|
|
64e76e7b58 | ||
|
|
ed9fcc6f3a | ||
|
|
0a945decb6 | ||
|
|
9b47f3c551 | ||
|
|
51d9b81927 | ||
|
|
3e047101f6 | ||
|
|
7ae0f9f817 | ||
|
|
40b495ae5d | ||
|
|
e3c46c10c2 | ||
|
|
42f66d9079 | ||
|
|
9ebc984ff8 | ||
|
|
ed6e1b4a2c | ||
|
|
125ddf43c2 | ||
|
|
a2b7afc3a0 | ||
|
|
112290f63e | ||
|
|
27506af2f3 | ||
|
|
db79d00848 | ||
|
|
eda6238180 | ||
|
|
2697eebffd | ||
|
|
edff0b29da | ||
|
|
f3b5bcdd97 | ||
|
|
27400587d5 | ||
|
|
7a9126432a | ||
|
|
ca1ad064ff | ||
|
|
0ea1516b6e | ||
|
|
4ff205d6ec | ||
|
|
a132e31b66 |
79
.drone.star
Normal file
79
.drone.star
Normal file
@@ -0,0 +1,79 @@
|
||||
# Use, modification, and distribution are
|
||||
# subject to the Boost Software License, Version 1.0. (See accompanying
|
||||
# file LICENSE.txt)
|
||||
#
|
||||
# Copyright Rene Rivera 2020.
|
||||
# Copyright Alexander Grund 2022.
|
||||
# Copyright T. Zachary Laine 2024.
|
||||
|
||||
# For Drone CI we use the Starlark scripting language to reduce duplication.
|
||||
# As the yaml syntax for Drone CI is rather limited.
|
||||
|
||||
# Base environment for all jobs
|
||||
globalenv={'B2_CI_VERSION': '1', 'B2_VARIANT': 'release'}
|
||||
|
||||
# Wrapper function to apply the globalenv to all jobs
|
||||
def job(
|
||||
# job specific environment options
|
||||
env={},
|
||||
**kwargs):
|
||||
real_env = dict(globalenv)
|
||||
real_env.update(env)
|
||||
return job_impl(env=real_env, **kwargs)
|
||||
|
||||
def main(ctx):
|
||||
return [
|
||||
job(compiler='clang-11', cxxstd='17', os='ubuntu-22.04'),
|
||||
job(compiler='clang-12', cxxstd='17', os='ubuntu-22.04'),
|
||||
job(compiler='clang-13', cxxstd='17,20', os='ubuntu-22.04'),
|
||||
job(compiler='clang-14', cxxstd='17,20', os='ubuntu-22.04'),
|
||||
job(compiler='clang-15', cxxstd='17,20', os='ubuntu-22.04', add_llvm=True),
|
||||
|
||||
job(compiler='gcc-9', cxxstd='17', os='ubuntu-18.04'),
|
||||
job(compiler='gcc-10', cxxstd='17,20', os='ubuntu-22.04'),
|
||||
job(compiler='gcc-11', cxxstd='17,20,2b', os='ubuntu-22.04'),
|
||||
job(compiler='gcc-12', cxxstd='17,20,2b', os='ubuntu-22.04'),
|
||||
|
||||
# Sanitizers
|
||||
job(name='ASAN', asan=True,
|
||||
compiler='gcc-12', cxxstd='17,20', os='ubuntu-22.04'),
|
||||
job(name='UBSAN', ubsan=True,
|
||||
compiler='gcc-12', cxxstd='17,20', os='ubuntu-22.04'),
|
||||
job(name='TSAN', tsan=True,
|
||||
compiler='gcc-12', cxxstd='17,20', os='ubuntu-22.04'),
|
||||
job(name='Clang 14 w/ sanitizers', asan=True, ubsan=True,
|
||||
compiler='clang-14', cxxstd='17', os='ubuntu-22.04'),
|
||||
job(name='Clang 11 libc++ w/ sanitizers', asan=True, ubsan=True, # libc++-11 is the latest working with ASAN: https://github.com/llvm/llvm-project/issues/59432
|
||||
compiler='clang-11', cxxstd='17', os='ubuntu-20.04', stdlib='libc++', install='libc++-11-dev libc++abi-11-dev'),
|
||||
|
||||
# libc++
|
||||
job(compiler='clang-10', cxxstd='17', os='ubuntu-20.04', stdlib='libc++', install='libc++-10-dev libc++abi-10-dev'),
|
||||
job(compiler='clang-11', cxxstd='17', os='ubuntu-20.04', stdlib='libc++', install='libc++-11-dev libc++abi-11-dev'),
|
||||
job(compiler='clang-12', cxxstd='17', os='ubuntu-22.04', stdlib='libc++', install='libc++-12-dev libc++abi-12-dev libunwind-12-dev'),
|
||||
job(compiler='clang-13', cxxstd='17,20', os='ubuntu-22.04', stdlib='libc++', install='libc++-13-dev libc++abi-13-dev'),
|
||||
job(compiler='clang-14', cxxstd='17,20', os='ubuntu-22.04', stdlib='libc++', install='libc++-14-dev libc++abi-14-dev'),
|
||||
job(compiler='clang-15', cxxstd='17,20', os='ubuntu-22.04', stdlib='libc++', install='libc++-15-dev libc++abi-15-dev', add_llvm=True),
|
||||
|
||||
# FreeBSD
|
||||
job(compiler='clang-10', cxxstd='17', os='freebsd-13.1'),
|
||||
job(compiler='clang-15', cxxstd='17', os='freebsd-13.1'),
|
||||
job(compiler='gcc-11', cxxstd='17,20', os='freebsd-13.1', linkflags='-Wl,-rpath=/usr/local/lib/gcc11'),
|
||||
# OSX
|
||||
job(compiler='clang', cxxstd='17', os='osx-xcode-10.3'),
|
||||
job(compiler='clang', cxxstd='17', os='osx-xcode-11.1'),
|
||||
job(compiler='clang', cxxstd='17', os='osx-xcode-11.7'),
|
||||
job(compiler='clang', cxxstd='17', os='osx-xcode-12'),
|
||||
job(compiler='clang', cxxstd='17', os='osx-xcode-12.5.1'),
|
||||
job(compiler='clang', cxxstd='17,20', os='osx-xcode-13.0'),
|
||||
job(compiler='clang', cxxstd='17,20', os='osx-xcode-13.4.1'),
|
||||
job(compiler='clang', cxxstd='17,20', os='osx-xcode-14.0'),
|
||||
job(compiler='clang', cxxstd='17,20,2b', os='osx-xcode-14.3.1'),
|
||||
job(compiler='clang', cxxstd='17,20,2b', os='osx-xcode-15.0.1'),
|
||||
# ARM64
|
||||
job(compiler='gcc-11', cxxstd='17,20', os='ubuntu-20.04', arch='arm64'),
|
||||
# S390x
|
||||
job(compiler='gcc-11', cxxstd='17,20', os='ubuntu-20.04', arch='s390x'),
|
||||
]
|
||||
|
||||
# from https://github.com/boostorg/boost-ci
|
||||
load("@boost_ci//ci/drone/:functions.star", "linux_cxx", "windows_cxx", "osx_cxx", "freebsd_cxx", "job_impl")
|
||||
4
.drone/after-install.bat
Normal file
4
.drone/after-install.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Replace with actual code or remove this file
|
||||
echo Done with installation
|
||||
4
.drone/after-install.sh
Executable file
4
.drone/after-install.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Replace with actual code or remove this file
|
||||
echo "Done with installation"
|
||||
4
.drone/before-install.bat
Normal file
4
.drone/before-install.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Replace with actual code or remove this file
|
||||
echo Doing (fake) custom installation
|
||||
4
.drone/before-install.sh
Executable file
4
.drone/before-install.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Replace with actual code or remove this file
|
||||
echo "Doing (fake) custom installation"
|
||||
27
.drone/drone.bat
Normal file
27
.drone/drone.bat
Normal file
@@ -0,0 +1,27 @@
|
||||
@ECHO ON
|
||||
|
||||
|
||||
git clone https://github.com/boostorg/boost-ci.git boost-ci-cloned --depth 1
|
||||
REM Copy ci folder if not testing Boost.CI
|
||||
if "%DRONE_REPO%" == "%DRONE_REPO:boost-ci=%" xcopy /s /e /q /i /y boost-ci-cloned\ci .\ci
|
||||
rmdir /s /q boost-ci-cloned
|
||||
|
||||
set BOOST_CI_TARGET_BRANCH=%DRONE_BRANCH%
|
||||
set BOOST_CI_SRC_FOLDER=%cd%
|
||||
|
||||
@ECHO OFF
|
||||
echo ========================^> INSTALL
|
||||
|
||||
set custom_script=%BOOST_CI_SRC_FOLDER%\.drone\before-install.bat
|
||||
if exist %custom_script% call %custom_script%
|
||||
|
||||
call %BOOST_CI_SRC_FOLDER%\ci\common_install.bat
|
||||
|
||||
set custom_script=%BOOST_CI_SRC_FOLDER%\.drone\after-install.bat
|
||||
if exist %custom_script% call %custom_script%
|
||||
|
||||
echo ========================^> COMPILE
|
||||
|
||||
if NOT "%DRONE_JOB_BUILDTYPE%" == "boost" echo Ignoring DRONE_JOB_BUILDTYPE=%DRONE_JOB_BUILDTYPE% and doing a Boost build
|
||||
|
||||
call %BOOST_CI_SRC_FOLDER%\ci\build.bat
|
||||
67
.drone/drone.sh
Executable file
67
.drone/drone.sh
Executable file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 Rene Rivera, Sam Darwin
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
set -e
|
||||
|
||||
export USER=$(whoami)
|
||||
export CC=${CC:-gcc}
|
||||
export PATH=~/.local/bin:/usr/local/bin:$PATH
|
||||
|
||||
git clone https://github.com/boostorg/boost-ci.git boost-ci-cloned --depth 1
|
||||
[ "$(basename $DRONE_REPO)" == "boost-ci" ] || cp -prf boost-ci-cloned/ci .
|
||||
rm -rf boost-ci-cloned
|
||||
|
||||
export BOOST_CI_TARGET_BRANCH="$DRONE_BRANCH"
|
||||
export BOOST_CI_SRC_FOLDER=$(pwd)
|
||||
export CODECOV_NAME=${CODECOV_NAME:-"Drone CI"}
|
||||
|
||||
set +x
|
||||
echo '==================================> INSTALL'
|
||||
|
||||
scripts=(
|
||||
"$BOOST_CI_SRC_FOLDER/.drone/before-install.sh"
|
||||
"$BOOST_CI_SRC_FOLDER/ci/common_install.sh"
|
||||
"$BOOST_CI_SRC_FOLDER/.drone/after-install.sh"
|
||||
)
|
||||
for script in "${scripts[@]}"; do
|
||||
if [ -e "$script" ]; then
|
||||
echo "==============================> RUN $script"
|
||||
source "$script"
|
||||
set +x
|
||||
fi
|
||||
done
|
||||
|
||||
echo "B2 config: $(env | grep B2_ || true)"
|
||||
echo "==================================> SCRIPT ($DRONE_JOB_BUILDTYPE)"
|
||||
|
||||
case "$DRONE_JOB_BUILDTYPE" in
|
||||
boost)
|
||||
$BOOST_CI_SRC_FOLDER/ci/build.sh
|
||||
;;
|
||||
codecov)
|
||||
$BOOST_CI_SRC_FOLDER/ci/travis/codecov.sh
|
||||
;;
|
||||
valgrind)
|
||||
$BOOST_CI_SRC_FOLDER/ci/travis/valgrind.sh
|
||||
;;
|
||||
coverity)
|
||||
if [ -z "$COVERITY_SCAN_NOTIFICATION_EMAIL" ] || [ -z "$COVERITY_SCAN_TOKEN" ]; then
|
||||
echo "Coverity details not set up"
|
||||
[ -n "$COVERITY_SCAN_NOTIFICATION_EMAIL" ] || echo 'Missing $COVERITY_SCAN_NOTIFICATION_EMAIL'
|
||||
[ -n "$COVERITY_SCAN_TOKEN" ] || echo 'Missing $COVERITY_SCAN_TOKEN'
|
||||
exit 1
|
||||
fi
|
||||
echo "DRONE_BRANCH=$DRONE_BRANCH, DRONE_BUILD_EVENT=$DRONE_BUILD_EVENT, DRONE_REPO=$DRONE_REPO"
|
||||
if [[ "$DRONE_BRANCH" =~ ^(master|develop)$ ]] && [[ "$DRONE_BUILD_EVENT" =~ ^(push|cron)$ ]]; then
|
||||
export BOOST_REPO="$DRONE_REPO"
|
||||
export BOOST_BRANCH="$DRONE_BRANCH"
|
||||
$BOOST_CI_SRC_FOLDER/ci/coverity.sh
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unknown build type: $DRONE_JOB_BUILDTYPE"
|
||||
;;
|
||||
esac
|
||||
44
.github/workflows/coverage.yml
vendored
Normal file
44
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Coverage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
posix:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CXX: g++-11
|
||||
CXXFLAGS: -g -O0 -std=c++20 --coverage -fkeep-inline-functions -fkeep-static-functions
|
||||
LDFLAGS: --coverage
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: 4
|
||||
steps:
|
||||
- name: Install lcov
|
||||
run: sudo apt-get --no-install-recommends -y install lcov libgd-perl
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/build --preset coverage .
|
||||
|
||||
- name: Build
|
||||
run: cmake --build --preset coverage
|
||||
|
||||
- name: Test
|
||||
run: cmake --build --preset coverage --target check
|
||||
|
||||
- name: Make the coverage file
|
||||
run: cmake --build --preset coverage --target coverage
|
||||
|
||||
- name: Upload to codecov
|
||||
run: |
|
||||
bash <(curl -s https://codecov.io/bash) -f ${{github.workspace}}/build/coverage/coverage.info
|
||||
2
.github/workflows/deploy_unified_header.yml
vendored
2
.github/workflows/deploy_unified_header.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
|
||||
88
.github/workflows/fedora.yml
vendored
Normal file
88
.github/workflows/fedora.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Fedora
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# this doesn't support specific gcc versions yet
|
||||
cxx: [gcc, clang-15, clang-16, clang-17, clang-18]
|
||||
cxx_std: [17, 20, 23]
|
||||
# older clang versions have insufficient concepts support for
|
||||
# libstdc++' ranges implementation
|
||||
exclude:
|
||||
- cxx: clang-15
|
||||
cxx_std: 20
|
||||
- cxx: clang-15
|
||||
cxx_std: 23
|
||||
- cxx: clang-16
|
||||
cxx_std: 23
|
||||
runs-on: ubuntu-latest
|
||||
container: docker.io/library/fedora:latest
|
||||
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
case "${{ matrix.cxx }}" in
|
||||
gcc)
|
||||
dnf install -y gcc-c++
|
||||
;;
|
||||
clang-16)
|
||||
# the llvm-compat-packages copr lacks clang-16 for some reason
|
||||
dnf install -y 'dnf-command(copr)'
|
||||
dnf copr enable -y @fedora-llvm-team/llvm16
|
||||
dnf install -y 'clang-16*'
|
||||
;;
|
||||
clang-*)
|
||||
VERSION="$(echo "${{ matrix.cxx }}" | sed 's/clang-//')"
|
||||
AVAILABLE_VERSION="$(dnf --showduplicates --quiet list clang.x86_64 | awk '{print $2}' | grep "${VERSION}" | sort --version-sort | tail -n 1)"
|
||||
if [[ "$AVAILABLE_VERSION" ]]; then
|
||||
# use the main repo version if available, because installing
|
||||
# the same version from copr may cause library conflicts
|
||||
dnf install -y clang
|
||||
else
|
||||
# official repo from the Fedora llvm team
|
||||
# https://copr.fedorainfracloud.org/groups/g/fedora-llvm-team/coprs/
|
||||
dnf install -y 'dnf-command(copr)'
|
||||
dnf copr enable -y @fedora-llvm-team/llvm-compat-packages
|
||||
dnf install -y "$(echo "${{ matrix.cxx }}" | sed 's/-//')"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
dnf install -y cmake git
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
case "${{ matrix.cxx }}" in
|
||||
gcc)
|
||||
export CC=gcc
|
||||
export CXX=g++
|
||||
;;
|
||||
clang-*)
|
||||
export CC="${{ matrix.cxx }}"
|
||||
export CXX="$(echo "${{ matrix.cxx }}" | sed 's/clang/clang++/')"
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_C_COMPILER="${CC}" -DCMAKE_CXX_COMPILER="${CXX}" -DCXX_STD=${{ matrix.cxx_std }}
|
||||
|
||||
- name: Build
|
||||
run: cd build ; make -j4
|
||||
|
||||
- name: Test
|
||||
run: cd build ; make check
|
||||
@@ -1,4 +1,4 @@
|
||||
name: macos-12 - Clang 13
|
||||
name: macos-15 - Clang
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,27 +10,26 @@ env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cxx_std: [17]
|
||||
cxx_std: [17, 20]
|
||||
|
||||
runs-on: macos-12
|
||||
runs-on: macos-15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_WITHOUT_HANA=true -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCXX_STD=${{matrix.cxx_std}}
|
||||
|
||||
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCXX_STD=${{ matrix.cxx_std }}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: make -j2
|
||||
|
||||
working-directory: build
|
||||
run: cmake --build .
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
working-directory: build
|
||||
run: make check
|
||||
37
.github/workflows/ubuntu-20.04.yml
vendored
37
.github/workflows/ubuntu-20.04.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: Ubuntu-20.04 - GCC 9,10
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
compiler_version: [g++-9, g++-10]
|
||||
cxx_std: [17]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_WITHOUT_HANA=true -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_COMPILER=${{matrix.compiler_version}} -DCXX_STD=${{matrix.cxx_std}}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: make -j2
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: make check
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu-22.04 - GCC 10,11
|
||||
name: Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,28 +10,30 @@ env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler_version: [g++-10, g++-11]
|
||||
cxx_std: [17, 20]
|
||||
os: [ubuntu-22.04]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_WITHOUT_HANA=true -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_COMPILER=${{matrix.compiler_version}} -DCXX_STD=${{matrix.cxx_std}}
|
||||
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler_version }} -DCXX_STD=${{ matrix.cxx_std }}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: make -j2
|
||||
|
||||
run: cd build ; make -j4
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: make check
|
||||
run: cd build ; make check
|
||||
36
.github/workflows/windows-2019.yml
vendored
36
.github/workflows/windows-2019.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: windows-2019 - Visual Studio 2019
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cxx_std: [17, 20]
|
||||
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_WITHOUT_HANA=true -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCXX_STD=${{matrix.cxx_std}}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: cmake --build .
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build/test
|
||||
run: ctest -j4 -C ${{env.BUILD_TYPE}} --output-on-failure
|
||||
36
.github/workflows/windows-2022.yml
vendored
36
.github/workflows/windows-2022.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: windows-2022 - Visual Studio 2022
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cxx_std: [17, 20]
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_WITHOUT_HANA=true -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCXX_STD=${{matrix.cxx_std}}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: cmake --build .
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build/test
|
||||
run: ctest -j4 -C ${{env.BUILD_TYPE}} --output-on-failure
|
||||
37
.github/workflows/windows.yml
vendored
Normal file
37
.github/workflows/windows.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Windows MSVC
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cxx_std: [17, 20, 23]
|
||||
os: [windows-2022]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCXX_STD=${{ matrix.cxx_std }}
|
||||
|
||||
- name: Build
|
||||
working-directory: build
|
||||
run: cmake --build . -- /p:CL_MPcount=4
|
||||
|
||||
- name: Test
|
||||
working-directory: build/test
|
||||
run: ctest -j4 -C ${{ env.BUILD_TYPE }} --output-on-failure
|
||||
@@ -1,4 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
# Copyright (C) 2024 T. Zachary Laine
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
|
||||
# Generated by `boostdep --cmake parser`
|
||||
|
||||
cmake_minimum_required(VERSION 3.14...3.20)
|
||||
|
||||
project(boost_parser VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
add_library(boost_parser INTERFACE)
|
||||
add_library(Boost::parser ALIAS boost_parser)
|
||||
|
||||
target_include_directories(boost_parser INTERFACE include)
|
||||
|
||||
target_link_libraries(boost_parser
|
||||
INTERFACE
|
||||
Boost::assert
|
||||
Boost::hana
|
||||
Boost::type_index
|
||||
)
|
||||
|
||||
else()
|
||||
|
||||
cmake_minimum_required(VERSION 3.14...3.20)
|
||||
project(parse)
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
@@ -8,18 +34,34 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
set(CXX_STD 17 CACHE STRING "Set to 14, 17, etc., to enable C++14, C++17, etc.")
|
||||
message("-- Using -std=c++${CXX_STD}")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
add_definitions(-g -Wall)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
add_definitions(-g -Wall)
|
||||
# to just check the different frontend flavours we could use the following code
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
get_filename_component(CNAME "${CMAKE_CXX_COMPILER}" NAME)
|
||||
message(STATUS "Detected MSVC style compiler frontend '" ${CNAME} "'")
|
||||
add_definitions(/W3)
|
||||
add_compile_options(/Zc:__cplusplus) # not sure, if it necessary for clang-cl
|
||||
elseif (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
|
||||
get_filename_component(CNAME "${CMAKE_CXX_COMPILER}" NAME)
|
||||
message(STATUS "Detected GNU style compiler frontend '" ${CNAME} "'")
|
||||
add_definitions(-Wall)
|
||||
endif ()
|
||||
|
||||
# alternatively the boolean variable 'MSVC' is equivalent to 'if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")'
|
||||
# summary for a more detailed analysis (https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html#variables-that-provide-information):
|
||||
# CMAKE_CXX_COMPILER_ID: Clang, GNU, MSVC
|
||||
# CMAKE_CXX_COMPILER_FRONTEND_VARIANT: GNU, MSVC
|
||||
# CMAKE_CXX_SIMULATE_ID: MSVC (for Microsoft clang and clang-cl as they use the MSVC runtime)
|
||||
# CMAKE_CXX_PLATFORM_ID: Windows, Darwin, MinGW, MSYS, Linux, UNIX, ...
|
||||
# Variables that describe the system (https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html#variables-that-describe-the-system)
|
||||
|
||||
|
||||
##################################################
|
||||
# Build config
|
||||
##################################################
|
||||
set(BUILD_WITHOUT_HANA false CACHE BOOL "Build with BOOST_PARSER_DISABLE_HANA_TUPLE defined.")
|
||||
if (BUILD_WITHOUT_HANA)
|
||||
add_definitions(-DBOOST_PARSER_DISABLE_HANA_TUPLE)
|
||||
set(BUILD_WITH_HANA false CACHE BOOL
|
||||
"Build with optional Hana support (and BOOST_PARSER_USE_HANA_TUPLE defined).")
|
||||
if (BUILD_WITH_HANA)
|
||||
add_definitions(-DBOOST_PARSER_USE_HANA_TUPLE)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -28,11 +70,6 @@ endif()
|
||||
##################################################
|
||||
include(dependencies)
|
||||
|
||||
find_package(PythonInterp)
|
||||
if (PYTHONINTERP_FOUND)
|
||||
message("-- Found Python ${PYTHON_VERSION_STRING} (${PYTHON_EXECUTABLE})")
|
||||
endif ()
|
||||
|
||||
|
||||
##################################################
|
||||
# Sanitizers
|
||||
@@ -42,14 +79,16 @@ set(USE_UBSAN false CACHE BOOL "Set to true to enable -fsanitize=undefined when
|
||||
if (USE_ASAN AND USE_UBSAN)
|
||||
message(FATAL_ERROR "USE_ASAN and USE_UBSAN must not be enabled at the same time")
|
||||
elseif (USE_ASAN)
|
||||
set(compile_flags -fsanitize=address)
|
||||
set(link_flags -fsanitize=address)
|
||||
elseif (USE_UBSAN)
|
||||
set(compile_flags -fsanitize=undefined)
|
||||
set(link_flags -fsanitize=undefined)
|
||||
endif()
|
||||
|
||||
|
||||
##################################################
|
||||
# Parser sub-library
|
||||
# Parser
|
||||
##################################################
|
||||
add_library(parser INTERFACE)
|
||||
target_include_directories(parser INTERFACE ${CMAKE_SOURCE_DIR}/include)
|
||||
@@ -57,8 +96,11 @@ if (Boost_FOUND)
|
||||
target_include_directories(parser INTERFACE ${Boost_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
|
||||
##################################################
|
||||
# Subdirectories
|
||||
##################################################
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(example)
|
||||
|
||||
endif()
|
||||
|
||||
49
CMakePresets.json
Normal file
49
CMakePresets.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"version": 2,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 14,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "cmake-pedantic",
|
||||
"hidden": true,
|
||||
"warnings": {
|
||||
"dev": true,
|
||||
"deprecated": true,
|
||||
"uninitialized": false,
|
||||
"unusedCli": true,
|
||||
"systemVars": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "coverage",
|
||||
"generator": "Unix Makefiles",
|
||||
"hidden": false,
|
||||
"inherits": ["cmake-pedantic"],
|
||||
"binaryDir": "${sourceDir}/build/coverage",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Coverage",
|
||||
"CMAKE_CXX_EXTENSIONS": "OFF",
|
||||
"CMAKE_CXX_FLAGS": "-Wall -std=c++17 -Og -g --coverage -fkeep-inline-functions -fkeep-static-functions",
|
||||
"CMAKE_CXX_STANDARD_REQUIRED": "ON",
|
||||
"CMAKE_EXE_LINKER_FLAGS": "--coverage",
|
||||
"CMAKE_SHARED_LINKER_FLAGS": "--coverage",
|
||||
"PROJECT_BINARY_DIR": "${sourceDir}/build/coverage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{ "name": "coverage", "configurePreset": "coverage" }
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "test",
|
||||
"hidden": true,
|
||||
"output": {"outputOnFailure": true},
|
||||
"execution": {"noTestsAction": "error", "stopOnFailure": true}
|
||||
},
|
||||
{ "name": "coverage", "configurePreset": "coverage", "inherits": ["test"] }
|
||||
]
|
||||
}
|
||||
31
README.md
31
README.md
@@ -34,12 +34,7 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||
This library is header-only, and has a default dependency on Boost.Hana. The
|
||||
Boost.Hana dependency can be eliminated, and `std::tuple` will be used instead
|
||||
of `boost::hana::tuple` throughout the library, if you `#define`
|
||||
`BOOST_PARSER_DISABLE_HANA_TUPLE`. To try out the lib without mseeing with
|
||||
dependencies, add its `include/` dir as an include path in your build and
|
||||
define `BOOST_PARSER_DISABLE_HANA_TUPLE` your build.
|
||||
This library is header-only, and has no Boost dependencies by default.
|
||||
|
||||
Features:
|
||||
|
||||
@@ -52,32 +47,26 @@ Features:
|
||||
- Trace support for debugging your parsers.
|
||||
- Clever hacks to make compile time errors easier to deal with. (These are totally optional.)
|
||||
|
||||
This library targets submission to Boost.
|
||||
|
||||
Online docs: https://tzlaine.github.io/parser
|
||||
This library first appeared in Boost 1.87.0
|
||||
|
||||
Master status:
|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/ubuntu.yml)
|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/fedora.yml)
|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
|
||||
|
||||

|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
|
||||
|
||||
Develop status:
|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/ubuntu.yml)
|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/fedora.yml)
|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
|
||||
|
||||

|
||||
|
||||

|
||||
[](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
|
||||
|
||||
[](LICENSE_1_0.txt)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
...
|
||||
|
||||
46
benchmark-v1.1.0/.gitignore
vendored
46
benchmark-v1.1.0/.gitignore
vendored
@@ -1,46 +0,0 @@
|
||||
*.a
|
||||
*.so
|
||||
*.so.?*
|
||||
*.dll
|
||||
*.exe
|
||||
*.dylib
|
||||
*.cmake
|
||||
!/cmake/*.cmake
|
||||
*~
|
||||
*.pyc
|
||||
__pycache__
|
||||
|
||||
# lcov
|
||||
*.lcov
|
||||
/lcov
|
||||
|
||||
# cmake files.
|
||||
/Testing
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
|
||||
# makefiles.
|
||||
Makefile
|
||||
|
||||
# in-source build.
|
||||
bin/
|
||||
lib/
|
||||
/test/*_test
|
||||
|
||||
# exuberant ctags.
|
||||
tags
|
||||
|
||||
# YouCompleteMe configuration.
|
||||
.ycm_extra_conf.pyc
|
||||
|
||||
# ninja generated files.
|
||||
.ninja_deps
|
||||
.ninja_log
|
||||
build.ninja
|
||||
install_manifest.txt
|
||||
rules.ninja
|
||||
|
||||
# out-of-source build top-level folders.
|
||||
build/
|
||||
_build/
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Install a newer CMake version
|
||||
curl -sSL https://cmake.org/files/v3.6/cmake-3.6.1-Linux-x86_64.sh -o install-cmake.sh
|
||||
chmod +x install-cmake.sh
|
||||
sudo ./install-cmake.sh --prefix=/usr/local --skip-license
|
||||
|
||||
# Checkout LLVM sources
|
||||
git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source
|
||||
git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx
|
||||
git clone --depth=1 https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi
|
||||
|
||||
# Build and install libc++ (Use unstable ABI for better sanitizer coverage)
|
||||
mkdir llvm-build && cd llvm-build
|
||||
cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DLIBCXX_ABI_UNSTABLE=ON \
|
||||
-DLLVM_USE_SANITIZER=${LIBCXX_SANITIZER} \
|
||||
../llvm-source
|
||||
make cxx -j2
|
||||
sudo make install-cxxabi install-cxx
|
||||
cd ../
|
||||
@@ -1,102 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: cpp
|
||||
|
||||
env:
|
||||
global:
|
||||
- /usr/local/bin:$PATH
|
||||
|
||||
# NOTE: The COMPILER variable is unused. It simply makes the display on
|
||||
# travis-ci.org more readable.
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lcov
|
||||
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Coverage
|
||||
- compiler: gcc
|
||||
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Debug
|
||||
- compiler: gcc
|
||||
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Release
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-6
|
||||
env:
|
||||
- COMPILER=g++-6 C_COMPILER=gcc-6 BUILD_TYPE=Debug
|
||||
- EXTRA_FLAGS="-fno-omit-frame-pointer -g -O2 -fsanitize=undefined,address -fuse-ld=gold"
|
||||
- compiler: clang
|
||||
env: COMPILER=clang++ C_COMPILER=clang BUILD_TYPE=Debug
|
||||
- compiler: clang
|
||||
env: COMPILER=clang++ C_COMPILER=clang BUILD_TYPE=Release
|
||||
# Clang w/ libc++
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
clang-3.8
|
||||
env:
|
||||
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||
- LIBCXX_BUILD=1
|
||||
- EXTRA_FLAGS="-stdlib=libc++"
|
||||
# Clang w/ libc++, ASAN, UBSAN
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
clang-3.8
|
||||
env:
|
||||
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||
- LIBCXX_BUILD=1 LIBCXX_SANITIZER="Undefined;Address"
|
||||
- EXTRA_FLAGS="-stdlib=libc++ -fno-omit-frame-pointer -g -O2 -fsanitize=undefined,address -fno-sanitize-recover=all"
|
||||
- UBSAN_OPTIONS=print_stacktrace=1
|
||||
# Clang w/ libc++ and MSAN
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
clang-3.8
|
||||
env:
|
||||
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=MemoryWithOrigins
|
||||
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins"
|
||||
# Clang w/ libc++ and MSAN
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
clang-3.8
|
||||
env:
|
||||
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=RelWithDebInfo
|
||||
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=Thread
|
||||
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all"
|
||||
|
||||
before_script:
|
||||
- if [ -n "${LIBCXX_BUILD}" ]; then
|
||||
source .travis-libcxx-setup.sh;
|
||||
fi
|
||||
- mkdir build && cd build
|
||||
|
||||
install:
|
||||
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||
PATH=~/.local/bin:${PATH};
|
||||
pip install --user --upgrade pip;
|
||||
pip install --user cpp-coveralls;
|
||||
fi
|
||||
|
||||
script:
|
||||
- cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS}" ..
|
||||
- make
|
||||
- make CTEST_OUTPUT_ON_FAILURE=1 test
|
||||
|
||||
after_success:
|
||||
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||
coveralls --include src --include include --gcov-options '\-lp' --root .. --build-root .;
|
||||
fi
|
||||
|
||||
sudo: required
|
||||
@@ -1,115 +0,0 @@
|
||||
import os
|
||||
import ycm_core
|
||||
|
||||
# These are the compilation flags that will be used in case there's no
|
||||
# compilation database set (by default, one is not set).
|
||||
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
|
||||
flags = [
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'-pendantic-errors',
|
||||
'-std=c++0x',
|
||||
'-fno-strict-aliasing',
|
||||
'-O3',
|
||||
'-DNDEBUG',
|
||||
# ...and the same thing goes for the magic -x option which specifies the
|
||||
# language that the files to be compiled are written in. This is mostly
|
||||
# relevant for c++ headers.
|
||||
# For a C project, you would set this to 'c' instead of 'c++'.
|
||||
'-x', 'c++',
|
||||
'-I', 'include',
|
||||
'-isystem', '/usr/include',
|
||||
'-isystem', '/usr/local/include',
|
||||
]
|
||||
|
||||
|
||||
# Set this to the absolute path to the folder (NOT the file!) containing the
|
||||
# compile_commands.json file to use that instead of 'flags'. See here for
|
||||
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
|
||||
#
|
||||
# Most projects will NOT need to set this to anything; you can just change the
|
||||
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
|
||||
compilation_database_folder = ''
|
||||
|
||||
if os.path.exists( compilation_database_folder ):
|
||||
database = ycm_core.CompilationDatabase( compilation_database_folder )
|
||||
else:
|
||||
database = None
|
||||
|
||||
SOURCE_EXTENSIONS = [ '.cc' ]
|
||||
|
||||
def DirectoryOfThisScript():
|
||||
return os.path.dirname( os.path.abspath( __file__ ) )
|
||||
|
||||
|
||||
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
|
||||
if not working_directory:
|
||||
return list( flags )
|
||||
new_flags = []
|
||||
make_next_absolute = False
|
||||
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
|
||||
for flag in flags:
|
||||
new_flag = flag
|
||||
|
||||
if make_next_absolute:
|
||||
make_next_absolute = False
|
||||
if not flag.startswith( '/' ):
|
||||
new_flag = os.path.join( working_directory, flag )
|
||||
|
||||
for path_flag in path_flags:
|
||||
if flag == path_flag:
|
||||
make_next_absolute = True
|
||||
break
|
||||
|
||||
if flag.startswith( path_flag ):
|
||||
path = flag[ len( path_flag ): ]
|
||||
new_flag = path_flag + os.path.join( working_directory, path )
|
||||
break
|
||||
|
||||
if new_flag:
|
||||
new_flags.append( new_flag )
|
||||
return new_flags
|
||||
|
||||
|
||||
def IsHeaderFile( filename ):
|
||||
extension = os.path.splitext( filename )[ 1 ]
|
||||
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
|
||||
|
||||
|
||||
def GetCompilationInfoForFile( filename ):
|
||||
# The compilation_commands.json file generated by CMake does not have entries
|
||||
# for header files. So we do our best by asking the db for flags for a
|
||||
# corresponding source file, if any. If one exists, the flags for that file
|
||||
# should be good enough.
|
||||
if IsHeaderFile( filename ):
|
||||
basename = os.path.splitext( filename )[ 0 ]
|
||||
for extension in SOURCE_EXTENSIONS:
|
||||
replacement_file = basename + extension
|
||||
if os.path.exists( replacement_file ):
|
||||
compilation_info = database.GetCompilationInfoForFile(
|
||||
replacement_file )
|
||||
if compilation_info.compiler_flags_:
|
||||
return compilation_info
|
||||
return None
|
||||
return database.GetCompilationInfoForFile( filename )
|
||||
|
||||
|
||||
def FlagsForFile( filename, **kwargs ):
|
||||
if database:
|
||||
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
|
||||
# python list, but a "list-like" StringVec object
|
||||
compilation_info = GetCompilationInfoForFile( filename )
|
||||
if not compilation_info:
|
||||
return None
|
||||
|
||||
final_flags = MakeRelativePathsInFlagsAbsolute(
|
||||
compilation_info.compiler_flags_,
|
||||
compilation_info.compiler_working_dir_ )
|
||||
else:
|
||||
relative_to = DirectoryOfThisScript()
|
||||
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
|
||||
|
||||
return {
|
||||
'flags': final_flags,
|
||||
'do_cache': True
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
# This is the official list of benchmark authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Albert Pretorius <pretoalb@gmail.com>
|
||||
Arne Beer <arne@twobeer.de>
|
||||
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||
Dominic Hamon <dma@stripysock.com>
|
||||
Eric Fiselier <eric@efcs.ca>
|
||||
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||
Evgeny Safronov <division494@gmail.com>
|
||||
Felix Homann <linuxaudio@showlabor.de>
|
||||
Google Inc.
|
||||
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
||||
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
||||
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
||||
Kaito Udagawa <umireon@gmail.com>
|
||||
Lei Xu <eddyxu@gmail.com>
|
||||
Matt Clarkson <mattyclarkson@gmail.com>
|
||||
Nick Hutchinson <nshutchinson@gmail.com>
|
||||
Oleksandr Sochka <sasha.sochka@gmail.com>
|
||||
Paul Redmond <paul.redmond@gmail.com>
|
||||
Radoslav Yovchev <radoslav.tm@gmail.com>
|
||||
Shuo Chen <chenshuo@chenshuo.com>
|
||||
Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
Dirac Research
|
||||
Zbigniew Skowron <zbychs@gmail.com>
|
||||
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
||||
@@ -1,179 +0,0 @@
|
||||
cmake_minimum_required (VERSION 2.8.11)
|
||||
project (benchmark)
|
||||
|
||||
foreach(p
|
||||
CMP0054 # CMake 3.1
|
||||
CMP0056 # export EXE_LINKER_FLAGS to try_run
|
||||
)
|
||||
if(POLICY ${p})
|
||||
cmake_policy(SET ${p} NEW)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
option(BENCHMARK_ENABLE_TESTING "Enable testing of the benchmark library." OFF)
|
||||
option(BENCHMARK_ENABLE_LTO "Enable link time optimisation of the benchmark library." OFF)
|
||||
option(BENCHMARK_USE_LIBCXX "Build and test using libc++ as the standard library." OFF)
|
||||
# Make sure we can import out CMake functions
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
# Read the git tags to determine the project version
|
||||
include(GetGitVersion)
|
||||
get_git_version(GIT_VERSION)
|
||||
|
||||
# Tell the user what versions we are using
|
||||
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" VERSION ${GIT_VERSION})
|
||||
message("-- Version: ${VERSION}")
|
||||
|
||||
# The version of the libraries
|
||||
set(GENERIC_LIB_VERSION ${VERSION})
|
||||
string(SUBSTRING ${VERSION} 0 1 GENERIC_LIB_SOVERSION)
|
||||
|
||||
# Import our CMake modules
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(AddCXXCompilerFlag)
|
||||
include(CXXFeatureCheck)
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# Turn compiler warnings up to 11
|
||||
string(REGEX REPLACE "[-/]W[1-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
# Link time optimisation
|
||||
if (BENCHMARK_ENABLE_LTO)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /GL")
|
||||
string(REGEX REPLACE "[-/]INCREMENTAL" "/INCREMENTAL:NO" CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
|
||||
string(REGEX REPLACE "[-/]INCREMENTAL" "/INCREMENTAL:NO" CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
|
||||
string(REGEX REPLACE "[-/]INCREMENTAL" "/INCREMENTAL:NO" CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /LTCG")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /GL")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL "${CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL} /LTCG")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} /LTCG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /LTCG")
|
||||
endif()
|
||||
else()
|
||||
# Try and enable C++11. Don't use C++14 because it doesn't work in some
|
||||
# configurations.
|
||||
add_cxx_compiler_flag(-std=c++11)
|
||||
if (NOT HAVE_CXX_FLAG_STD_CXX11)
|
||||
add_cxx_compiler_flag(-std=c++0x)
|
||||
endif()
|
||||
|
||||
# Turn compiler warnings up to 11
|
||||
add_cxx_compiler_flag(-Wall)
|
||||
|
||||
add_cxx_compiler_flag(-Wextra)
|
||||
add_cxx_compiler_flag(-Wshadow)
|
||||
add_cxx_compiler_flag(-Werror RELEASE)
|
||||
add_cxx_compiler_flag(-Werror RELWITHDEBINFO)
|
||||
add_cxx_compiler_flag(-Werror MINSIZEREL)
|
||||
add_cxx_compiler_flag(-pedantic)
|
||||
add_cxx_compiler_flag(-pedantic-errors)
|
||||
add_cxx_compiler_flag(-Wshorten-64-to-32)
|
||||
add_cxx_compiler_flag(-Wfloat-equal)
|
||||
add_cxx_compiler_flag(-fstrict-aliasing)
|
||||
if (NOT BENCHMARK_USE_LIBCXX)
|
||||
add_cxx_compiler_flag(-Wzero-as-null-pointer-constant)
|
||||
endif()
|
||||
if (HAVE_CXX_FLAG_FSTRICT_ALIASING)
|
||||
add_cxx_compiler_flag(-Wstrict-aliasing)
|
||||
endif()
|
||||
add_cxx_compiler_flag(-Wthread-safety)
|
||||
if (HAVE_CXX_FLAG_WTHREAD_SAFETY)
|
||||
cxx_feature_check(THREAD_SAFETY_ATTRIBUTES)
|
||||
endif()
|
||||
|
||||
# On most UNIX like platforms g++ and clang++ define _GNU_SOURCE as a
|
||||
# predefined macro, which turns on all of the wonderful libc extensions.
|
||||
# However g++ doesn't do this in Cygwin so we have to define it ourselfs
|
||||
# since we depend on GNU/POSIX/BSD extensions.
|
||||
if (CYGWIN)
|
||||
add_definitions(-D_GNU_SOURCE=1)
|
||||
endif()
|
||||
|
||||
# Link time optimisation
|
||||
if (BENCHMARK_ENABLE_LTO)
|
||||
add_cxx_compiler_flag(-flto)
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
find_program(GCC_AR gcc-ar)
|
||||
if (GCC_AR)
|
||||
set(CMAKE_AR ${GCC_AR})
|
||||
endif()
|
||||
find_program(GCC_RANLIB gcc-ranlib)
|
||||
if (GCC_RANLIB)
|
||||
set(CMAKE_RANLIB ${GCC_RANLIB})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Coverage build type
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING
|
||||
"Flags used by the C++ compiler during coverage builds."
|
||||
FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
"${CMAKE_EXE_LINKER_FLAGS_DEBUG}" CACHE STRING
|
||||
"Flags used for linking binaries during coverage builds."
|
||||
FORCE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG}" CACHE STRING
|
||||
"Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE)
|
||||
mark_as_advanced(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE)
|
||||
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
|
||||
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel Coverage."
|
||||
FORCE)
|
||||
add_cxx_compiler_flag(--coverage COVERAGE)
|
||||
endif()
|
||||
|
||||
if (BENCHMARK_USE_LIBCXX)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_cxx_compiler_flag(-stdlib=libc++)
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
||||
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
|
||||
add_cxx_compiler_flag(-nostdinc++)
|
||||
message("libc++ header path must be manually specified using CMAKE_CXX_FLAGS")
|
||||
# Adding -nodefaultlibs directly to CMAKE_<TYPE>_LINKER_FLAGS will break
|
||||
# configuration checks such as 'find_package(Threads)'
|
||||
list(APPEND BENCHMARK_CXX_LINKER_FLAGS -nodefaultlibs)
|
||||
# -lc++ cannot be added directly to CMAKE_<TYPE>_LINKER_FLAGS because
|
||||
# linker flags appear before all linker inputs and -lc++ must appear after.
|
||||
list(APPEND BENCHMARK_CXX_LIBRARIES c++)
|
||||
else()
|
||||
message(FATAL "-DBENCHMARK_USE_LIBCXX:BOOL=ON is not supported for compiler")
|
||||
endif()
|
||||
endif(BENCHMARK_USE_LIBCXX)
|
||||
|
||||
# C++ feature checks
|
||||
# Determine the correct regular expression engine to use
|
||||
cxx_feature_check(STD_REGEX)
|
||||
cxx_feature_check(GNU_POSIX_REGEX)
|
||||
cxx_feature_check(POSIX_REGEX)
|
||||
if(NOT HAVE_STD_REGEX AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX)
|
||||
message(FATAL_ERROR "Failed to determine the source files for the regular expression backend")
|
||||
endif()
|
||||
|
||||
cxx_feature_check(STEADY_CLOCK)
|
||||
# Ensure we have pthreads
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# Set up directories
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
# Build the targets
|
||||
add_subdirectory(src)
|
||||
|
||||
if (BENCHMARK_ENABLE_TESTING)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
@@ -1,58 +0,0 @@
|
||||
# How to contribute #
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
a just a few small guidelines you need to follow.
|
||||
|
||||
|
||||
## Contributor License Agreement ##
|
||||
|
||||
Contributions to any Google project must be accompanied by a Contributor
|
||||
License Agreement. This is not a copyright **assignment**, it simply gives
|
||||
Google permission to use and redistribute your contributions as part of the
|
||||
project.
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual
|
||||
CLA][].
|
||||
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA][].
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted
|
||||
one (even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
[individual CLA]: https://developers.google.com/open-source/cla/individual
|
||||
[corporate CLA]: https://developers.google.com/open-source/cla/corporate
|
||||
|
||||
Once your CLA is submitted (or if you already submitted one for
|
||||
another Google project), make a commit adding yourself to the
|
||||
[AUTHORS][] and [CONTRIBUTORS][] files. This commit can be part
|
||||
of your first [pull request][].
|
||||
|
||||
[AUTHORS]: AUTHORS
|
||||
[CONTRIBUTORS]: CONTRIBUTORS
|
||||
|
||||
|
||||
## Submitting a patch ##
|
||||
|
||||
1. It's generally best to start by opening a new issue describing the bug or
|
||||
feature you're intending to fix. Even if you think it's relatively minor,
|
||||
it's helpful to know what people are working on. Mention in the initial
|
||||
issue that you are planning to work on that bug or feature so that it can
|
||||
be assigned to you.
|
||||
|
||||
1. Follow the normal process of [forking][] the project, and setup a new
|
||||
branch to work in. It's important that each group of changes be done in
|
||||
separate branches in order to ensure that a pull request only includes the
|
||||
commits related to that bug or feature.
|
||||
|
||||
1. Do your best to have [well-formed commit messages][] for each change.
|
||||
This provides consistency throughout the project, and ensures that commit
|
||||
messages are able to be formatted properly by various git tools.
|
||||
|
||||
1. Finally, push the commits to your fork and submit a [pull request][].
|
||||
|
||||
[forking]: https://help.github.com/articles/fork-a-repo
|
||||
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
[pull request]: https://help.github.com/articles/creating-a-pull-request
|
||||
@@ -1,53 +0,0 @@
|
||||
# People who have agreed to one of the CLAs and can contribute patches.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# https://developers.google.com/open-source/cla/individual
|
||||
# https://developers.google.com/open-source/cla/corporate
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name <email address>
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Albert Pretorius <pretoalb@gmail.com>
|
||||
Arne Beer <arne@twobeer.de>
|
||||
Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
|
||||
Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
|
||||
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||
Dominic Hamon <dma@stripysock.com>
|
||||
Eric Fiselier <eric@efcs.ca>
|
||||
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||
Evgeny Safronov <division494@gmail.com>
|
||||
Felix Homann <linuxaudio@showlabor.de>
|
||||
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
||||
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
||||
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
||||
Kaito Udagawa <umireon@gmail.com>
|
||||
Kai Wolf <kai.wolf@gmail.com>
|
||||
Lei Xu <eddyxu@gmail.com>
|
||||
Matt Clarkson <mattyclarkson@gmail.com>
|
||||
Nick Hutchinson <nshutchinson@gmail.com>
|
||||
Oleksandr Sochka <sasha.sochka@gmail.com>
|
||||
Pascal Leroy <phl@google.com>
|
||||
Paul Redmond <paul.redmond@gmail.com>
|
||||
Pierre Phaneuf <pphaneuf@google.com>
|
||||
Radoslav Yovchev <radoslav.tm@gmail.com>
|
||||
Shuo Chen <chenshuo@chenshuo.com>
|
||||
Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
Tobias Ulvgård <tobias.ulvgard@dirac.se>
|
||||
Zbigniew Skowron <zbychs@gmail.com>
|
||||
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
||||
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,54 +0,0 @@
|
||||
version: '{build}'
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- compiler: msvc-12-seh
|
||||
generator: "Visual Studio 12 2013"
|
||||
|
||||
- compiler: msvc-12-seh
|
||||
generator: "Visual Studio 12 2013 Win64"
|
||||
|
||||
- compiler: msvc-14-seh
|
||||
generator: "Visual Studio 14 2015"
|
||||
|
||||
- compiler: msvc-14-seh
|
||||
generator: "Visual Studio 14 2015 Win64"
|
||||
|
||||
- compiler: gcc-5.3.0-posix
|
||||
generator: "MinGW Makefiles"
|
||||
cxx_path: 'C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin'
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
install:
|
||||
# git bash conflicts with MinGW makefiles
|
||||
- if "%generator%"=="MinGW Makefiles" (set "PATH=%PATH:C:\Program Files\Git\usr\bin;=%")
|
||||
- if not "%cxx_path%"=="" (set "PATH=%PATH%;%cxx_path%")
|
||||
|
||||
# TODO Remove this. This is a hack to work around bogus warning messages
|
||||
# See http://goo.gl/euguBI for more information.
|
||||
before_build:
|
||||
- del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
|
||||
- del "C:\Program Files (x86)\MSBuild\12.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
|
||||
|
||||
build_script:
|
||||
- md _build -Force
|
||||
- cd _build
|
||||
- echo %configuration%
|
||||
- cmake -G "%generator%" "-DCMAKE_BUILD_TYPE=%configuration%" ..
|
||||
- cmake --build . --config %configuration%
|
||||
|
||||
test_script:
|
||||
- ctest -c %configuration% --timeout 300 --output-on-failure
|
||||
|
||||
artifacts:
|
||||
- path: '_build/CMakeFiles/*.log'
|
||||
name: logs
|
||||
- path: '_build/Testing/**/*.xml'
|
||||
name: test_results
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# - Adds a compiler flag if it is supported by the compiler
|
||||
#
|
||||
# This function checks that the supplied compiler flag is supported and then
|
||||
# adds it to the corresponding compiler flags
|
||||
#
|
||||
# add_cxx_compiler_flag(<FLAG> [<VARIANT>])
|
||||
#
|
||||
# - Example
|
||||
#
|
||||
# include(AddCXXCompilerFlag)
|
||||
# add_cxx_compiler_flag(-Wall)
|
||||
# add_cxx_compiler_flag(-no-strict-aliasing RELEASE)
|
||||
# Requires CMake 2.6+
|
||||
|
||||
if(__add_cxx_compiler_flag)
|
||||
return()
|
||||
endif()
|
||||
set(__add_cxx_compiler_flag INCLUDED)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
function(add_cxx_compiler_flag FLAG)
|
||||
string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG)
|
||||
string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||
string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||
string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
|
||||
check_cxx_compiler_flag("${FLAG}" ${SANITIZED_FLAG})
|
||||
if(${SANITIZED_FLAG})
|
||||
set(VARIANT ${ARGV1})
|
||||
if(ARGV1)
|
||||
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# - Compile and run code to check for C++ features
|
||||
#
|
||||
# This functions compiles a source file under the `cmake` folder
|
||||
# and adds the corresponding `HAVE_[FILENAME]` flag to the CMake
|
||||
# environment
|
||||
#
|
||||
# cxx_feature_check(<FLAG> [<VARIANT>])
|
||||
#
|
||||
# - Example
|
||||
#
|
||||
# include(CXXFeatureCheck)
|
||||
# cxx_feature_check(STD_REGEX)
|
||||
# Requires CMake 2.6+
|
||||
|
||||
if(__cxx_feature_check)
|
||||
return()
|
||||
endif()
|
||||
set(__cxx_feature_check INCLUDED)
|
||||
|
||||
function(cxx_feature_check FILE)
|
||||
string(TOLOWER ${FILE} FILE)
|
||||
string(TOUPPER ${FILE} VAR)
|
||||
string(TOUPPER "HAVE_${VAR}" FEATURE)
|
||||
if (DEFINED HAVE_${VAR})
|
||||
return()
|
||||
endif()
|
||||
message("-- Performing Test ${FEATURE}")
|
||||
try_run(RUN_${FEATURE} COMPILE_${FEATURE}
|
||||
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
||||
CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
|
||||
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
|
||||
if(RUN_${FEATURE} EQUAL 0)
|
||||
message("-- Performing Test ${FEATURE} -- success")
|
||||
set(HAVE_${VAR} 1 CACHE INTERNAL "Feature test for ${FILE}" PARENT_SCOPE)
|
||||
add_definitions(-DHAVE_${VAR})
|
||||
else()
|
||||
if(NOT COMPILE_${FEATURE})
|
||||
message("-- Performing Test ${FEATURE} -- failed to compile")
|
||||
else()
|
||||
message("-- Performing Test ${FEATURE} -- compiled but failed to run")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# - Returns a version string from Git tags
|
||||
#
|
||||
# This function inspects the annotated git tags for the project and returns a string
|
||||
# into a CMake variable
|
||||
#
|
||||
# get_git_version(<var>)
|
||||
#
|
||||
# - Example
|
||||
#
|
||||
# include(GetGitVersion)
|
||||
# get_git_version(GIT_VERSION)
|
||||
#
|
||||
# Requires CMake 2.8.11+
|
||||
find_package(Git)
|
||||
|
||||
if(__get_git_version)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_version INCLUDED)
|
||||
|
||||
function(get_git_version var)
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8
|
||||
RESULT_VARIABLE status
|
||||
OUTPUT_VARIABLE GIT_VERSION
|
||||
ERROR_QUIET)
|
||||
if(${status})
|
||||
set(GIT_VERSION "v0.0.0")
|
||||
else()
|
||||
string(STRIP ${GIT_VERSION} GIT_VERSION)
|
||||
string(REGEX REPLACE "-[0-9]+-g" "-" GIT_VERSION ${GIT_VERSION})
|
||||
endif()
|
||||
|
||||
# Work out if the repository is dirty
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD --
|
||||
OUTPUT_VARIABLE GIT_DIFF_INDEX
|
||||
ERROR_QUIET)
|
||||
string(COMPARE NOTEQUAL "${GIT_DIFF_INDEX}" "" GIT_DIRTY)
|
||||
if (${GIT_DIRTY})
|
||||
set(GIT_VERSION "${GIT_VERSION}-dirty")
|
||||
endif()
|
||||
else()
|
||||
set(GIT_VERSION "v0.0.0")
|
||||
endif()
|
||||
|
||||
message("-- git Version: ${GIT_VERSION}")
|
||||
set(${var} ${GIT_VERSION} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -1,12 +0,0 @@
|
||||
#include <gnuregex.h>
|
||||
#include <string>
|
||||
int main() {
|
||||
std::string str = "test0159";
|
||||
regex_t re;
|
||||
int ec = regcomp(&re, "^[a-z]+[0-9]+$", REG_EXTENDED | REG_NOSUB);
|
||||
if (ec != 0) {
|
||||
return ec;
|
||||
}
|
||||
return regexec(&re, str.c_str(), 0, nullptr, 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#include <regex.h>
|
||||
#include <string>
|
||||
int main() {
|
||||
std::string str = "test0159";
|
||||
regex_t re;
|
||||
int ec = regcomp(&re, "^[a-z]+[0-9]+$", REG_EXTENDED | REG_NOSUB);
|
||||
if (ec != 0) {
|
||||
return ec;
|
||||
}
|
||||
int ret = regexec(&re, str.c_str(), 0, nullptr, 0) ? -1 : 0;
|
||||
regfree(&re);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <regex>
|
||||
#include <string>
|
||||
int main() {
|
||||
const std::string str = "test0159";
|
||||
std::regex re;
|
||||
re = std::regex("^[a-z]+[0-9]+$",
|
||||
std::regex_constants::extended | std::regex_constants::nosubs);
|
||||
return std::regex_search(str, re) ? 0 : -1;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#include <chrono>
|
||||
|
||||
int main() {
|
||||
typedef std::chrono::steady_clock Clock;
|
||||
Clock::time_point tp = Clock::now();
|
||||
((void)tp);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#define HAVE_THREAD_SAFETY_ATTRIBUTES
|
||||
#include "../src/mutex.h"
|
||||
|
||||
int main() {}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef BENCHMARK_BENCHMARK_H_
|
||||
#define BENCHMARK_BENCHMARK_H_
|
||||
|
||||
#include "benchmark_api.h"
|
||||
#include "macros.h"
|
||||
#include "reporter.h"
|
||||
|
||||
#endif // BENCHMARK_BENCHMARK_H_
|
||||
@@ -1,864 +0,0 @@
|
||||
// Support for registering benchmarks for functions.
|
||||
|
||||
/* Example usage:
|
||||
// Define a function that executes the code to be measured a
|
||||
// specified number of times:
|
||||
static void BM_StringCreation(benchmark::State& state) {
|
||||
while (state.KeepRunning())
|
||||
std::string empty_string;
|
||||
}
|
||||
|
||||
// Register the function as a benchmark
|
||||
BENCHMARK(BM_StringCreation);
|
||||
|
||||
// Define another benchmark
|
||||
static void BM_StringCopy(benchmark::State& state) {
|
||||
std::string x = "hello";
|
||||
while (state.KeepRunning())
|
||||
std::string copy(x);
|
||||
}
|
||||
BENCHMARK(BM_StringCopy);
|
||||
|
||||
// Augment the main() program to invoke benchmarks if specified
|
||||
// via the --benchmarks command line flag. E.g.,
|
||||
// my_unittest --benchmark_filter=all
|
||||
// my_unittest --benchmark_filter=BM_StringCreation
|
||||
// my_unittest --benchmark_filter=String
|
||||
// my_unittest --benchmark_filter='Copy|Creation'
|
||||
int main(int argc, char** argv) {
|
||||
benchmark::Initialize(&argc, argv);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sometimes a family of microbenchmarks can be implemented with
|
||||
// just one routine that takes an extra argument to specify which
|
||||
// one of the family of benchmarks to run. For example, the following
|
||||
// code defines a family of microbenchmarks for measuring the speed
|
||||
// of memcpy() calls of different lengths:
|
||||
|
||||
static void BM_memcpy(benchmark::State& state) {
|
||||
char* src = new char[state.range(0)]; char* dst = new char[state.range(0)];
|
||||
memset(src, 'x', state.range(0));
|
||||
while (state.KeepRunning())
|
||||
memcpy(dst, src, state.range(0));
|
||||
state.SetBytesProcessed(int64_t(state.iterations()) *
|
||||
int64_t(state.range(0)));
|
||||
delete[] src; delete[] dst;
|
||||
}
|
||||
BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1<<10)->Arg(8<<10);
|
||||
|
||||
// The preceding code is quite repetitive, and can be replaced with the
|
||||
// following short-hand. The following invocation will pick a few
|
||||
// appropriate arguments in the specified range and will generate a
|
||||
// microbenchmark for each such argument.
|
||||
BENCHMARK(BM_memcpy)->Range(8, 8<<10);
|
||||
|
||||
// You might have a microbenchmark that depends on two inputs. For
|
||||
// example, the following code defines a family of microbenchmarks for
|
||||
// measuring the speed of set insertion.
|
||||
static void BM_SetInsert(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
set<int> data = ConstructRandomSet(state.range(0));
|
||||
state.ResumeTiming();
|
||||
for (int j = 0; j < state.range(1); ++j)
|
||||
data.insert(RandomNumber());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_SetInsert)
|
||||
->Args({1<<10, 1})
|
||||
->Args({1<<10, 8})
|
||||
->Args({1<<10, 64})
|
||||
->Args({1<<10, 512})
|
||||
->Args({8<<10, 1})
|
||||
->Args({8<<10, 8})
|
||||
->Args({8<<10, 64})
|
||||
->Args({8<<10, 512});
|
||||
|
||||
// The preceding code is quite repetitive, and can be replaced with
|
||||
// the following short-hand. The following macro will pick a few
|
||||
// appropriate arguments in the product of the two specified ranges
|
||||
// and will generate a microbenchmark for each such pair.
|
||||
BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {1, 512}});
|
||||
|
||||
// For more complex patterns of inputs, passing a custom function
|
||||
// to Apply allows programmatic specification of an
|
||||
// arbitrary set of arguments to run the microbenchmark on.
|
||||
// The following example enumerates a dense range on
|
||||
// one parameter, and a sparse range on the second.
|
||||
static void CustomArguments(benchmark::internal::Benchmark* b) {
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
for (int j = 32; j <= 1024*1024; j *= 8)
|
||||
b->Args({i, j});
|
||||
}
|
||||
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
|
||||
|
||||
// Templated microbenchmarks work the same way:
|
||||
// Produce then consume 'size' messages 'iters' times
|
||||
// Measures throughput in the absence of multiprogramming.
|
||||
template <class Q> int BM_Sequential(benchmark::State& state) {
|
||||
Q q;
|
||||
typename Q::value_type v;
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = state.range(0); i--; )
|
||||
q.push(v);
|
||||
for (int e = state.range(0); e--; )
|
||||
q.Wait(&v);
|
||||
}
|
||||
// actually messages, not bytes:
|
||||
state.SetBytesProcessed(
|
||||
static_cast<int64_t>(state.iterations())*state.range(0));
|
||||
}
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
|
||||
|
||||
Use `Benchmark::MinTime(double t)` to set the minimum time used to run the
|
||||
benchmark. This option overrides the `benchmark_min_time` flag.
|
||||
|
||||
void BM_test(benchmark::State& state) {
|
||||
... body ...
|
||||
}
|
||||
BENCHMARK(BM_test)->MinTime(2.0); // Run for at least 2 seconds.
|
||||
|
||||
In a multithreaded test, it is guaranteed that none of the threads will start
|
||||
until all have called KeepRunning, and all will have finished before KeepRunning
|
||||
returns false. As such, any global setup or teardown you want to do can be
|
||||
wrapped in a check against the thread index:
|
||||
|
||||
static void BM_MultiThreaded(benchmark::State& state) {
|
||||
if (state.thread_index == 0) {
|
||||
// Setup code here.
|
||||
}
|
||||
while (state.KeepRunning()) {
|
||||
// Run the test as normal.
|
||||
}
|
||||
if (state.thread_index == 0) {
|
||||
// Teardown code here.
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_MultiThreaded)->Threads(4);
|
||||
|
||||
|
||||
If a benchmark runs a few milliseconds it may be hard to visually compare the
|
||||
measured times, since the output data is given in nanoseconds per default. In
|
||||
order to manually set the time unit, you can specify it manually:
|
||||
|
||||
BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
|
||||
*/
|
||||
|
||||
#ifndef BENCHMARK_BENCHMARK_API_H_
|
||||
#define BENCHMARK_BENCHMARK_API_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
#if defined(BENCHMARK_HAS_CXX11)
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
namespace benchmark {
|
||||
class BenchmarkReporter;
|
||||
|
||||
void Initialize(int* argc, char** argv);
|
||||
|
||||
// Generate a list of benchmarks matching the specified --benchmark_filter flag
|
||||
// and if --benchmark_list_tests is specified return after printing the name
|
||||
// of each matching benchmark. Otherwise run each matching benchmark and
|
||||
// report the results.
|
||||
//
|
||||
// The second and third overload use the specified 'console_reporter' and
|
||||
// 'file_reporter' respectively. 'file_reporter' will write to the file
|
||||
// specified
|
||||
// by '--benchmark_output'. If '--benchmark_output' is not given the
|
||||
// 'file_reporter' is ignored.
|
||||
//
|
||||
// RETURNS: The number of matching benchmarks.
|
||||
size_t RunSpecifiedBenchmarks();
|
||||
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter);
|
||||
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter,
|
||||
BenchmarkReporter* file_reporter);
|
||||
|
||||
// If this routine is called, peak memory allocation past this point in the
|
||||
// benchmark is reported at the end of the benchmark report line. (It is
|
||||
// computed by running the benchmark once with a single iteration and a memory
|
||||
// tracer.)
|
||||
// TODO(dominic)
|
||||
// void MemoryUsage();
|
||||
|
||||
namespace internal {
|
||||
class Benchmark;
|
||||
class BenchmarkImp;
|
||||
class BenchmarkFamilies;
|
||||
|
||||
template <class T>
|
||||
struct Voider {
|
||||
typedef void type;
|
||||
};
|
||||
|
||||
template <class T, class = void>
|
||||
struct EnableIfString {};
|
||||
|
||||
template <class T>
|
||||
struct EnableIfString<T, typename Voider<typename T::basic_string>::type> {
|
||||
typedef int type;
|
||||
};
|
||||
|
||||
void UseCharPointer(char const volatile*);
|
||||
|
||||
// Take ownership of the pointer and register the benchmark. Return the
|
||||
// registered benchmark.
|
||||
Benchmark* RegisterBenchmarkInternal(Benchmark*);
|
||||
|
||||
// Ensure that the standard streams are properly initialized in every TU.
|
||||
int InitializeStreams();
|
||||
BENCHMARK_UNUSED static int stream_init_anchor = InitializeStreams();
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
// The DoNotOptimize(...) function can be used to prevent a value or
|
||||
// expression from being optimized away by the compiler. This function is
|
||||
// intended to add little to no overhead.
|
||||
// See: https://youtu.be/nXaxk27zwlk?t=2441
|
||||
#if defined(__GNUC__)
|
||||
template <class Tp>
|
||||
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
|
||||
asm volatile("" : : "g"(value) : "memory");
|
||||
}
|
||||
// Force the compiler to flush pending writes to global memory. Acts as an
|
||||
// effective read/write barrier
|
||||
inline BENCHMARK_ALWAYS_INLINE void ClobberMemory() {
|
||||
asm volatile("" : : : "memory");
|
||||
}
|
||||
#else
|
||||
template <class Tp>
|
||||
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
|
||||
internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
|
||||
}
|
||||
// FIXME Add ClobberMemory() for non-gnu compilers
|
||||
#endif
|
||||
|
||||
// TimeUnit is passed to a benchmark in order to specify the order of magnitude
|
||||
// for the measured time.
|
||||
enum TimeUnit { kNanosecond, kMicrosecond, kMillisecond };
|
||||
|
||||
// BigO is passed to a benchmark in order to specify the asymptotic
|
||||
// computational
|
||||
// complexity for the benchmark. In case oAuto is selected, complexity will be
|
||||
// calculated automatically to the best fit.
|
||||
enum BigO { oNone, o1, oN, oNSquared, oNCubed, oLogN, oNLogN, oAuto, oLambda };
|
||||
|
||||
// BigOFunc is passed to a benchmark in order to specify the asymptotic
|
||||
// computational complexity for the benchmark.
|
||||
typedef double(BigOFunc)(int);
|
||||
|
||||
namespace internal {
|
||||
class ThreadTimer;
|
||||
class ThreadManager;
|
||||
|
||||
#if defined(BENCHMARK_HAS_CXX11)
|
||||
enum ReportMode : unsigned {
|
||||
#else
|
||||
enum ReportMode {
|
||||
#endif
|
||||
RM_Unspecified, // The mode has not been manually specified
|
||||
RM_Default, // The mode is user-specified as default.
|
||||
RM_ReportAggregatesOnly
|
||||
};
|
||||
}
|
||||
|
||||
// State is passed to a running Benchmark and contains state for the
|
||||
// benchmark to use.
|
||||
class State {
|
||||
public:
|
||||
// Returns true if the benchmark should continue through another iteration.
|
||||
// NOTE: A benchmark may not return from the test until KeepRunning() has
|
||||
// returned false.
|
||||
bool KeepRunning() {
|
||||
if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
|
||||
StartKeepRunning();
|
||||
}
|
||||
bool const res = total_iterations_++ < max_iterations;
|
||||
if (BENCHMARK_BUILTIN_EXPECT(!res, false)) {
|
||||
FinishKeepRunning();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// REQUIRES: timer is running and 'SkipWithError(...)' has not been called
|
||||
// by the current thread.
|
||||
// Stop the benchmark timer. If not called, the timer will be
|
||||
// automatically stopped after KeepRunning() returns false for the first time.
|
||||
//
|
||||
// For threaded benchmarks the PauseTiming() function only pauses the timing
|
||||
// for the current thread.
|
||||
//
|
||||
// NOTE: The "real time" measurement is per-thread. If different threads
|
||||
// report different measurements the largest one is reported.
|
||||
//
|
||||
// NOTE: PauseTiming()/ResumeTiming() are relatively
|
||||
// heavyweight, and so their use should generally be avoided
|
||||
// within each benchmark iteration, if possible.
|
||||
void PauseTiming();
|
||||
|
||||
// REQUIRES: timer is not running and 'SkipWithError(...)' has not been called
|
||||
// by the current thread.
|
||||
// Start the benchmark timer. The timer is NOT running on entrance to the
|
||||
// benchmark function. It begins running after the first call to KeepRunning()
|
||||
//
|
||||
// NOTE: PauseTiming()/ResumeTiming() are relatively
|
||||
// heavyweight, and so their use should generally be avoided
|
||||
// within each benchmark iteration, if possible.
|
||||
void ResumeTiming();
|
||||
|
||||
// REQUIRES: 'SkipWithError(...)' has not been called previously by the
|
||||
// current thread.
|
||||
// Skip any future iterations of the 'KeepRunning()' loop in the current
|
||||
// thread and report an error with the specified 'msg'. After this call
|
||||
// the user may explicitly 'return' from the benchmark.
|
||||
//
|
||||
// For threaded benchmarks only the current thread stops executing and future
|
||||
// calls to `KeepRunning()` will block until all threads have completed
|
||||
// the `KeepRunning()` loop. If multiple threads report an error only the
|
||||
// first error message is used.
|
||||
//
|
||||
// NOTE: Calling 'SkipWithError(...)' does not cause the benchmark to exit
|
||||
// the current scope immediately. If the function is called from within
|
||||
// the 'KeepRunning()' loop the current iteration will finish. It is the users
|
||||
// responsibility to exit the scope as needed.
|
||||
void SkipWithError(const char* msg);
|
||||
|
||||
// REQUIRES: called exactly once per iteration of the KeepRunning loop.
|
||||
// Set the manually measured time for this benchmark iteration, which
|
||||
// is used instead of automatically measured time if UseManualTime() was
|
||||
// specified.
|
||||
//
|
||||
// For threaded benchmarks the final value will be set to the largest
|
||||
// reported values.
|
||||
void SetIterationTime(double seconds);
|
||||
|
||||
// Set the number of bytes processed by the current benchmark
|
||||
// execution. This routine is typically called once at the end of a
|
||||
// throughput oriented benchmark. If this routine is called with a
|
||||
// value > 0, the report is printed in MB/sec instead of nanoseconds
|
||||
// per iteration.
|
||||
//
|
||||
// REQUIRES: a benchmark has exited its KeepRunning loop.
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
void SetBytesProcessed(size_t bytes) { bytes_processed_ = bytes; }
|
||||
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
size_t bytes_processed() const { return bytes_processed_; }
|
||||
|
||||
// If this routine is called with complexity_n > 0 and complexity report is
|
||||
// requested for the
|
||||
// family benchmark, then current benchmark will be part of the computation
|
||||
// and complexity_n will
|
||||
// represent the length of N.
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
void SetComplexityN(int complexity_n) { complexity_n_ = complexity_n; }
|
||||
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
int complexity_length_n() { return complexity_n_; }
|
||||
|
||||
// If this routine is called with items > 0, then an items/s
|
||||
// label is printed on the benchmark report line for the currently
|
||||
// executing benchmark. It is typically called at the end of a processing
|
||||
// benchmark where a processing items/second output is desired.
|
||||
//
|
||||
// REQUIRES: a benchmark has exited its KeepRunning loop.
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
void SetItemsProcessed(size_t items) { items_processed_ = items; }
|
||||
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
size_t items_processed() const { return items_processed_; }
|
||||
|
||||
// If this routine is called, the specified label is printed at the
|
||||
// end of the benchmark report line for the currently executing
|
||||
// benchmark. Example:
|
||||
// static void BM_Compress(benchmark::State& state) {
|
||||
// ...
|
||||
// double compress = input_size / output_size;
|
||||
// state.SetLabel(StringPrintf("compress:%.1f%%", 100.0*compression));
|
||||
// }
|
||||
// Produces output that looks like:
|
||||
// BM_Compress 50 50 14115038 compress:27.3%
|
||||
//
|
||||
// REQUIRES: a benchmark has exited its KeepRunning loop.
|
||||
void SetLabel(const char* label);
|
||||
|
||||
// Allow the use of std::string without actually including <string>.
|
||||
// This function does not participate in overload resolution unless StringType
|
||||
// has the nested typename `basic_string`. This typename should be provided
|
||||
// as an injected class name in the case of std::string.
|
||||
template <class StringType>
|
||||
void SetLabel(StringType const& str,
|
||||
typename internal::EnableIfString<StringType>::type = 1) {
|
||||
this->SetLabel(str.c_str());
|
||||
}
|
||||
|
||||
// Range arguments for this run. CHECKs if the argument has been set.
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
int range(std::size_t pos = 0) const {
|
||||
assert(range_.size() > pos);
|
||||
return range_[pos];
|
||||
}
|
||||
|
||||
BENCHMARK_DEPRECATED_MSG("use 'range(0)' instead")
|
||||
int range_x() const { return range(0); }
|
||||
|
||||
BENCHMARK_DEPRECATED_MSG("use 'range(1)' instead")
|
||||
int range_y() const { return range(1); }
|
||||
|
||||
BENCHMARK_ALWAYS_INLINE
|
||||
size_t iterations() const { return total_iterations_; }
|
||||
|
||||
private:
|
||||
bool started_;
|
||||
bool finished_;
|
||||
size_t total_iterations_;
|
||||
|
||||
std::vector<int> range_;
|
||||
|
||||
size_t bytes_processed_;
|
||||
size_t items_processed_;
|
||||
|
||||
int complexity_n_;
|
||||
|
||||
bool error_occurred_;
|
||||
|
||||
public:
|
||||
// Index of the executing thread. Values from [0, threads).
|
||||
const int thread_index;
|
||||
// Number of threads concurrently executing the benchmark.
|
||||
const int threads;
|
||||
const size_t max_iterations;
|
||||
|
||||
// TODO make me private
|
||||
State(size_t max_iters, const std::vector<int>& ranges, int thread_i,
|
||||
int n_threads, internal::ThreadTimer* timer,
|
||||
internal::ThreadManager* manager);
|
||||
|
||||
private:
|
||||
void StartKeepRunning();
|
||||
void FinishKeepRunning();
|
||||
internal::ThreadTimer* timer_;
|
||||
internal::ThreadManager* manager_;
|
||||
BENCHMARK_DISALLOW_COPY_AND_ASSIGN(State);
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
typedef void(Function)(State&);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Benchmark registration object. The BENCHMARK() macro expands
|
||||
// into an internal::Benchmark* object. Various methods can
|
||||
// be called on this object to change the properties of the benchmark.
|
||||
// Each method returns "this" so that multiple method calls can
|
||||
// chained into one expression.
|
||||
class Benchmark {
|
||||
public:
|
||||
virtual ~Benchmark();
|
||||
|
||||
// Note: the following methods all return "this" so that multiple
|
||||
// method calls can be chained together in one expression.
|
||||
|
||||
// Run this benchmark once with "x" as the extra argument passed
|
||||
// to the function.
|
||||
// REQUIRES: The function passed to the constructor must accept an arg1.
|
||||
Benchmark* Arg(int x);
|
||||
|
||||
// Run this benchmark with the given time unit for the generated output report
|
||||
Benchmark* Unit(TimeUnit unit);
|
||||
|
||||
// Run this benchmark once for a number of values picked from the
|
||||
// range [start..limit]. (start and limit are always picked.)
|
||||
// REQUIRES: The function passed to the constructor must accept an arg1.
|
||||
Benchmark* Range(int start, int limit);
|
||||
|
||||
// Run this benchmark once for all values in the range [start..limit] with
|
||||
// specific step
|
||||
// REQUIRES: The function passed to the constructor must accept an arg1.
|
||||
Benchmark* DenseRange(int start, int limit, int step = 1);
|
||||
|
||||
// Run this benchmark once with "args" as the extra arguments passed
|
||||
// to the function.
|
||||
// REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
|
||||
Benchmark* Args(const std::vector<int>& args);
|
||||
|
||||
// Equivalent to Args({x, y})
|
||||
// NOTE: This is a legacy C++03 interface provided for compatibility only.
|
||||
// New code should use 'Args'.
|
||||
Benchmark* ArgPair(int x, int y) {
|
||||
std::vector<int> args;
|
||||
args.push_back(x);
|
||||
args.push_back(y);
|
||||
return Args(args);
|
||||
}
|
||||
|
||||
// Run this benchmark once for a number of values picked from the
|
||||
// ranges [start..limit]. (starts and limits are always picked.)
|
||||
// REQUIRES: The function passed to the constructor must accept arg1, arg2 ...
|
||||
Benchmark* Ranges(const std::vector<std::pair<int, int> >& ranges);
|
||||
|
||||
// Equivalent to ArgNames({name})
|
||||
Benchmark* ArgName(const std::string& name);
|
||||
|
||||
// Set the argument names to display in the benchmark name. If not called,
|
||||
// only argument values will be shown.
|
||||
Benchmark* ArgNames(const std::vector<std::string>& names);
|
||||
|
||||
// Equivalent to Ranges({{lo1, hi1}, {lo2, hi2}}).
|
||||
// NOTE: This is a legacy C++03 interface provided for compatibility only.
|
||||
// New code should use 'Ranges'.
|
||||
Benchmark* RangePair(int lo1, int hi1, int lo2, int hi2) {
|
||||
std::vector<std::pair<int, int> > ranges;
|
||||
ranges.push_back(std::make_pair(lo1, hi1));
|
||||
ranges.push_back(std::make_pair(lo2, hi2));
|
||||
return Ranges(ranges);
|
||||
}
|
||||
|
||||
// Pass this benchmark object to *func, which can customize
|
||||
// the benchmark by calling various methods like Arg, Args,
|
||||
// Threads, etc.
|
||||
Benchmark* Apply(void (*func)(Benchmark* benchmark));
|
||||
|
||||
// Set the range multiplier for non-dense range. If not called, the range
|
||||
// multiplier kRangeMultiplier will be used.
|
||||
Benchmark* RangeMultiplier(int multiplier);
|
||||
|
||||
// Set the minimum amount of time to use when running this benchmark. This
|
||||
// option overrides the `benchmark_min_time` flag.
|
||||
// REQUIRES: `t > 0`
|
||||
Benchmark* MinTime(double t);
|
||||
|
||||
// Specify the amount of times to repeat this benchmark. This option overrides
|
||||
// the `benchmark_repetitions` flag.
|
||||
// REQUIRES: `n > 0`
|
||||
Benchmark* Repetitions(int n);
|
||||
|
||||
// Specify if each repetition of the benchmark should be reported separately
|
||||
// or if only the final statistics should be reported. If the benchmark
|
||||
// is not repeated then the single result is always reported.
|
||||
Benchmark* ReportAggregatesOnly(bool v = true);
|
||||
|
||||
// If a particular benchmark is I/O bound, runs multiple threads internally or
|
||||
// if for some reason CPU timings are not representative, call this method. If
|
||||
// called, the elapsed time will be used to control how many iterations are
|
||||
// run, and in the printing of items/second or MB/seconds values. If not
|
||||
// called, the cpu time used by the benchmark will be used.
|
||||
Benchmark* UseRealTime();
|
||||
|
||||
// If a benchmark must measure time manually (e.g. if GPU execution time is
|
||||
// being
|
||||
// measured), call this method. If called, each benchmark iteration should
|
||||
// call
|
||||
// SetIterationTime(seconds) to report the measured time, which will be used
|
||||
// to control how many iterations are run, and in the printing of items/second
|
||||
// or MB/second values.
|
||||
Benchmark* UseManualTime();
|
||||
|
||||
// Set the asymptotic computational complexity for the benchmark. If called
|
||||
// the asymptotic computational complexity will be shown on the output.
|
||||
Benchmark* Complexity(BigO complexity = benchmark::oAuto);
|
||||
|
||||
// Set the asymptotic computational complexity for the benchmark. If called
|
||||
// the asymptotic computational complexity will be shown on the output.
|
||||
Benchmark* Complexity(BigOFunc* complexity);
|
||||
|
||||
// Support for running multiple copies of the same benchmark concurrently
|
||||
// in multiple threads. This may be useful when measuring the scaling
|
||||
// of some piece of code.
|
||||
|
||||
// Run one instance of this benchmark concurrently in t threads.
|
||||
Benchmark* Threads(int t);
|
||||
|
||||
// Pick a set of values T from [min_threads,max_threads].
|
||||
// min_threads and max_threads are always included in T. Run this
|
||||
// benchmark once for each value in T. The benchmark run for a
|
||||
// particular value t consists of t threads running the benchmark
|
||||
// function concurrently. For example, consider:
|
||||
// BENCHMARK(Foo)->ThreadRange(1,16);
|
||||
// This will run the following benchmarks:
|
||||
// Foo in 1 thread
|
||||
// Foo in 2 threads
|
||||
// Foo in 4 threads
|
||||
// Foo in 8 threads
|
||||
// Foo in 16 threads
|
||||
Benchmark* ThreadRange(int min_threads, int max_threads);
|
||||
|
||||
// For each value n in the range, run this benchmark once using n threads.
|
||||
// min_threads and max_threads are always included in the range.
|
||||
// stride specifies the increment. E.g. DenseThreadRange(1, 8, 3) starts
|
||||
// a benchmark with 1, 4, 7 and 8 threads.
|
||||
Benchmark* DenseThreadRange(int min_threads, int max_threads, int stride = 1);
|
||||
|
||||
// Equivalent to ThreadRange(NumCPUs(), NumCPUs())
|
||||
Benchmark* ThreadPerCpu();
|
||||
|
||||
virtual void Run(State& state) = 0;
|
||||
|
||||
// Used inside the benchmark implementation
|
||||
struct Instance;
|
||||
|
||||
protected:
|
||||
explicit Benchmark(const char* name);
|
||||
Benchmark(Benchmark const&);
|
||||
void SetName(const char* name);
|
||||
|
||||
int ArgsCnt() const;
|
||||
|
||||
static void AddRange(std::vector<int>* dst, int lo, int hi, int mult);
|
||||
|
||||
private:
|
||||
friend class BenchmarkFamilies;
|
||||
|
||||
std::string name_;
|
||||
ReportMode report_mode_;
|
||||
std::vector<std::string> arg_names_; // Args for all benchmark runs
|
||||
std::vector<std::vector<int> > args_; // Args for all benchmark runs
|
||||
TimeUnit time_unit_;
|
||||
int range_multiplier_;
|
||||
double min_time_;
|
||||
int repetitions_;
|
||||
bool use_real_time_;
|
||||
bool use_manual_time_;
|
||||
BigO complexity_;
|
||||
BigOFunc* complexity_lambda_;
|
||||
std::vector<int> thread_counts_;
|
||||
|
||||
Benchmark& operator=(Benchmark const&);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// Create and register a benchmark with the specified 'name' that invokes
|
||||
// the specified functor 'fn'.
|
||||
//
|
||||
// RETURNS: A pointer to the registered benchmark.
|
||||
internal::Benchmark* RegisterBenchmark(const char* name,
|
||||
internal::Function* fn);
|
||||
|
||||
#if defined(BENCHMARK_HAS_CXX11)
|
||||
template <class Lambda>
|
||||
internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn);
|
||||
#endif
|
||||
|
||||
namespace internal {
|
||||
// The class used to hold all Benchmarks created from static function.
|
||||
// (ie those created using the BENCHMARK(...) macros.
|
||||
class FunctionBenchmark : public Benchmark {
|
||||
public:
|
||||
FunctionBenchmark(const char* name, Function* func)
|
||||
: Benchmark(name), func_(func) {}
|
||||
|
||||
virtual void Run(State& st);
|
||||
|
||||
private:
|
||||
Function* func_;
|
||||
};
|
||||
|
||||
#ifdef BENCHMARK_HAS_CXX11
|
||||
template <class Lambda>
|
||||
class LambdaBenchmark : public Benchmark {
|
||||
public:
|
||||
virtual void Run(State& st) { lambda_(st); }
|
||||
|
||||
private:
|
||||
template <class OLambda>
|
||||
LambdaBenchmark(const char* name, OLambda&& lam)
|
||||
: Benchmark(name), lambda_(std::forward<OLambda>(lam)) {}
|
||||
|
||||
LambdaBenchmark(LambdaBenchmark const&) = delete;
|
||||
|
||||
private:
|
||||
template <class Lam>
|
||||
friend Benchmark* ::benchmark::RegisterBenchmark(const char*, Lam&&);
|
||||
|
||||
Lambda lambda_;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
inline internal::Benchmark* RegisterBenchmark(const char* name,
|
||||
internal::Function* fn) {
|
||||
return internal::RegisterBenchmarkInternal(
|
||||
::new internal::FunctionBenchmark(name, fn));
|
||||
}
|
||||
|
||||
#ifdef BENCHMARK_HAS_CXX11
|
||||
template <class Lambda>
|
||||
internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn) {
|
||||
using BenchType =
|
||||
internal::LambdaBenchmark<typename std::decay<Lambda>::type>;
|
||||
return internal::RegisterBenchmarkInternal(
|
||||
::new BenchType(name, std::forward<Lambda>(fn)));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BENCHMARK_HAS_CXX11) && \
|
||||
(!defined(BENCHMARK_GCC_VERSION) || BENCHMARK_GCC_VERSION >= 409)
|
||||
template <class Lambda, class... Args>
|
||||
internal::Benchmark* RegisterBenchmark(const char* name, Lambda&& fn,
|
||||
Args&&... args) {
|
||||
return benchmark::RegisterBenchmark(
|
||||
name, [=](benchmark::State& st) { fn(st, args...); });
|
||||
}
|
||||
#else
|
||||
#define BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||
#endif
|
||||
|
||||
// The base class for all fixture tests.
|
||||
class Fixture : public internal::Benchmark {
|
||||
public:
|
||||
Fixture() : internal::Benchmark("") {}
|
||||
|
||||
virtual void Run(State& st) {
|
||||
this->SetUp(st);
|
||||
this->BenchmarkCase(st);
|
||||
this->TearDown(st);
|
||||
}
|
||||
|
||||
// These will be deprecated ...
|
||||
virtual void SetUp(const State&) {}
|
||||
virtual void TearDown(const State&) {}
|
||||
// ... In favor of these.
|
||||
virtual void SetUp(State& st) { SetUp(const_cast<const State&>(st)); }
|
||||
virtual void TearDown(State& st) { TearDown(const_cast<const State&>(st)); }
|
||||
|
||||
protected:
|
||||
virtual void BenchmarkCase(State&) = 0;
|
||||
};
|
||||
|
||||
} // end namespace benchmark
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Macro to register benchmarks
|
||||
|
||||
// Check that __COUNTER__ is defined and that __COUNTER__ increases by 1
|
||||
// every time it is expanded. X + 1 == X + 0 is used in case X is defined to be
|
||||
// empty. If X is empty the expression becomes (+1 == +0).
|
||||
#if defined(__COUNTER__) && (__COUNTER__ + 1 == __COUNTER__ + 0)
|
||||
#define BENCHMARK_PRIVATE_UNIQUE_ID __COUNTER__
|
||||
#else
|
||||
#define BENCHMARK_PRIVATE_UNIQUE_ID __LINE__
|
||||
#endif
|
||||
|
||||
// Helpers for generating unique variable names
|
||||
#define BENCHMARK_PRIVATE_NAME(n) \
|
||||
BENCHMARK_PRIVATE_CONCAT(_benchmark_, BENCHMARK_PRIVATE_UNIQUE_ID, n)
|
||||
#define BENCHMARK_PRIVATE_CONCAT(a, b, c) BENCHMARK_PRIVATE_CONCAT2(a, b, c)
|
||||
#define BENCHMARK_PRIVATE_CONCAT2(a, b, c) a##b##c
|
||||
|
||||
#define BENCHMARK_PRIVATE_DECLARE(n) \
|
||||
static ::benchmark::internal::Benchmark* BENCHMARK_PRIVATE_NAME(n) \
|
||||
BENCHMARK_UNUSED
|
||||
|
||||
#define BENCHMARK(n) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
new ::benchmark::internal::FunctionBenchmark(#n, n)))
|
||||
|
||||
// Old-style macros
|
||||
#define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a))
|
||||
#define BENCHMARK_WITH_ARG2(n, a1, a2) BENCHMARK(n)->Args({(a1), (a2)})
|
||||
#define BENCHMARK_WITH_UNIT(n, t) BENCHMARK(n)->Unit((t))
|
||||
#define BENCHMARK_RANGE(n, lo, hi) BENCHMARK(n)->Range((lo), (hi))
|
||||
#define BENCHMARK_RANGE2(n, l1, h1, l2, h2) \
|
||||
BENCHMARK(n)->RangePair({{(l1), (h1)}, {(l2), (h2)}})
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
|
||||
// Register a benchmark which invokes the function specified by `func`
|
||||
// with the additional arguments specified by `...`.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// template <class ...ExtraArgs>`
|
||||
// void BM_takes_args(benchmark::State& state, ExtraArgs&&... extra_args) {
|
||||
// [...]
|
||||
//}
|
||||
// /* Registers a benchmark named "BM_takes_args/int_string_test` */
|
||||
// BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc"));
|
||||
#define BENCHMARK_CAPTURE(func, test_case_name, ...) \
|
||||
BENCHMARK_PRIVATE_DECLARE(func) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
new ::benchmark::internal::FunctionBenchmark( \
|
||||
#func "/" #test_case_name, \
|
||||
[](::benchmark::State& st) { func(st, __VA_ARGS__); })))
|
||||
|
||||
#endif // __cplusplus >= 11
|
||||
|
||||
// This will register a benchmark for a templatized function. For example:
|
||||
//
|
||||
// template<int arg>
|
||||
// void BM_Foo(int iters);
|
||||
//
|
||||
// BENCHMARK_TEMPLATE(BM_Foo, 1);
|
||||
//
|
||||
// will register BM_Foo<1> as a benchmark.
|
||||
#define BENCHMARK_TEMPLATE1(n, a) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
new ::benchmark::internal::FunctionBenchmark(#n "<" #a ">", n<a>)))
|
||||
|
||||
#define BENCHMARK_TEMPLATE2(n, a, b) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
new ::benchmark::internal::FunctionBenchmark(#n "<" #a "," #b ">", \
|
||||
n<a, b>)))
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#define BENCHMARK_TEMPLATE(n, ...) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
new ::benchmark::internal::FunctionBenchmark( \
|
||||
#n "<" #__VA_ARGS__ ">", n<__VA_ARGS__>)))
|
||||
#else
|
||||
#define BENCHMARK_TEMPLATE(n, a) BENCHMARK_TEMPLATE1(n, a)
|
||||
#endif
|
||||
|
||||
#define BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
|
||||
class BaseClass##_##Method##_Benchmark : public BaseClass { \
|
||||
public: \
|
||||
BaseClass##_##Method##_Benchmark() : BaseClass() { \
|
||||
this->SetName(#BaseClass "/" #Method); \
|
||||
} \
|
||||
\
|
||||
protected: \
|
||||
virtual void BenchmarkCase(::benchmark::State&); \
|
||||
};
|
||||
|
||||
#define BENCHMARK_DEFINE_F(BaseClass, Method) \
|
||||
BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
|
||||
void BaseClass##_##Method##_Benchmark::BenchmarkCase
|
||||
|
||||
#define BENCHMARK_REGISTER_F(BaseClass, Method) \
|
||||
BENCHMARK_PRIVATE_REGISTER_F(BaseClass##_##Method##_Benchmark)
|
||||
|
||||
#define BENCHMARK_PRIVATE_REGISTER_F(TestName) \
|
||||
BENCHMARK_PRIVATE_DECLARE(TestName) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal(new TestName()))
|
||||
|
||||
// This macro will define and register a benchmark within a fixture class.
|
||||
#define BENCHMARK_F(BaseClass, Method) \
|
||||
BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
|
||||
BENCHMARK_REGISTER_F(BaseClass, Method); \
|
||||
void BaseClass##_##Method##_Benchmark::BenchmarkCase
|
||||
|
||||
// Helper macro to create a main routine in a test that runs the benchmarks
|
||||
#define BENCHMARK_MAIN() \
|
||||
int main(int argc, char** argv) { \
|
||||
::benchmark::Initialize(&argc, argv); \
|
||||
::benchmark::RunSpecifiedBenchmarks(); \
|
||||
}
|
||||
|
||||
#endif // BENCHMARK_BENCHMARK_API_H_
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef BENCHMARK_MACROS_H_
|
||||
#define BENCHMARK_MACROS_H_
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#define BENCHMARK_HAS_CXX11
|
||||
#endif
|
||||
|
||||
#ifndef BENCHMARK_HAS_CXX11
|
||||
#define BENCHMARK_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
TypeName& operator=(const TypeName&)
|
||||
#else
|
||||
#define BENCHMARK_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName& operator=(const TypeName&) = delete
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define BENCHMARK_UNUSED __attribute__((unused))
|
||||
#define BENCHMARK_ALWAYS_INLINE __attribute__((always_inline))
|
||||
#define BENCHMARK_NOEXCEPT noexcept
|
||||
#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
|
||||
#elif defined(_MSC_VER) && !defined(__clang__)
|
||||
#define BENCHMARK_UNUSED
|
||||
#define BENCHMARK_ALWAYS_INLINE __forceinline
|
||||
#if _MSC_VER >= 1900
|
||||
#define BENCHMARK_NOEXCEPT noexcept
|
||||
#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
|
||||
#else
|
||||
#define BENCHMARK_NOEXCEPT
|
||||
#define BENCHMARK_NOEXCEPT_OP(x)
|
||||
#endif
|
||||
#define __func__ __FUNCTION__
|
||||
#else
|
||||
#define BENCHMARK_UNUSED
|
||||
#define BENCHMARK_ALWAYS_INLINE
|
||||
#define BENCHMARK_NOEXCEPT
|
||||
#define BENCHMARK_NOEXCEPT_OP(x)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define BENCHMARK_BUILTIN_EXPECT(x, y) __builtin_expect(x, y)
|
||||
#define BENCHMARK_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
|
||||
#else
|
||||
#define BENCHMARK_BUILTIN_EXPECT(x, y) x
|
||||
#define BENCHMARK_DEPRECATED_MSG(msg)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#define BENCHMARK_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
#endif
|
||||
|
||||
#endif // BENCHMARK_MACROS_H_
|
||||
@@ -1,219 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef BENCHMARK_REPORTER_H_
|
||||
#define BENCHMARK_REPORTER_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "benchmark_api.h" // For forward declaration of BenchmarkReporter
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
// Interface for custom benchmark result printers.
|
||||
// By default, benchmark reports are printed to stdout. However an application
|
||||
// can control the destination of the reports by calling
|
||||
// RunSpecifiedBenchmarks and passing it a custom reporter object.
|
||||
// The reporter object must implement the following interface.
|
||||
class BenchmarkReporter {
|
||||
public:
|
||||
struct Context {
|
||||
int num_cpus;
|
||||
double mhz_per_cpu;
|
||||
bool cpu_scaling_enabled;
|
||||
|
||||
// The number of chars in the longest benchmark name.
|
||||
size_t name_field_width;
|
||||
};
|
||||
|
||||
struct Run {
|
||||
Run()
|
||||
: error_occurred(false),
|
||||
iterations(1),
|
||||
time_unit(kNanosecond),
|
||||
real_accumulated_time(0),
|
||||
cpu_accumulated_time(0),
|
||||
bytes_per_second(0),
|
||||
items_per_second(0),
|
||||
max_heapbytes_used(0),
|
||||
complexity(oNone),
|
||||
complexity_lambda(),
|
||||
complexity_n(0),
|
||||
report_big_o(false),
|
||||
report_rms(false) {}
|
||||
|
||||
std::string benchmark_name;
|
||||
std::string report_label; // Empty if not set by benchmark.
|
||||
bool error_occurred;
|
||||
std::string error_message;
|
||||
|
||||
int64_t iterations;
|
||||
TimeUnit time_unit;
|
||||
double real_accumulated_time;
|
||||
double cpu_accumulated_time;
|
||||
|
||||
// Return a value representing the real time per iteration in the unit
|
||||
// specified by 'time_unit'.
|
||||
// NOTE: If 'iterations' is zero the returned value represents the
|
||||
// accumulated time.
|
||||
double GetAdjustedRealTime() const;
|
||||
|
||||
// Return a value representing the cpu time per iteration in the unit
|
||||
// specified by 'time_unit'.
|
||||
// NOTE: If 'iterations' is zero the returned value represents the
|
||||
// accumulated time.
|
||||
double GetAdjustedCPUTime() const;
|
||||
|
||||
// Zero if not set by benchmark.
|
||||
double bytes_per_second;
|
||||
double items_per_second;
|
||||
|
||||
// This is set to 0.0 if memory tracing is not enabled.
|
||||
double max_heapbytes_used;
|
||||
|
||||
// Keep track of arguments to compute asymptotic complexity
|
||||
BigO complexity;
|
||||
BigOFunc* complexity_lambda;
|
||||
int complexity_n;
|
||||
|
||||
// Inform print function whether the current run is a complexity report
|
||||
bool report_big_o;
|
||||
bool report_rms;
|
||||
};
|
||||
|
||||
// Construct a BenchmarkReporter with the output stream set to 'std::cout'
|
||||
// and the error stream set to 'std::cerr'
|
||||
BenchmarkReporter();
|
||||
|
||||
// Called once for every suite of benchmarks run.
|
||||
// The parameter "context" contains information that the
|
||||
// reporter may wish to use when generating its report, for example the
|
||||
// platform under which the benchmarks are running. The benchmark run is
|
||||
// never started if this function returns false, allowing the reporter
|
||||
// to skip runs based on the context information.
|
||||
virtual bool ReportContext(const Context& context) = 0;
|
||||
|
||||
// Called once for each group of benchmark runs, gives information about
|
||||
// cpu-time and heap memory usage during the benchmark run. If the group
|
||||
// of runs contained more than two entries then 'report' contains additional
|
||||
// elements representing the mean and standard deviation of those runs.
|
||||
// Additionally if this group of runs was the last in a family of benchmarks
|
||||
// 'reports' contains additional entries representing the asymptotic
|
||||
// complexity and RMS of that benchmark family.
|
||||
virtual void ReportRuns(const std::vector<Run>& report) = 0;
|
||||
|
||||
// Called once and only once after ever group of benchmarks is run and
|
||||
// reported.
|
||||
virtual void Finalize() {}
|
||||
|
||||
// REQUIRES: The object referenced by 'out' is valid for the lifetime
|
||||
// of the reporter.
|
||||
void SetOutputStream(std::ostream* out) {
|
||||
assert(out);
|
||||
output_stream_ = out;
|
||||
}
|
||||
|
||||
// REQUIRES: The object referenced by 'err' is valid for the lifetime
|
||||
// of the reporter.
|
||||
void SetErrorStream(std::ostream* err) {
|
||||
assert(err);
|
||||
error_stream_ = err;
|
||||
}
|
||||
|
||||
std::ostream& GetOutputStream() const { return *output_stream_; }
|
||||
|
||||
std::ostream& GetErrorStream() const { return *error_stream_; }
|
||||
|
||||
virtual ~BenchmarkReporter();
|
||||
|
||||
// Write a human readable string to 'out' representing the specified
|
||||
// 'context'.
|
||||
// REQUIRES: 'out' is non-null.
|
||||
static void PrintBasicContext(std::ostream* out, Context const& context);
|
||||
|
||||
private:
|
||||
std::ostream* output_stream_;
|
||||
std::ostream* error_stream_;
|
||||
};
|
||||
|
||||
// Simple reporter that outputs benchmark data to the console. This is the
|
||||
// default reporter used by RunSpecifiedBenchmarks().
|
||||
class ConsoleReporter : public BenchmarkReporter {
|
||||
public:
|
||||
enum OutputOptions { OO_None, OO_Color };
|
||||
explicit ConsoleReporter(OutputOptions color_output = OO_Color)
|
||||
: name_field_width_(0), color_output_(color_output == OO_Color) {}
|
||||
|
||||
virtual bool ReportContext(const Context& context);
|
||||
virtual void ReportRuns(const std::vector<Run>& reports);
|
||||
|
||||
protected:
|
||||
virtual void PrintRunData(const Run& report);
|
||||
size_t name_field_width_;
|
||||
|
||||
private:
|
||||
bool color_output_;
|
||||
};
|
||||
|
||||
class JSONReporter : public BenchmarkReporter {
|
||||
public:
|
||||
JSONReporter() : first_report_(true) {}
|
||||
virtual bool ReportContext(const Context& context);
|
||||
virtual void ReportRuns(const std::vector<Run>& reports);
|
||||
virtual void Finalize();
|
||||
|
||||
private:
|
||||
void PrintRunData(const Run& report);
|
||||
|
||||
bool first_report_;
|
||||
};
|
||||
|
||||
class CSVReporter : public BenchmarkReporter {
|
||||
public:
|
||||
virtual bool ReportContext(const Context& context);
|
||||
virtual void ReportRuns(const std::vector<Run>& reports);
|
||||
|
||||
private:
|
||||
void PrintRunData(const Run& report);
|
||||
};
|
||||
|
||||
inline const char* GetTimeUnitString(TimeUnit unit) {
|
||||
switch (unit) {
|
||||
case kMillisecond:
|
||||
return "ms";
|
||||
case kMicrosecond:
|
||||
return "us";
|
||||
case kNanosecond:
|
||||
default:
|
||||
return "ns";
|
||||
}
|
||||
}
|
||||
|
||||
inline double GetTimeUnitMultiplier(TimeUnit unit) {
|
||||
switch (unit) {
|
||||
case kMillisecond:
|
||||
return 1e3;
|
||||
case kMicrosecond:
|
||||
return 1e6;
|
||||
case kNanosecond:
|
||||
default:
|
||||
return 1e9;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
#endif // BENCHMARK_REPORTER_H_
|
||||
@@ -1,320 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
try:
|
||||
import winreg
|
||||
except ImportError:
|
||||
import _winreg as winreg
|
||||
try:
|
||||
import urllib.request as request
|
||||
except ImportError:
|
||||
import urllib as request
|
||||
try:
|
||||
import urllib.parse as parse
|
||||
except ImportError:
|
||||
import urlparse as parse
|
||||
|
||||
class EmptyLogger(object):
|
||||
'''
|
||||
Provides an implementation that performs no logging
|
||||
'''
|
||||
def debug(self, *k, **kw):
|
||||
pass
|
||||
def info(self, *k, **kw):
|
||||
pass
|
||||
def warn(self, *k, **kw):
|
||||
pass
|
||||
def error(self, *k, **kw):
|
||||
pass
|
||||
def critical(self, *k, **kw):
|
||||
pass
|
||||
def setLevel(self, *k, **kw):
|
||||
pass
|
||||
|
||||
urls = (
|
||||
'http://downloads.sourceforge.net/project/mingw-w64/Toolchains%20'
|
||||
'targetting%20Win32/Personal%20Builds/mingw-builds/installer/'
|
||||
'repository.txt',
|
||||
'http://downloads.sourceforge.net/project/mingwbuilds/host-windows/'
|
||||
'repository.txt'
|
||||
)
|
||||
'''
|
||||
A list of mingw-build repositories
|
||||
'''
|
||||
|
||||
def repository(urls = urls, log = EmptyLogger()):
|
||||
'''
|
||||
Downloads and parse mingw-build repository files and parses them
|
||||
'''
|
||||
log.info('getting mingw-builds repository')
|
||||
versions = {}
|
||||
re_sourceforge = re.compile(r'http://sourceforge.net/projects/([^/]+)/files')
|
||||
re_sub = r'http://downloads.sourceforge.net/project/\1'
|
||||
for url in urls:
|
||||
log.debug(' - requesting: %s', url)
|
||||
socket = request.urlopen(url)
|
||||
repo = socket.read()
|
||||
if not isinstance(repo, str):
|
||||
repo = repo.decode();
|
||||
socket.close()
|
||||
for entry in repo.split('\n')[:-1]:
|
||||
value = entry.split('|')
|
||||
version = tuple([int(n) for n in value[0].strip().split('.')])
|
||||
version = versions.setdefault(version, {})
|
||||
arch = value[1].strip()
|
||||
if arch == 'x32':
|
||||
arch = 'i686'
|
||||
elif arch == 'x64':
|
||||
arch = 'x86_64'
|
||||
arch = version.setdefault(arch, {})
|
||||
threading = arch.setdefault(value[2].strip(), {})
|
||||
exceptions = threading.setdefault(value[3].strip(), {})
|
||||
revision = exceptions.setdefault(int(value[4].strip()[3:]),
|
||||
re_sourceforge.sub(re_sub, value[5].strip()))
|
||||
return versions
|
||||
|
||||
def find_in_path(file, path=None):
|
||||
'''
|
||||
Attempts to find an executable in the path
|
||||
'''
|
||||
if platform.system() == 'Windows':
|
||||
file += '.exe'
|
||||
if path is None:
|
||||
path = os.environ.get('PATH', '')
|
||||
if type(path) is type(''):
|
||||
path = path.split(os.pathsep)
|
||||
return list(filter(os.path.exists,
|
||||
map(lambda dir, file=file: os.path.join(dir, file), path)))
|
||||
|
||||
def find_7zip(log = EmptyLogger()):
|
||||
'''
|
||||
Attempts to find 7zip for unpacking the mingw-build archives
|
||||
'''
|
||||
log.info('finding 7zip')
|
||||
path = find_in_path('7z')
|
||||
if not path:
|
||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\7-Zip')
|
||||
path, _ = winreg.QueryValueEx(key, 'Path')
|
||||
path = [os.path.join(path, '7z.exe')]
|
||||
log.debug('found \'%s\'', path[0])
|
||||
return path[0]
|
||||
|
||||
find_7zip()
|
||||
|
||||
def unpack(archive, location, log = EmptyLogger()):
|
||||
'''
|
||||
Unpacks a mingw-builds archive
|
||||
'''
|
||||
sevenzip = find_7zip(log)
|
||||
log.info('unpacking %s', os.path.basename(archive))
|
||||
cmd = [sevenzip, 'x', archive, '-o' + location, '-y']
|
||||
log.debug(' - %r', cmd)
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
subprocess.check_call(cmd, stdout = devnull)
|
||||
|
||||
def download(url, location, log = EmptyLogger()):
|
||||
'''
|
||||
Downloads and unpacks a mingw-builds archive
|
||||
'''
|
||||
log.info('downloading MinGW')
|
||||
log.debug(' - url: %s', url)
|
||||
log.debug(' - location: %s', location)
|
||||
|
||||
re_content = re.compile(r'attachment;[ \t]*filename=(")?([^"]*)(")?[\r\n]*')
|
||||
|
||||
stream = request.urlopen(url)
|
||||
try:
|
||||
content = stream.getheader('Content-Disposition') or ''
|
||||
except AttributeError:
|
||||
content = stream.headers.getheader('Content-Disposition') or ''
|
||||
matches = re_content.match(content)
|
||||
if matches:
|
||||
filename = matches.group(2)
|
||||
else:
|
||||
parsed = parse.urlparse(stream.geturl())
|
||||
filename = os.path.basename(parsed.path)
|
||||
|
||||
try:
|
||||
os.makedirs(location)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST and os.path.isdir(location):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
archive = os.path.join(location, filename)
|
||||
with open(archive, 'wb') as out:
|
||||
while True:
|
||||
buf = stream.read(1024)
|
||||
if not buf:
|
||||
break
|
||||
out.write(buf)
|
||||
unpack(archive, location, log = log)
|
||||
os.remove(archive)
|
||||
|
||||
possible = os.path.join(location, 'mingw64')
|
||||
if not os.path.exists(possible):
|
||||
possible = os.path.join(location, 'mingw32')
|
||||
if not os.path.exists(possible):
|
||||
raise ValueError('Failed to find unpacked MinGW: ' + possible)
|
||||
return possible
|
||||
|
||||
def root(location = None, arch = None, version = None, threading = None,
|
||||
exceptions = None, revision = None, log = EmptyLogger()):
|
||||
'''
|
||||
Returns the root folder of a specific version of the mingw-builds variant
|
||||
of gcc. Will download the compiler if needed
|
||||
'''
|
||||
|
||||
# Get the repository if we don't have all the information
|
||||
if not (arch and version and threading and exceptions and revision):
|
||||
versions = repository(log = log)
|
||||
|
||||
# Determine some defaults
|
||||
version = version or max(versions.keys())
|
||||
if not arch:
|
||||
arch = platform.machine().lower()
|
||||
if arch == 'x86':
|
||||
arch = 'i686'
|
||||
elif arch == 'amd64':
|
||||
arch = 'x86_64'
|
||||
if not threading:
|
||||
keys = versions[version][arch].keys()
|
||||
if 'posix' in keys:
|
||||
threading = 'posix'
|
||||
elif 'win32' in keys:
|
||||
threading = 'win32'
|
||||
else:
|
||||
threading = keys[0]
|
||||
if not exceptions:
|
||||
keys = versions[version][arch][threading].keys()
|
||||
if 'seh' in keys:
|
||||
exceptions = 'seh'
|
||||
elif 'sjlj' in keys:
|
||||
exceptions = 'sjlj'
|
||||
else:
|
||||
exceptions = keys[0]
|
||||
if revision == None:
|
||||
revision = max(versions[version][arch][threading][exceptions].keys())
|
||||
if not location:
|
||||
location = os.path.join(tempfile.gettempdir(), 'mingw-builds')
|
||||
|
||||
# Get the download url
|
||||
url = versions[version][arch][threading][exceptions][revision]
|
||||
|
||||
# Tell the user whatzzup
|
||||
log.info('finding MinGW %s', '.'.join(str(v) for v in version))
|
||||
log.debug(' - arch: %s', arch)
|
||||
log.debug(' - threading: %s', threading)
|
||||
log.debug(' - exceptions: %s', exceptions)
|
||||
log.debug(' - revision: %s', revision)
|
||||
log.debug(' - url: %s', url)
|
||||
|
||||
# Store each specific revision differently
|
||||
slug = '{version}-{arch}-{threading}-{exceptions}-rev{revision}'
|
||||
slug = slug.format(
|
||||
version = '.'.join(str(v) for v in version),
|
||||
arch = arch,
|
||||
threading = threading,
|
||||
exceptions = exceptions,
|
||||
revision = revision
|
||||
)
|
||||
if arch == 'x86_64':
|
||||
root_dir = os.path.join(location, slug, 'mingw64')
|
||||
elif arch == 'i686':
|
||||
root_dir = os.path.join(location, slug, 'mingw32')
|
||||
else:
|
||||
raise ValueError('Unknown MinGW arch: ' + arch)
|
||||
|
||||
# Download if needed
|
||||
if not os.path.exists(root_dir):
|
||||
downloaded = download(url, os.path.join(location, slug), log = log)
|
||||
if downloaded != root_dir:
|
||||
raise ValueError('The location of mingw did not match\n%s\n%s'
|
||||
% (downloaded, root_dir))
|
||||
|
||||
return root_dir
|
||||
|
||||
def str2ver(string):
|
||||
'''
|
||||
Converts a version string into a tuple
|
||||
'''
|
||||
try:
|
||||
version = tuple(int(v) for v in string.split('.'))
|
||||
if len(version) is not 3:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
raise argparse.ArgumentTypeError(
|
||||
'please provide a three digit version string')
|
||||
return version
|
||||
|
||||
def main():
|
||||
'''
|
||||
Invoked when the script is run directly by the python interpreter
|
||||
'''
|
||||
parser = argparse.ArgumentParser(
|
||||
description = 'Downloads a specific version of MinGW',
|
||||
formatter_class = argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument('--location',
|
||||
help = 'the location to download the compiler to',
|
||||
default = os.path.join(tempfile.gettempdir(), 'mingw-builds'))
|
||||
parser.add_argument('--arch', required = True, choices = ['i686', 'x86_64'],
|
||||
help = 'the target MinGW architecture string')
|
||||
parser.add_argument('--version', type = str2ver,
|
||||
help = 'the version of GCC to download')
|
||||
parser.add_argument('--threading', choices = ['posix', 'win32'],
|
||||
help = 'the threading type of the compiler')
|
||||
parser.add_argument('--exceptions', choices = ['sjlj', 'seh', 'dwarf'],
|
||||
help = 'the method to throw exceptions')
|
||||
parser.add_argument('--revision', type=int,
|
||||
help = 'the revision of the MinGW release')
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('-v', '--verbose', action='store_true',
|
||||
help='increase the script output verbosity')
|
||||
group.add_argument('-q', '--quiet', action='store_true',
|
||||
help='only print errors and warning')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create the logger
|
||||
logger = logging.getLogger('mingw')
|
||||
handler = logging.StreamHandler()
|
||||
formatter = logging.Formatter('%(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
if args.quiet:
|
||||
logger.setLevel(logging.WARN)
|
||||
if args.verbose:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Get MinGW
|
||||
root_dir = root(location = args.location, arch = args.arch,
|
||||
version = args.version, threading = args.threading,
|
||||
exceptions = args.exceptions, revision = args.revision,
|
||||
log = logger)
|
||||
|
||||
sys.stdout.write('%s\n' % os.path.join(root_dir, 'bin'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except IOError as e:
|
||||
sys.stderr.write('IO error: %s\n' % e)
|
||||
sys.exit(1)
|
||||
except OSError as e:
|
||||
sys.stderr.write('OS error: %s\n' % e)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt as e:
|
||||
sys.stderr.write('Killed\n')
|
||||
sys.exit(1)
|
||||
@@ -1,44 +0,0 @@
|
||||
# Allow the source files to find headers in src/
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
if (DEFINED BENCHMARK_CXX_LINKER_FLAGS)
|
||||
list(APPEND CMAKE_SHARED_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
|
||||
list(APPEND CMAKE_MODULE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
|
||||
endif()
|
||||
|
||||
file(GLOB
|
||||
SOURCE_FILES
|
||||
*.cc
|
||||
${PROJECT_SOURCE_DIR}/include/benchmark/*.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||
|
||||
add_library(benchmark ${SOURCE_FILES})
|
||||
set_target_properties(benchmark PROPERTIES
|
||||
OUTPUT_NAME "benchmark"
|
||||
VERSION ${GENERIC_LIB_VERSION}
|
||||
SOVERSION ${GENERIC_LIB_SOVERSION}
|
||||
)
|
||||
|
||||
# Link threads.
|
||||
target_link_libraries(benchmark ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
# We need extra libraries on Windows
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
target_link_libraries(benchmark Shlwapi)
|
||||
endif()
|
||||
|
||||
# Expose public API
|
||||
target_include_directories(benchmark PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
# Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable)
|
||||
install(
|
||||
TARGETS benchmark
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
COMPONENT library)
|
||||
|
||||
install(
|
||||
DIRECTORY "${PROJECT_SOURCE_DIR}/include/benchmark"
|
||||
DESTINATION include
|
||||
FILES_MATCHING PATTERN "*.*h")
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef BENCHMARK_ARRAYSIZE_H_
|
||||
#define BENCHMARK_ARRAYSIZE_H_
|
||||
|
||||
#include "internal_macros.h"
|
||||
|
||||
namespace benchmark {
|
||||
namespace internal {
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||
// The expression is a compile-time constant, and therefore can be
|
||||
// used in defining new arrays, for example. If you use arraysize on
|
||||
// a pointer by mistake, you will get a compile-time error.
|
||||
//
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
|
||||
// That gcc wants both of these prototypes seems mysterious. VC, for
|
||||
// its part, can't decide which to use (another mystery). Matching of
|
||||
// template overloads: the final frontier.
|
||||
#ifndef COMPILER_MSVC
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(const T (&array)[N]))[N];
|
||||
#endif
|
||||
|
||||
#define arraysize(array) (sizeof(::benchmark::internal::ArraySizeHelper(array)))
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_ARRAYSIZE_H_
|
||||
@@ -1,667 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "benchmark_api_internal.h"
|
||||
#include "internal_macros.h"
|
||||
|
||||
#ifndef BENCHMARK_OS_WINDOWS
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "check.h"
|
||||
#include "colorprint.h"
|
||||
#include "commandlineflags.h"
|
||||
#include "complexity.h"
|
||||
#include "log.h"
|
||||
#include "mutex.h"
|
||||
#include "re.h"
|
||||
#include "stat.h"
|
||||
#include "string_util.h"
|
||||
#include "sysinfo.h"
|
||||
#include "timers.h"
|
||||
|
||||
DEFINE_bool(benchmark_list_tests, false,
|
||||
"Print a list of benchmarks. This option overrides all other "
|
||||
"options.");
|
||||
|
||||
DEFINE_string(benchmark_filter, ".",
|
||||
"A regular expression that specifies the set of benchmarks "
|
||||
"to execute. If this flag is empty, no benchmarks are run. "
|
||||
"If this flag is the string \"all\", all benchmarks linked "
|
||||
"into the process are run.");
|
||||
|
||||
DEFINE_double(benchmark_min_time, 0.5,
|
||||
"Minimum number of seconds we should run benchmark before "
|
||||
"results are considered significant. For cpu-time based "
|
||||
"tests, this is the lower bound on the total cpu time "
|
||||
"used by all threads that make up the test. For real-time "
|
||||
"based tests, this is the lower bound on the elapsed time "
|
||||
"of the benchmark execution, regardless of number of "
|
||||
"threads.");
|
||||
|
||||
DEFINE_int32(benchmark_repetitions, 1,
|
||||
"The number of runs of each benchmark. If greater than 1, the "
|
||||
"mean and standard deviation of the runs will be reported.");
|
||||
|
||||
DEFINE_bool(benchmark_report_aggregates_only, false,
|
||||
"Report the result of each benchmark repetitions. When 'true' is "
|
||||
"specified only the mean, standard deviation, and other statistics "
|
||||
"are reported for repeated benchmarks.");
|
||||
|
||||
DEFINE_string(benchmark_format, "console",
|
||||
"The format to use for console output. Valid values are "
|
||||
"'console', 'json', or 'csv'.");
|
||||
|
||||
DEFINE_string(benchmark_out_format, "json",
|
||||
"The format to use for file output. Valid values are "
|
||||
"'console', 'json', or 'csv'.");
|
||||
|
||||
DEFINE_string(benchmark_out, "", "The file to write additonal output to");
|
||||
|
||||
DEFINE_string(benchmark_color, "auto",
|
||||
"Whether to use colors in the output. Valid values: "
|
||||
"'true'/'yes'/1, 'false'/'no'/0, and 'auto'. 'auto' means to use "
|
||||
"colors if the output is being sent to a terminal and the TERM "
|
||||
"environment variable is set to a terminal type that supports "
|
||||
"colors.");
|
||||
|
||||
DEFINE_int32(v, 0, "The level of verbose logging to output");
|
||||
|
||||
namespace benchmark {
|
||||
namespace internal {
|
||||
|
||||
void UseCharPointer(char const volatile*) {}
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
namespace {
|
||||
|
||||
static const size_t kMaxIterations = 1000000000;
|
||||
|
||||
} // end namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
class ThreadManager {
|
||||
public:
|
||||
ThreadManager(int num_threads)
|
||||
: alive_threads_(num_threads), start_stop_barrier_(num_threads) {}
|
||||
|
||||
Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) {
|
||||
return benchmark_mutex_;
|
||||
}
|
||||
|
||||
bool StartStopBarrier() EXCLUDES(end_cond_mutex_) {
|
||||
return start_stop_barrier_.wait();
|
||||
}
|
||||
|
||||
void NotifyThreadComplete() EXCLUDES(end_cond_mutex_) {
|
||||
start_stop_barrier_.removeThread();
|
||||
if (--alive_threads_ == 0) {
|
||||
MutexLock lock(end_cond_mutex_);
|
||||
end_condition_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void WaitForAllThreads() EXCLUDES(end_cond_mutex_) {
|
||||
MutexLock lock(end_cond_mutex_);
|
||||
end_condition_.wait(lock.native_handle(),
|
||||
[this]() { return alive_threads_ == 0; });
|
||||
}
|
||||
|
||||
public:
|
||||
struct Result {
|
||||
double real_time_used = 0;
|
||||
double cpu_time_used = 0;
|
||||
double manual_time_used = 0;
|
||||
int64_t bytes_processed = 0;
|
||||
int64_t items_processed = 0;
|
||||
int complexity_n = 0;
|
||||
std::string report_label_;
|
||||
std::string error_message_;
|
||||
bool has_error_ = false;
|
||||
};
|
||||
GUARDED_BY(GetBenchmarkMutex()) Result results;
|
||||
|
||||
private:
|
||||
mutable Mutex benchmark_mutex_;
|
||||
std::atomic<int> alive_threads_;
|
||||
Barrier start_stop_barrier_;
|
||||
Mutex end_cond_mutex_;
|
||||
Condition end_condition_;
|
||||
};
|
||||
|
||||
// Timer management class
|
||||
class ThreadTimer {
|
||||
public:
|
||||
ThreadTimer() = default;
|
||||
|
||||
// Called by each thread
|
||||
void StartTimer() {
|
||||
running_ = true;
|
||||
start_real_time_ = ChronoClockNow();
|
||||
start_cpu_time_ = ThreadCPUUsage();
|
||||
}
|
||||
|
||||
// Called by each thread
|
||||
void StopTimer() {
|
||||
CHECK(running_);
|
||||
running_ = false;
|
||||
real_time_used_ += ChronoClockNow() - start_real_time_;
|
||||
cpu_time_used_ += ThreadCPUUsage() - start_cpu_time_;
|
||||
}
|
||||
|
||||
// Called by each thread
|
||||
void SetIterationTime(double seconds) { manual_time_used_ += seconds; }
|
||||
|
||||
bool running() const { return running_; }
|
||||
|
||||
// REQUIRES: timer is not running
|
||||
double real_time_used() {
|
||||
CHECK(!running_);
|
||||
return real_time_used_;
|
||||
}
|
||||
|
||||
// REQUIRES: timer is not running
|
||||
double cpu_time_used() {
|
||||
CHECK(!running_);
|
||||
return cpu_time_used_;
|
||||
}
|
||||
|
||||
// REQUIRES: timer is not running
|
||||
double manual_time_used() {
|
||||
CHECK(!running_);
|
||||
return manual_time_used_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool running_ = false; // Is the timer running
|
||||
double start_real_time_ = 0; // If running_
|
||||
double start_cpu_time_ = 0; // If running_
|
||||
|
||||
// Accumulated time so far (does not contain current slice if running_)
|
||||
double real_time_used_ = 0;
|
||||
double cpu_time_used_ = 0;
|
||||
// Manually set iteration time. User sets this with SetIterationTime(seconds).
|
||||
double manual_time_used_ = 0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
BenchmarkReporter::Run CreateRunReport(
|
||||
const benchmark::internal::Benchmark::Instance& b,
|
||||
const internal::ThreadManager::Result& results, size_t iters,
|
||||
double seconds) {
|
||||
// Create report about this benchmark run.
|
||||
BenchmarkReporter::Run report;
|
||||
|
||||
report.benchmark_name = b.name;
|
||||
report.error_occurred = results.has_error_;
|
||||
report.error_message = results.error_message_;
|
||||
report.report_label = results.report_label_;
|
||||
// Report the total iterations across all threads.
|
||||
report.iterations = static_cast<int64_t>(iters) * b.threads;
|
||||
report.time_unit = b.time_unit;
|
||||
|
||||
if (!report.error_occurred) {
|
||||
double bytes_per_second = 0;
|
||||
if (results.bytes_processed > 0 && seconds > 0.0) {
|
||||
bytes_per_second = (results.bytes_processed / seconds);
|
||||
}
|
||||
double items_per_second = 0;
|
||||
if (results.items_processed > 0 && seconds > 0.0) {
|
||||
items_per_second = (results.items_processed / seconds);
|
||||
}
|
||||
|
||||
if (b.use_manual_time) {
|
||||
report.real_accumulated_time = results.manual_time_used;
|
||||
} else {
|
||||
report.real_accumulated_time = results.real_time_used;
|
||||
}
|
||||
report.cpu_accumulated_time = results.cpu_time_used;
|
||||
report.bytes_per_second = bytes_per_second;
|
||||
report.items_per_second = items_per_second;
|
||||
report.complexity_n = results.complexity_n;
|
||||
report.complexity = b.complexity;
|
||||
report.complexity_lambda = b.complexity_lambda;
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
// Execute one thread of benchmark b for the specified number of iterations.
|
||||
// Adds the stats collected for the thread into *total.
|
||||
void RunInThread(const benchmark::internal::Benchmark::Instance* b,
|
||||
size_t iters, int thread_id,
|
||||
internal::ThreadManager* manager) {
|
||||
internal::ThreadTimer timer;
|
||||
State st(iters, b->arg, thread_id, b->threads, &timer, manager);
|
||||
b->benchmark->Run(st);
|
||||
CHECK(st.iterations() == st.max_iterations)
|
||||
<< "Benchmark returned before State::KeepRunning() returned false!";
|
||||
{
|
||||
MutexLock l(manager->GetBenchmarkMutex());
|
||||
internal::ThreadManager::Result& results = manager->results;
|
||||
results.cpu_time_used += timer.cpu_time_used();
|
||||
results.real_time_used += timer.real_time_used();
|
||||
results.manual_time_used += timer.manual_time_used();
|
||||
results.bytes_processed += st.bytes_processed();
|
||||
results.items_processed += st.items_processed();
|
||||
results.complexity_n += st.complexity_length_n();
|
||||
}
|
||||
manager->NotifyThreadComplete();
|
||||
}
|
||||
|
||||
std::vector<BenchmarkReporter::Run> RunBenchmark(
|
||||
const benchmark::internal::Benchmark::Instance& b,
|
||||
std::vector<BenchmarkReporter::Run>* complexity_reports) {
|
||||
std::vector<BenchmarkReporter::Run> reports; // return value
|
||||
|
||||
size_t iters = 1;
|
||||
std::unique_ptr<internal::ThreadManager> manager;
|
||||
std::vector<std::thread> pool(b.threads - 1);
|
||||
const int repeats =
|
||||
b.repetitions != 0 ? b.repetitions : FLAGS_benchmark_repetitions;
|
||||
const bool report_aggregates_only =
|
||||
repeats != 1 &&
|
||||
(b.report_mode == internal::RM_Unspecified
|
||||
? FLAGS_benchmark_report_aggregates_only
|
||||
: b.report_mode == internal::RM_ReportAggregatesOnly);
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
for (;;) {
|
||||
// Try benchmark
|
||||
VLOG(2) << "Running " << b.name << " for " << iters << "\n";
|
||||
|
||||
manager.reset(new internal::ThreadManager(b.threads));
|
||||
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
|
||||
pool[ti] = std::thread(&RunInThread, &b, iters,
|
||||
static_cast<int>(ti + 1), manager.get());
|
||||
}
|
||||
RunInThread(&b, iters, 0, manager.get());
|
||||
manager->WaitForAllThreads();
|
||||
for (std::thread& thread : pool) thread.join();
|
||||
internal::ThreadManager::Result results;
|
||||
{
|
||||
MutexLock l(manager->GetBenchmarkMutex());
|
||||
results = manager->results;
|
||||
}
|
||||
manager.reset();
|
||||
// Adjust real/manual time stats since they were reported per thread.
|
||||
results.real_time_used /= b.threads;
|
||||
results.manual_time_used /= b.threads;
|
||||
|
||||
VLOG(2) << "Ran in " << results.cpu_time_used << "/"
|
||||
<< results.real_time_used << "\n";
|
||||
|
||||
// Base decisions off of real time if requested by this benchmark.
|
||||
double seconds = results.cpu_time_used;
|
||||
if (b.use_manual_time) {
|
||||
seconds = results.manual_time_used;
|
||||
} else if (b.use_real_time) {
|
||||
seconds = results.real_time_used;
|
||||
}
|
||||
|
||||
const double min_time =
|
||||
!IsZero(b.min_time) ? b.min_time : FLAGS_benchmark_min_time;
|
||||
// If this was the first run, was elapsed time or cpu time large enough?
|
||||
// If this is not the first run, go with the current value of iter.
|
||||
if ((i > 0) || results.has_error_ || (iters >= kMaxIterations) ||
|
||||
(seconds >= min_time) || (results.real_time_used >= 5 * min_time)) {
|
||||
BenchmarkReporter::Run report =
|
||||
CreateRunReport(b, results, iters, seconds);
|
||||
if (!report.error_occurred && b.complexity != oNone)
|
||||
complexity_reports->push_back(report);
|
||||
reports.push_back(report);
|
||||
break;
|
||||
}
|
||||
|
||||
// See how much iterations should be increased by
|
||||
// Note: Avoid division by zero with max(seconds, 1ns).
|
||||
double multiplier = min_time * 1.4 / std::max(seconds, 1e-9);
|
||||
// If our last run was at least 10% of FLAGS_benchmark_min_time then we
|
||||
// use the multiplier directly. Otherwise we use at most 10 times
|
||||
// expansion.
|
||||
// NOTE: When the last run was at least 10% of the min time the max
|
||||
// expansion should be 14x.
|
||||
bool is_significant = (seconds / min_time) > 0.1;
|
||||
multiplier = is_significant ? multiplier : std::min(10.0, multiplier);
|
||||
if (multiplier <= 1.0) multiplier = 2.0;
|
||||
double next_iters = std::max(multiplier * iters, iters + 1.0);
|
||||
if (next_iters > kMaxIterations) {
|
||||
next_iters = kMaxIterations;
|
||||
}
|
||||
VLOG(3) << "Next iters: " << next_iters << ", " << multiplier << "\n";
|
||||
iters = static_cast<int>(next_iters + 0.5);
|
||||
}
|
||||
}
|
||||
// Calculate additional statistics
|
||||
auto stat_reports = ComputeStats(reports);
|
||||
if ((b.complexity != oNone) && b.last_benchmark_instance) {
|
||||
auto additional_run_stats = ComputeBigO(*complexity_reports);
|
||||
stat_reports.insert(stat_reports.end(), additional_run_stats.begin(),
|
||||
additional_run_stats.end());
|
||||
complexity_reports->clear();
|
||||
}
|
||||
|
||||
if (report_aggregates_only) reports.clear();
|
||||
reports.insert(reports.end(), stat_reports.begin(), stat_reports.end());
|
||||
return reports;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace internal
|
||||
|
||||
State::State(size_t max_iters, const std::vector<int>& ranges, int thread_i,
|
||||
int n_threads, internal::ThreadTimer* timer,
|
||||
internal::ThreadManager* manager)
|
||||
: started_(false),
|
||||
finished_(false),
|
||||
total_iterations_(0),
|
||||
range_(ranges),
|
||||
bytes_processed_(0),
|
||||
items_processed_(0),
|
||||
complexity_n_(0),
|
||||
error_occurred_(false),
|
||||
thread_index(thread_i),
|
||||
threads(n_threads),
|
||||
max_iterations(max_iters),
|
||||
timer_(timer),
|
||||
manager_(manager) {
|
||||
CHECK(max_iterations != 0) << "At least one iteration must be run";
|
||||
CHECK_LT(thread_index, threads) << "thread_index must be less than threads";
|
||||
}
|
||||
|
||||
void State::PauseTiming() {
|
||||
// Add in time accumulated so far
|
||||
CHECK(started_ && !finished_ && !error_occurred_);
|
||||
timer_->StopTimer();
|
||||
}
|
||||
|
||||
void State::ResumeTiming() {
|
||||
CHECK(started_ && !finished_ && !error_occurred_);
|
||||
timer_->StartTimer();
|
||||
}
|
||||
|
||||
void State::SkipWithError(const char* msg) {
|
||||
CHECK(msg);
|
||||
error_occurred_ = true;
|
||||
{
|
||||
MutexLock l(manager_->GetBenchmarkMutex());
|
||||
if (manager_->results.has_error_ == false) {
|
||||
manager_->results.error_message_ = msg;
|
||||
manager_->results.has_error_ = true;
|
||||
}
|
||||
}
|
||||
total_iterations_ = max_iterations;
|
||||
if (timer_->running()) timer_->StopTimer();
|
||||
}
|
||||
|
||||
void State::SetIterationTime(double seconds) {
|
||||
timer_->SetIterationTime(seconds);
|
||||
}
|
||||
|
||||
void State::SetLabel(const char* label) {
|
||||
MutexLock l(manager_->GetBenchmarkMutex());
|
||||
manager_->results.report_label_ = label;
|
||||
}
|
||||
|
||||
void State::StartKeepRunning() {
|
||||
CHECK(!started_ && !finished_);
|
||||
started_ = true;
|
||||
manager_->StartStopBarrier();
|
||||
if (!error_occurred_) ResumeTiming();
|
||||
}
|
||||
|
||||
void State::FinishKeepRunning() {
|
||||
CHECK(started_ && (!finished_ || error_occurred_));
|
||||
if (!error_occurred_) {
|
||||
PauseTiming();
|
||||
}
|
||||
// Total iterations now is one greater than max iterations. Fix this.
|
||||
total_iterations_ = max_iterations;
|
||||
finished_ = true;
|
||||
manager_->StartStopBarrier();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
void RunBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
|
||||
BenchmarkReporter* console_reporter,
|
||||
BenchmarkReporter* file_reporter) {
|
||||
// Note the file_reporter can be null.
|
||||
CHECK(console_reporter != nullptr);
|
||||
|
||||
// Determine the width of the name field using a minimum width of 10.
|
||||
bool has_repetitions = FLAGS_benchmark_repetitions > 1;
|
||||
size_t name_field_width = 10;
|
||||
for (const Benchmark::Instance& benchmark : benchmarks) {
|
||||
name_field_width =
|
||||
std::max<size_t>(name_field_width, benchmark.name.size());
|
||||
has_repetitions |= benchmark.repetitions > 1;
|
||||
}
|
||||
if (has_repetitions) name_field_width += std::strlen("_stddev");
|
||||
|
||||
// Print header here
|
||||
BenchmarkReporter::Context context;
|
||||
context.num_cpus = NumCPUs();
|
||||
context.mhz_per_cpu = CyclesPerSecond() / 1000000.0f;
|
||||
|
||||
context.cpu_scaling_enabled = CpuScalingEnabled();
|
||||
context.name_field_width = name_field_width;
|
||||
|
||||
// Keep track of runing times of all instances of current benchmark
|
||||
std::vector<BenchmarkReporter::Run> complexity_reports;
|
||||
|
||||
// We flush streams after invoking reporter methods that write to them. This
|
||||
// ensures users get timely updates even when streams are not line-buffered.
|
||||
auto flushStreams = [](BenchmarkReporter* reporter) {
|
||||
if (!reporter) return;
|
||||
std::flush(reporter->GetOutputStream());
|
||||
std::flush(reporter->GetErrorStream());
|
||||
};
|
||||
|
||||
if (console_reporter->ReportContext(context) &&
|
||||
(!file_reporter || file_reporter->ReportContext(context))) {
|
||||
flushStreams(console_reporter);
|
||||
flushStreams(file_reporter);
|
||||
for (const auto& benchmark : benchmarks) {
|
||||
std::vector<BenchmarkReporter::Run> reports =
|
||||
RunBenchmark(benchmark, &complexity_reports);
|
||||
console_reporter->ReportRuns(reports);
|
||||
if (file_reporter) file_reporter->ReportRuns(reports);
|
||||
flushStreams(console_reporter);
|
||||
flushStreams(file_reporter);
|
||||
}
|
||||
}
|
||||
console_reporter->Finalize();
|
||||
if (file_reporter) file_reporter->Finalize();
|
||||
flushStreams(console_reporter);
|
||||
flushStreams(file_reporter);
|
||||
}
|
||||
|
||||
std::unique_ptr<BenchmarkReporter> CreateReporter(
|
||||
std::string const& name, ConsoleReporter::OutputOptions allow_color) {
|
||||
typedef std::unique_ptr<BenchmarkReporter> PtrType;
|
||||
if (name == "console") {
|
||||
return PtrType(new ConsoleReporter(allow_color));
|
||||
} else if (name == "json") {
|
||||
return PtrType(new JSONReporter);
|
||||
} else if (name == "csv") {
|
||||
return PtrType(new CSVReporter);
|
||||
} else {
|
||||
std::cerr << "Unexpected format: '" << name << "'\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
} // end namespace internal
|
||||
|
||||
size_t RunSpecifiedBenchmarks() {
|
||||
return RunSpecifiedBenchmarks(nullptr, nullptr);
|
||||
}
|
||||
|
||||
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter) {
|
||||
return RunSpecifiedBenchmarks(console_reporter, nullptr);
|
||||
}
|
||||
|
||||
size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter,
|
||||
BenchmarkReporter* file_reporter) {
|
||||
std::string spec = FLAGS_benchmark_filter;
|
||||
if (spec.empty() || spec == "all")
|
||||
spec = "."; // Regexp that matches all benchmarks
|
||||
|
||||
// Setup the reporters
|
||||
std::ofstream output_file;
|
||||
std::unique_ptr<BenchmarkReporter> default_console_reporter;
|
||||
std::unique_ptr<BenchmarkReporter> default_file_reporter;
|
||||
if (!console_reporter) {
|
||||
auto output_opts = ConsoleReporter::OO_None;
|
||||
if (FLAGS_benchmark_color == "auto")
|
||||
output_opts = IsColorTerminal() ? ConsoleReporter::OO_Color
|
||||
: ConsoleReporter::OO_None;
|
||||
else
|
||||
output_opts = IsTruthyFlagValue(FLAGS_benchmark_color)
|
||||
? ConsoleReporter::OO_Color
|
||||
: ConsoleReporter::OO_None;
|
||||
default_console_reporter =
|
||||
internal::CreateReporter(FLAGS_benchmark_format, output_opts);
|
||||
console_reporter = default_console_reporter.get();
|
||||
}
|
||||
auto& Out = console_reporter->GetOutputStream();
|
||||
auto& Err = console_reporter->GetErrorStream();
|
||||
|
||||
std::string const& fname = FLAGS_benchmark_out;
|
||||
if (fname == "" && file_reporter) {
|
||||
Err << "A custom file reporter was provided but "
|
||||
"--benchmark_out=<file> was not specified."
|
||||
<< std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
if (fname != "") {
|
||||
output_file.open(fname);
|
||||
if (!output_file.is_open()) {
|
||||
Err << "invalid file name: '" << fname << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
if (!file_reporter) {
|
||||
default_file_reporter = internal::CreateReporter(
|
||||
FLAGS_benchmark_out_format, ConsoleReporter::OO_None);
|
||||
file_reporter = default_file_reporter.get();
|
||||
}
|
||||
file_reporter->SetOutputStream(&output_file);
|
||||
file_reporter->SetErrorStream(&output_file);
|
||||
}
|
||||
|
||||
std::vector<internal::Benchmark::Instance> benchmarks;
|
||||
if (!FindBenchmarksInternal(spec, &benchmarks, &Err)) return 0;
|
||||
|
||||
if (benchmarks.empty()) {
|
||||
Err << "Failed to match any benchmarks against regex: " << spec << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FLAGS_benchmark_list_tests) {
|
||||
for (auto const& benchmark : benchmarks) Out << benchmark.name << "\n";
|
||||
} else {
|
||||
internal::RunBenchmarks(benchmarks, console_reporter, file_reporter);
|
||||
}
|
||||
|
||||
return benchmarks.size();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
void PrintUsageAndExit() {
|
||||
fprintf(stdout,
|
||||
"benchmark"
|
||||
" [--benchmark_list_tests={true|false}]\n"
|
||||
" [--benchmark_filter=<regex>]\n"
|
||||
" [--benchmark_min_time=<min_time>]\n"
|
||||
" [--benchmark_repetitions=<num_repetitions>]\n"
|
||||
" [--benchmark_report_aggregates_only={true|false}\n"
|
||||
" [--benchmark_format=<console|json|csv>]\n"
|
||||
" [--benchmark_out=<filename>]\n"
|
||||
" [--benchmark_out_format=<json|console|csv>]\n"
|
||||
" [--benchmark_color={auto|true|false}]\n"
|
||||
" [--v=<verbosity>]\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void ParseCommandLineFlags(int* argc, char** argv) {
|
||||
using namespace benchmark;
|
||||
for (int i = 1; i < *argc; ++i) {
|
||||
if (ParseBoolFlag(argv[i], "benchmark_list_tests",
|
||||
&FLAGS_benchmark_list_tests) ||
|
||||
ParseStringFlag(argv[i], "benchmark_filter", &FLAGS_benchmark_filter) ||
|
||||
ParseDoubleFlag(argv[i], "benchmark_min_time",
|
||||
&FLAGS_benchmark_min_time) ||
|
||||
ParseInt32Flag(argv[i], "benchmark_repetitions",
|
||||
&FLAGS_benchmark_repetitions) ||
|
||||
ParseBoolFlag(argv[i], "benchmark_report_aggregates_only",
|
||||
&FLAGS_benchmark_report_aggregates_only) ||
|
||||
ParseStringFlag(argv[i], "benchmark_format", &FLAGS_benchmark_format) ||
|
||||
ParseStringFlag(argv[i], "benchmark_out", &FLAGS_benchmark_out) ||
|
||||
ParseStringFlag(argv[i], "benchmark_out_format",
|
||||
&FLAGS_benchmark_out_format) ||
|
||||
ParseStringFlag(argv[i], "benchmark_color", &FLAGS_benchmark_color) ||
|
||||
// "color_print" is the deprecated name for "benchmark_color".
|
||||
// TODO: Remove this.
|
||||
ParseStringFlag(argv[i], "color_print", &FLAGS_benchmark_color) ||
|
||||
ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
|
||||
for (int j = i; j != *argc; ++j) argv[j] = argv[j + 1];
|
||||
|
||||
--(*argc);
|
||||
--i;
|
||||
} else if (IsFlag(argv[i], "help")) {
|
||||
PrintUsageAndExit();
|
||||
}
|
||||
}
|
||||
for (auto const* flag :
|
||||
{&FLAGS_benchmark_format, &FLAGS_benchmark_out_format})
|
||||
if (*flag != "console" && *flag != "json" && *flag != "csv") {
|
||||
PrintUsageAndExit();
|
||||
}
|
||||
if (FLAGS_benchmark_color.empty()) {
|
||||
PrintUsageAndExit();
|
||||
}
|
||||
}
|
||||
|
||||
int InitializeStreams() {
|
||||
static std::ios_base::Init init;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
void Initialize(int* argc, char** argv) {
|
||||
internal::ParseCommandLineFlags(argc, argv);
|
||||
internal::LogLevel() = FLAGS_v;
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,47 +0,0 @@
|
||||
#ifndef BENCHMARK_API_INTERNAL_H
|
||||
#define BENCHMARK_API_INTERNAL_H
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace benchmark {
|
||||
namespace internal {
|
||||
|
||||
// Information kept per benchmark we may want to run
|
||||
struct Benchmark::Instance {
|
||||
std::string name;
|
||||
Benchmark* benchmark;
|
||||
ReportMode report_mode;
|
||||
std::vector<int> arg;
|
||||
TimeUnit time_unit;
|
||||
int range_multiplier;
|
||||
bool use_real_time;
|
||||
bool use_manual_time;
|
||||
BigO complexity;
|
||||
BigOFunc* complexity_lambda;
|
||||
bool last_benchmark_instance;
|
||||
int repetitions;
|
||||
double min_time;
|
||||
int threads; // Number of concurrent threads to us
|
||||
};
|
||||
|
||||
bool FindBenchmarksInternal(const std::string& re,
|
||||
std::vector<Benchmark::Instance>* benchmarks,
|
||||
std::ostream* Err);
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsZero(double n) {
|
||||
return std::abs(n) < std::numeric_limits<double>::epsilon();
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
} // end namespace internal
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_API_INTERNAL_H
|
||||
@@ -1,439 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "benchmark_api_internal.h"
|
||||
#include "internal_macros.h"
|
||||
|
||||
#ifndef BENCHMARK_OS_WINDOWS
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "check.h"
|
||||
#include "commandlineflags.h"
|
||||
#include "complexity.h"
|
||||
#include "log.h"
|
||||
#include "mutex.h"
|
||||
#include "re.h"
|
||||
#include "stat.h"
|
||||
#include "string_util.h"
|
||||
#include "sysinfo.h"
|
||||
#include "timers.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
namespace {
|
||||
// For non-dense Range, intermediate values are powers of kRangeMultiplier.
|
||||
static const int kRangeMultiplier = 8;
|
||||
// The size of a benchmark family determines is the number of inputs to repeat
|
||||
// the benchmark on. If this is "large" then warn the user during configuration.
|
||||
static const size_t kMaxFamilySize = 100;
|
||||
} // end namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
//=============================================================================//
|
||||
// BenchmarkFamilies
|
||||
//=============================================================================//
|
||||
|
||||
// Class for managing registered benchmarks. Note that each registered
|
||||
// benchmark identifies a family of related benchmarks to run.
|
||||
class BenchmarkFamilies {
|
||||
public:
|
||||
static BenchmarkFamilies* GetInstance();
|
||||
|
||||
// Registers a benchmark family and returns the index assigned to it.
|
||||
size_t AddBenchmark(std::unique_ptr<Benchmark> family);
|
||||
|
||||
// Extract the list of benchmark instances that match the specified
|
||||
// regular expression.
|
||||
bool FindBenchmarks(const std::string& re,
|
||||
std::vector<Benchmark::Instance>* benchmarks,
|
||||
std::ostream* Err);
|
||||
|
||||
private:
|
||||
BenchmarkFamilies() {}
|
||||
|
||||
std::vector<std::unique_ptr<Benchmark>> families_;
|
||||
Mutex mutex_;
|
||||
};
|
||||
|
||||
BenchmarkFamilies* BenchmarkFamilies::GetInstance() {
|
||||
static BenchmarkFamilies instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
size_t BenchmarkFamilies::AddBenchmark(std::unique_ptr<Benchmark> family) {
|
||||
MutexLock l(mutex_);
|
||||
size_t index = families_.size();
|
||||
families_.push_back(std::move(family));
|
||||
return index;
|
||||
}
|
||||
|
||||
bool BenchmarkFamilies::FindBenchmarks(
|
||||
const std::string& spec, std::vector<Benchmark::Instance>* benchmarks,
|
||||
std::ostream* ErrStream) {
|
||||
CHECK(ErrStream);
|
||||
auto& Err = *ErrStream;
|
||||
// Make regular expression out of command-line flag
|
||||
std::string error_msg;
|
||||
Regex re;
|
||||
if (!re.Init(spec, &error_msg)) {
|
||||
Err << "Could not compile benchmark re: " << error_msg << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special list of thread counts to use when none are specified
|
||||
const std::vector<int> one_thread = {1};
|
||||
|
||||
MutexLock l(mutex_);
|
||||
for (std::unique_ptr<Benchmark>& family : families_) {
|
||||
// Family was deleted or benchmark doesn't match
|
||||
if (!family) continue;
|
||||
|
||||
if (family->ArgsCnt() == -1) {
|
||||
family->Args({});
|
||||
}
|
||||
const std::vector<int>* thread_counts =
|
||||
(family->thread_counts_.empty()
|
||||
? &one_thread
|
||||
: &static_cast<const std::vector<int>&>(family->thread_counts_));
|
||||
const size_t family_size = family->args_.size() * thread_counts->size();
|
||||
// The benchmark will be run at least 'family_size' different inputs.
|
||||
// If 'family_size' is very large warn the user.
|
||||
if (family_size > kMaxFamilySize) {
|
||||
Err << "The number of inputs is very large. " << family->name_
|
||||
<< " will be repeated at least " << family_size << " times.\n";
|
||||
}
|
||||
// reserve in the special case the regex ".", since we know the final
|
||||
// family size.
|
||||
if (spec == ".") benchmarks->reserve(family_size);
|
||||
|
||||
for (auto const& args : family->args_) {
|
||||
for (int num_threads : *thread_counts) {
|
||||
Benchmark::Instance instance;
|
||||
instance.name = family->name_;
|
||||
instance.benchmark = family.get();
|
||||
instance.report_mode = family->report_mode_;
|
||||
instance.arg = args;
|
||||
instance.time_unit = family->time_unit_;
|
||||
instance.range_multiplier = family->range_multiplier_;
|
||||
instance.min_time = family->min_time_;
|
||||
instance.repetitions = family->repetitions_;
|
||||
instance.use_real_time = family->use_real_time_;
|
||||
instance.use_manual_time = family->use_manual_time_;
|
||||
instance.complexity = family->complexity_;
|
||||
instance.complexity_lambda = family->complexity_lambda_;
|
||||
instance.threads = num_threads;
|
||||
|
||||
// Add arguments to instance name
|
||||
size_t arg_i = 0;
|
||||
for (auto const& arg : args) {
|
||||
instance.name += "/";
|
||||
|
||||
if (arg_i < family->arg_names_.size()) {
|
||||
const auto& arg_name = family->arg_names_[arg_i];
|
||||
if (!arg_name.empty()) {
|
||||
instance.name +=
|
||||
StringPrintF("%s:", family->arg_names_[arg_i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
AppendHumanReadable(arg, &instance.name);
|
||||
++arg_i;
|
||||
}
|
||||
|
||||
if (!IsZero(family->min_time_)) {
|
||||
instance.name += StringPrintF("/min_time:%0.3f", family->min_time_);
|
||||
}
|
||||
if (family->repetitions_ != 0) {
|
||||
instance.name += StringPrintF("/repeats:%d", family->repetitions_);
|
||||
}
|
||||
if (family->use_manual_time_) {
|
||||
instance.name += "/manual_time";
|
||||
} else if (family->use_real_time_) {
|
||||
instance.name += "/real_time";
|
||||
}
|
||||
|
||||
// Add the number of threads used to the name
|
||||
if (!family->thread_counts_.empty()) {
|
||||
instance.name += StringPrintF("/threads:%d", instance.threads);
|
||||
}
|
||||
|
||||
if (re.Match(instance.name)) {
|
||||
instance.last_benchmark_instance = (&args == &family->args_.back());
|
||||
benchmarks->push_back(std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Benchmark* RegisterBenchmarkInternal(Benchmark* bench) {
|
||||
std::unique_ptr<Benchmark> bench_ptr(bench);
|
||||
BenchmarkFamilies* families = BenchmarkFamilies::GetInstance();
|
||||
families->AddBenchmark(std::move(bench_ptr));
|
||||
return bench;
|
||||
}
|
||||
|
||||
// FIXME: This function is a hack so that benchmark.cc can access
|
||||
// `BenchmarkFamilies`
|
||||
bool FindBenchmarksInternal(const std::string& re,
|
||||
std::vector<Benchmark::Instance>* benchmarks,
|
||||
std::ostream* Err) {
|
||||
return BenchmarkFamilies::GetInstance()->FindBenchmarks(re, benchmarks, Err);
|
||||
}
|
||||
|
||||
//=============================================================================//
|
||||
// Benchmark
|
||||
//=============================================================================//
|
||||
|
||||
Benchmark::Benchmark(const char* name)
|
||||
: name_(name),
|
||||
report_mode_(RM_Unspecified),
|
||||
time_unit_(kNanosecond),
|
||||
range_multiplier_(kRangeMultiplier),
|
||||
min_time_(0),
|
||||
repetitions_(0),
|
||||
use_real_time_(false),
|
||||
use_manual_time_(false),
|
||||
complexity_(oNone),
|
||||
complexity_lambda_(nullptr) {}
|
||||
|
||||
Benchmark::~Benchmark() {}
|
||||
|
||||
void Benchmark::AddRange(std::vector<int>* dst, int lo, int hi, int mult) {
|
||||
CHECK_GE(lo, 0);
|
||||
CHECK_GE(hi, lo);
|
||||
CHECK_GE(mult, 2);
|
||||
|
||||
// Add "lo"
|
||||
dst->push_back(lo);
|
||||
|
||||
static const int kint32max = std::numeric_limits<int32_t>::max();
|
||||
|
||||
// Now space out the benchmarks in multiples of "mult"
|
||||
for (int32_t i = 1; i < kint32max / mult; i *= mult) {
|
||||
if (i >= hi) break;
|
||||
if (i > lo) {
|
||||
dst->push_back(i);
|
||||
}
|
||||
}
|
||||
// Add "hi" (if different from "lo")
|
||||
if (hi != lo) {
|
||||
dst->push_back(hi);
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Arg(int x) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||
args_.push_back({x});
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Unit(TimeUnit unit) {
|
||||
time_unit_ = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Range(int start, int limit) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||
std::vector<int> arglist;
|
||||
AddRange(&arglist, start, limit, range_multiplier_);
|
||||
|
||||
for (int i : arglist) {
|
||||
args_.push_back({i});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Ranges(const std::vector<std::pair<int, int>>& ranges) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(ranges.size()));
|
||||
std::vector<std::vector<int>> arglists(ranges.size());
|
||||
std::size_t total = 1;
|
||||
for (std::size_t i = 0; i < ranges.size(); i++) {
|
||||
AddRange(&arglists[i], ranges[i].first, ranges[i].second,
|
||||
range_multiplier_);
|
||||
total *= arglists[i].size();
|
||||
}
|
||||
|
||||
std::vector<std::size_t> ctr(arglists.size(), 0);
|
||||
|
||||
for (std::size_t i = 0; i < total; i++) {
|
||||
std::vector<int> tmp;
|
||||
tmp.reserve(arglists.size());
|
||||
|
||||
for (std::size_t j = 0; j < arglists.size(); j++) {
|
||||
tmp.push_back(arglists[j].at(ctr[j]));
|
||||
}
|
||||
|
||||
args_.push_back(std::move(tmp));
|
||||
|
||||
for (std::size_t j = 0; j < arglists.size(); j++) {
|
||||
if (ctr[j] + 1 < arglists[j].size()) {
|
||||
++ctr[j];
|
||||
break;
|
||||
}
|
||||
ctr[j] = 0;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::ArgName(const std::string& name) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||
arg_names_ = {name};
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::ArgNames(const std::vector<std::string>& names) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(names.size()));
|
||||
arg_names_ = names;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::DenseRange(int start, int limit, int step) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||
CHECK_GE(start, 0);
|
||||
CHECK_LE(start, limit);
|
||||
for (int arg = start; arg <= limit; arg += step) {
|
||||
args_.push_back({arg});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Args(const std::vector<int>& args) {
|
||||
CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(args.size()));
|
||||
args_.push_back(args);
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Apply(void (*custom_arguments)(Benchmark* benchmark)) {
|
||||
custom_arguments(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::RangeMultiplier(int multiplier) {
|
||||
CHECK(multiplier > 1);
|
||||
range_multiplier_ = multiplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Repetitions(int n) {
|
||||
CHECK(n > 0);
|
||||
repetitions_ = n;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::ReportAggregatesOnly(bool value) {
|
||||
report_mode_ = value ? RM_ReportAggregatesOnly : RM_Default;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::MinTime(double t) {
|
||||
CHECK(t > 0.0);
|
||||
min_time_ = t;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::UseRealTime() {
|
||||
CHECK(!use_manual_time_)
|
||||
<< "Cannot set UseRealTime and UseManualTime simultaneously.";
|
||||
use_real_time_ = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::UseManualTime() {
|
||||
CHECK(!use_real_time_)
|
||||
<< "Cannot set UseRealTime and UseManualTime simultaneously.";
|
||||
use_manual_time_ = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Complexity(BigO complexity) {
|
||||
complexity_ = complexity;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Complexity(BigOFunc* complexity) {
|
||||
complexity_lambda_ = complexity;
|
||||
complexity_ = oLambda;
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::Threads(int t) {
|
||||
CHECK_GT(t, 0);
|
||||
thread_counts_.push_back(t);
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::ThreadRange(int min_threads, int max_threads) {
|
||||
CHECK_GT(min_threads, 0);
|
||||
CHECK_GE(max_threads, min_threads);
|
||||
|
||||
AddRange(&thread_counts_, min_threads, max_threads, 2);
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::DenseThreadRange(int min_threads, int max_threads,
|
||||
int stride) {
|
||||
CHECK_GT(min_threads, 0);
|
||||
CHECK_GE(max_threads, min_threads);
|
||||
CHECK_GE(stride, 1);
|
||||
|
||||
for (auto i = min_threads; i < max_threads; i += stride) {
|
||||
thread_counts_.push_back(i);
|
||||
}
|
||||
thread_counts_.push_back(max_threads);
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::ThreadPerCpu() {
|
||||
static int num_cpus = NumCPUs();
|
||||
thread_counts_.push_back(num_cpus);
|
||||
return this;
|
||||
}
|
||||
|
||||
void Benchmark::SetName(const char* name) { name_ = name; }
|
||||
|
||||
int Benchmark::ArgsCnt() const {
|
||||
if (args_.empty()) {
|
||||
if (arg_names_.empty()) return -1;
|
||||
return static_cast<int>(arg_names_.size());
|
||||
}
|
||||
return static_cast<int>(args_.front().size());
|
||||
}
|
||||
|
||||
//=============================================================================//
|
||||
// FunctionBenchmark
|
||||
//=============================================================================//
|
||||
|
||||
void FunctionBenchmark::Run(State& st) { func_(st); }
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace benchmark
|
||||
@@ -1,71 +0,0 @@
|
||||
#ifndef CHECK_H_
|
||||
#define CHECK_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
|
||||
#include "internal_macros.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace benchmark {
|
||||
namespace internal {
|
||||
|
||||
typedef void(AbortHandlerT)();
|
||||
|
||||
inline AbortHandlerT*& GetAbortHandler() {
|
||||
static AbortHandlerT* handler = &std::abort;
|
||||
return handler;
|
||||
}
|
||||
|
||||
BENCHMARK_NORETURN inline void CallAbortHandler() {
|
||||
GetAbortHandler()();
|
||||
std::abort(); // fallback to enforce noreturn
|
||||
}
|
||||
|
||||
// CheckHandler is the class constructed by failing CHECK macros. CheckHandler
|
||||
// will log information about the failures and abort when it is destructed.
|
||||
class CheckHandler {
|
||||
public:
|
||||
CheckHandler(const char* check, const char* file, const char* func, int line)
|
||||
: log_(GetErrorLogInstance()) {
|
||||
log_ << file << ":" << line << ": " << func << ": Check `" << check
|
||||
<< "' failed. ";
|
||||
}
|
||||
|
||||
LogType& GetLog() { return log_; }
|
||||
|
||||
BENCHMARK_NORETURN ~CheckHandler() BENCHMARK_NOEXCEPT_OP(false) {
|
||||
log_ << std::endl;
|
||||
CallAbortHandler();
|
||||
}
|
||||
|
||||
CheckHandler& operator=(const CheckHandler&) = delete;
|
||||
CheckHandler(const CheckHandler&) = delete;
|
||||
CheckHandler() = delete;
|
||||
|
||||
private:
|
||||
LogType& log_;
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace benchmark
|
||||
|
||||
// The CHECK macro returns a std::ostream object that can have extra information
|
||||
// written to it.
|
||||
#ifndef NDEBUG
|
||||
#define CHECK(b) \
|
||||
(b ? ::benchmark::internal::GetNullLogInstance() \
|
||||
: ::benchmark::internal::CheckHandler(#b, __FILE__, __func__, __LINE__) \
|
||||
.GetLog())
|
||||
#else
|
||||
#define CHECK(b) ::benchmark::internal::GetNullLogInstance()
|
||||
#endif
|
||||
|
||||
#define CHECK_EQ(a, b) CHECK((a) == (b))
|
||||
#define CHECK_NE(a, b) CHECK((a) != (b))
|
||||
#define CHECK_GE(a, b) CHECK((a) >= (b))
|
||||
#define CHECK_LE(a, b) CHECK((a) <= (b))
|
||||
#define CHECK_GT(a, b) CHECK((a) > (b))
|
||||
#define CHECK_LT(a, b) CHECK((a) < (b))
|
||||
|
||||
#endif // CHECK_H_
|
||||
@@ -1,188 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "colorprint.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "check.h"
|
||||
#include "internal_macros.h"
|
||||
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif // BENCHMARK_OS_WINDOWS
|
||||
|
||||
namespace benchmark {
|
||||
namespace {
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
typedef WORD PlatformColorCode;
|
||||
#else
|
||||
typedef const char* PlatformColorCode;
|
||||
#endif
|
||||
|
||||
PlatformColorCode GetPlatformColorCode(LogColor color) {
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
switch (color) {
|
||||
case COLOR_RED:
|
||||
return FOREGROUND_RED;
|
||||
case COLOR_GREEN:
|
||||
return FOREGROUND_GREEN;
|
||||
case COLOR_YELLOW:
|
||||
return FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
case COLOR_BLUE:
|
||||
return FOREGROUND_BLUE;
|
||||
case COLOR_MAGENTA:
|
||||
return FOREGROUND_BLUE | FOREGROUND_RED;
|
||||
case COLOR_CYAN:
|
||||
return FOREGROUND_BLUE | FOREGROUND_GREEN;
|
||||
case COLOR_WHITE: // fall through to default
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
switch (color) {
|
||||
case COLOR_RED:
|
||||
return "1";
|
||||
case COLOR_GREEN:
|
||||
return "2";
|
||||
case COLOR_YELLOW:
|
||||
return "3";
|
||||
case COLOR_BLUE:
|
||||
return "4";
|
||||
case COLOR_MAGENTA:
|
||||
return "5";
|
||||
case COLOR_CYAN:
|
||||
return "6";
|
||||
case COLOR_WHITE:
|
||||
return "7";
|
||||
default:
|
||||
return nullptr;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
std::string FormatString(const char* msg, va_list args) {
|
||||
// we might need a second shot at this, so pre-emptivly make a copy
|
||||
va_list args_cp;
|
||||
va_copy(args_cp, args);
|
||||
|
||||
std::size_t size = 256;
|
||||
char local_buff[256];
|
||||
auto ret = std::vsnprintf(local_buff, size, msg, args_cp);
|
||||
|
||||
va_end(args_cp);
|
||||
|
||||
// currently there is no error handling for failure, so this is hack.
|
||||
CHECK(ret >= 0);
|
||||
|
||||
if (ret == 0) // handle empty expansion
|
||||
return {};
|
||||
else if (static_cast<size_t>(ret) < size)
|
||||
return local_buff;
|
||||
else {
|
||||
// we did not provide a long enough buffer on our first attempt.
|
||||
size = (size_t)ret + 1; // + 1 for the null byte
|
||||
std::unique_ptr<char[]> buff(new char[size]);
|
||||
ret = std::vsnprintf(buff.get(), size, msg, args);
|
||||
CHECK(ret > 0 && ((size_t)ret) < size);
|
||||
return buff.get();
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatString(const char* msg, ...) {
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
auto tmp = FormatString(msg, args);
|
||||
va_end(args);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
ColorPrintf(out, color, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
|
||||
va_list args) {
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
((void)out); // suppress unused warning
|
||||
|
||||
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
// Gets the current text color.
|
||||
CONSOLE_SCREEN_BUFFER_INFO buffer_info;
|
||||
GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
|
||||
const WORD old_color_attrs = buffer_info.wAttributes;
|
||||
|
||||
// We need to flush the stream buffers into the console before each
|
||||
// SetConsoleTextAttribute call lest it affect the text that is already
|
||||
// printed but has not yet reached the console.
|
||||
fflush(stdout);
|
||||
SetConsoleTextAttribute(stdout_handle,
|
||||
GetPlatformColorCode(color) | FOREGROUND_INTENSITY);
|
||||
vprintf(fmt, args);
|
||||
|
||||
fflush(stdout);
|
||||
// Restores the text color.
|
||||
SetConsoleTextAttribute(stdout_handle, old_color_attrs);
|
||||
#else
|
||||
const char* color_code = GetPlatformColorCode(color);
|
||||
if (color_code) out << FormatString("\033[0;3%sm", color_code);
|
||||
out << FormatString(fmt, args) << "\033[m";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsColorTerminal() {
|
||||
#if BENCHMARK_OS_WINDOWS
|
||||
// On Windows the TERM variable is usually not set, but the
|
||||
// console there does support colors.
|
||||
return 0 != _isatty(_fileno(stdout));
|
||||
#else
|
||||
// On non-Windows platforms, we rely on the TERM variable. This list of
|
||||
// supported TERM values is copied from Google Test:
|
||||
// <https://github.com/google/googletest/blob/master/googletest/src/gtest.cc#L2925>.
|
||||
const char* const SUPPORTED_TERM_VALUES[] = {
|
||||
"xterm", "xterm-color", "xterm-256color",
|
||||
"screen", "screen-256color", "tmux",
|
||||
"tmux-256color", "rxvt-unicode", "rxvt-unicode-256color",
|
||||
"linux", "cygwin",
|
||||
};
|
||||
|
||||
const char* const term = getenv("TERM");
|
||||
|
||||
bool term_supports_color = false;
|
||||
for (const char* candidate : SUPPORTED_TERM_VALUES) {
|
||||
if (term && 0 == strcmp(term, candidate)) {
|
||||
term_supports_color = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0 != isatty(fileno(stdout)) && term_supports_color;
|
||||
#endif // BENCHMARK_OS_WINDOWS
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef BENCHMARK_COLORPRINT_H_
|
||||
#define BENCHMARK_COLORPRINT_H_
|
||||
|
||||
#include <cstdarg>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace benchmark {
|
||||
enum LogColor {
|
||||
COLOR_DEFAULT,
|
||||
COLOR_RED,
|
||||
COLOR_GREEN,
|
||||
COLOR_YELLOW,
|
||||
COLOR_BLUE,
|
||||
COLOR_MAGENTA,
|
||||
COLOR_CYAN,
|
||||
COLOR_WHITE
|
||||
};
|
||||
|
||||
std::string FormatString(const char* msg, va_list args);
|
||||
std::string FormatString(const char* msg, ...);
|
||||
|
||||
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
|
||||
va_list args);
|
||||
void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...);
|
||||
|
||||
// Returns true if stdout appears to be a terminal that supports colored
|
||||
// output, false otherwise.
|
||||
bool IsColorTerminal();
|
||||
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_COLORPRINT_H_
|
||||
@@ -1,218 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "commandlineflags.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
namespace benchmark {
|
||||
// Parses 'str' for a 32-bit signed integer. If successful, writes
|
||||
// the result to *value and returns true; otherwise leaves *value
|
||||
// unchanged and returns false.
|
||||
bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) {
|
||||
// Parses the environment variable as a decimal integer.
|
||||
char* end = nullptr;
|
||||
const long long_value = strtol(str, &end, 10); // NOLINT
|
||||
|
||||
// Has strtol() consumed all characters in the string?
|
||||
if (*end != '\0') {
|
||||
// No - an invalid character was encountered.
|
||||
std::cerr << src_text << " is expected to be a 32-bit integer, "
|
||||
<< "but actually has value \"" << str << "\".\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the parsed value in the range of an Int32?
|
||||
const int32_t result = static_cast<int32_t>(long_value);
|
||||
if (long_value == std::numeric_limits<long>::max() ||
|
||||
long_value == std::numeric_limits<long>::min() ||
|
||||
// The parsed value overflows as a long. (strtol() returns
|
||||
// LONG_MAX or LONG_MIN when the input overflows.)
|
||||
result != long_value
|
||||
// The parsed value overflows as an Int32.
|
||||
) {
|
||||
std::cerr << src_text << " is expected to be a 32-bit integer, "
|
||||
<< "but actually has value \"" << str << "\", "
|
||||
<< "which overflows.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses 'str' for a double. If successful, writes the result to *value and
|
||||
// returns true; otherwise leaves *value unchanged and returns false.
|
||||
bool ParseDouble(const std::string& src_text, const char* str, double* value) {
|
||||
// Parses the environment variable as a decimal integer.
|
||||
char* end = nullptr;
|
||||
const double double_value = strtod(str, &end); // NOLINT
|
||||
|
||||
// Has strtol() consumed all characters in the string?
|
||||
if (*end != '\0') {
|
||||
// No - an invalid character was encountered.
|
||||
std::cerr << src_text << " is expected to be a double, "
|
||||
<< "but actually has value \"" << str << "\".\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = double_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the name of the environment variable corresponding to the
|
||||
// given flag. For example, FlagToEnvVar("foo") will return
|
||||
// "BENCHMARK_FOO" in the open-source version.
|
||||
static std::string FlagToEnvVar(const char* flag) {
|
||||
const std::string flag_str(flag);
|
||||
|
||||
std::string env_var;
|
||||
for (size_t i = 0; i != flag_str.length(); ++i)
|
||||
env_var += static_cast<char>(::toupper(flag_str.c_str()[i]));
|
||||
|
||||
return "BENCHMARK_" + env_var;
|
||||
}
|
||||
|
||||
// Reads and returns the Boolean environment variable corresponding to
|
||||
// the given flag; if it's not set, returns default_value.
|
||||
//
|
||||
// The value is considered true iff it's not "0".
|
||||
bool BoolFromEnv(const char* flag, bool default_value) {
|
||||
const std::string env_var = FlagToEnvVar(flag);
|
||||
const char* const string_value = getenv(env_var.c_str());
|
||||
return string_value == nullptr ? default_value
|
||||
: strcmp(string_value, "0") != 0;
|
||||
}
|
||||
|
||||
// Reads and returns a 32-bit integer stored in the environment
|
||||
// variable corresponding to the given flag; if it isn't set or
|
||||
// doesn't represent a valid 32-bit integer, returns default_value.
|
||||
int32_t Int32FromEnv(const char* flag, int32_t default_value) {
|
||||
const std::string env_var = FlagToEnvVar(flag);
|
||||
const char* const string_value = getenv(env_var.c_str());
|
||||
if (string_value == nullptr) {
|
||||
// The environment variable is not set.
|
||||
return default_value;
|
||||
}
|
||||
|
||||
int32_t result = default_value;
|
||||
if (!ParseInt32(std::string("Environment variable ") + env_var, string_value,
|
||||
&result)) {
|
||||
std::cout << "The default value " << default_value << " is used.\n";
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Reads and returns the string environment variable corresponding to
|
||||
// the given flag; if it's not set, returns default_value.
|
||||
const char* StringFromEnv(const char* flag, const char* default_value) {
|
||||
const std::string env_var = FlagToEnvVar(flag);
|
||||
const char* const value = getenv(env_var.c_str());
|
||||
return value == nullptr ? default_value : value;
|
||||
}
|
||||
|
||||
// Parses a string as a command line flag. The string should have
|
||||
// the format "--flag=value". When def_optional is true, the "=value"
|
||||
// part can be omitted.
|
||||
//
|
||||
// Returns the value of the flag, or nullptr if the parsing failed.
|
||||
const char* ParseFlagValue(const char* str, const char* flag,
|
||||
bool def_optional) {
|
||||
// str and flag must not be nullptr.
|
||||
if (str == nullptr || flag == nullptr) return nullptr;
|
||||
|
||||
// The flag must start with "--".
|
||||
const std::string flag_str = std::string("--") + std::string(flag);
|
||||
const size_t flag_len = flag_str.length();
|
||||
if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;
|
||||
|
||||
// Skips the flag name.
|
||||
const char* flag_end = str + flag_len;
|
||||
|
||||
// When def_optional is true, it's OK to not have a "=value" part.
|
||||
if (def_optional && (flag_end[0] == '\0')) return flag_end;
|
||||
|
||||
// If def_optional is true and there are more characters after the
|
||||
// flag name, or if def_optional is false, there must be a '=' after
|
||||
// the flag name.
|
||||
if (flag_end[0] != '=') return nullptr;
|
||||
|
||||
// Returns the string after "=".
|
||||
return flag_end + 1;
|
||||
}
|
||||
|
||||
bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
|
||||
// Gets the value of the flag as a string.
|
||||
const char* const value_str = ParseFlagValue(str, flag, true);
|
||||
|
||||
// Aborts if the parsing failed.
|
||||
if (value_str == nullptr) return false;
|
||||
|
||||
// Converts the string value to a bool.
|
||||
*value = IsTruthyFlagValue(value_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) {
|
||||
// Gets the value of the flag as a string.
|
||||
const char* const value_str = ParseFlagValue(str, flag, false);
|
||||
|
||||
// Aborts if the parsing failed.
|
||||
if (value_str == nullptr) return false;
|
||||
|
||||
// Sets *value to the value of the flag.
|
||||
return ParseInt32(std::string("The value of flag --") + flag, value_str,
|
||||
value);
|
||||
}
|
||||
|
||||
bool ParseDoubleFlag(const char* str, const char* flag, double* value) {
|
||||
// Gets the value of the flag as a string.
|
||||
const char* const value_str = ParseFlagValue(str, flag, false);
|
||||
|
||||
// Aborts if the parsing failed.
|
||||
if (value_str == nullptr) return false;
|
||||
|
||||
// Sets *value to the value of the flag.
|
||||
return ParseDouble(std::string("The value of flag --") + flag, value_str,
|
||||
value);
|
||||
}
|
||||
|
||||
bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
|
||||
// Gets the value of the flag as a string.
|
||||
const char* const value_str = ParseFlagValue(str, flag, false);
|
||||
|
||||
// Aborts if the parsing failed.
|
||||
if (value_str == nullptr) return false;
|
||||
|
||||
*value = value_str;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsFlag(const char* str, const char* flag) {
|
||||
return (ParseFlagValue(str, flag, true) != nullptr);
|
||||
}
|
||||
|
||||
bool IsTruthyFlagValue(const std::string& str) {
|
||||
if (str.empty()) return true;
|
||||
char ch = str[0];
|
||||
return isalnum(ch) &&
|
||||
!(ch == '0' || ch == 'f' || ch == 'F' || ch == 'n' || ch == 'N');
|
||||
}
|
||||
} // end namespace benchmark
|
||||
@@ -1,79 +0,0 @@
|
||||
#ifndef BENCHMARK_COMMANDLINEFLAGS_H_
|
||||
#define BENCHMARK_COMMANDLINEFLAGS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// Macro for referencing flags.
|
||||
#define FLAG(name) FLAGS_##name
|
||||
|
||||
// Macros for declaring flags.
|
||||
#define DECLARE_bool(name) extern bool FLAG(name)
|
||||
#define DECLARE_int32(name) extern int32_t FLAG(name)
|
||||
#define DECLARE_int64(name) extern int64_t FLAG(name)
|
||||
#define DECLARE_double(name) extern double FLAG(name)
|
||||
#define DECLARE_string(name) extern std::string FLAG(name)
|
||||
|
||||
// Macros for defining flags.
|
||||
#define DEFINE_bool(name, default_val, doc) bool FLAG(name) = (default_val)
|
||||
#define DEFINE_int32(name, default_val, doc) int32_t FLAG(name) = (default_val)
|
||||
#define DEFINE_int64(name, default_val, doc) int64_t FLAG(name) = (default_val)
|
||||
#define DEFINE_double(name, default_val, doc) double FLAG(name) = (default_val)
|
||||
#define DEFINE_string(name, default_val, doc) \
|
||||
std::string FLAG(name) = (default_val)
|
||||
|
||||
namespace benchmark {
|
||||
// Parses 'str' for a 32-bit signed integer. If successful, writes the result
|
||||
// to *value and returns true; otherwise leaves *value unchanged and returns
|
||||
// false.
|
||||
bool ParseInt32(const std::string& src_text, const char* str, int32_t* value);
|
||||
|
||||
// Parses a bool/Int32/string from the environment variable
|
||||
// corresponding to the given Google Test flag.
|
||||
bool BoolFromEnv(const char* flag, bool default_val);
|
||||
int32_t Int32FromEnv(const char* flag, int32_t default_val);
|
||||
double DoubleFromEnv(const char* flag, double default_val);
|
||||
const char* StringFromEnv(const char* flag, const char* default_val);
|
||||
|
||||
// Parses a string for a bool flag, in the form of either
|
||||
// "--flag=value" or "--flag".
|
||||
//
|
||||
// In the former case, the value is taken as true if it passes IsTruthyValue().
|
||||
//
|
||||
// In the latter case, the value is taken as true.
|
||||
//
|
||||
// On success, stores the value of the flag in *value, and returns
|
||||
// true. On failure, returns false without changing *value.
|
||||
bool ParseBoolFlag(const char* str, const char* flag, bool* value);
|
||||
|
||||
// Parses a string for an Int32 flag, in the form of
|
||||
// "--flag=value".
|
||||
//
|
||||
// On success, stores the value of the flag in *value, and returns
|
||||
// true. On failure, returns false without changing *value.
|
||||
bool ParseInt32Flag(const char* str, const char* flag, int32_t* value);
|
||||
|
||||
// Parses a string for a Double flag, in the form of
|
||||
// "--flag=value".
|
||||
//
|
||||
// On success, stores the value of the flag in *value, and returns
|
||||
// true. On failure, returns false without changing *value.
|
||||
bool ParseDoubleFlag(const char* str, const char* flag, double* value);
|
||||
|
||||
// Parses a string for a string flag, in the form of
|
||||
// "--flag=value".
|
||||
//
|
||||
// On success, stores the value of the flag in *value, and returns
|
||||
// true. On failure, returns false without changing *value.
|
||||
bool ParseStringFlag(const char* str, const char* flag, std::string* value);
|
||||
|
||||
// Returns true if the string matches the flag.
|
||||
bool IsFlag(const char* str, const char* flag);
|
||||
|
||||
// Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or
|
||||
// some non-alphanumeric character. As a special case, also returns true if
|
||||
// value is the empty string.
|
||||
bool IsTruthyFlagValue(const std::string& value);
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_COMMANDLINEFLAGS_H_
|
||||
@@ -1,284 +0,0 @@
|
||||
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Source project : https://github.com/ismaelJimenez/cpp.leastsq
|
||||
// Adapted to be used with google benchmark
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "check.h"
|
||||
#include "complexity.h"
|
||||
#include "stat.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
// Internal function to calculate the different scalability forms
|
||||
BigOFunc* FittingCurve(BigO complexity) {
|
||||
switch (complexity) {
|
||||
case oN:
|
||||
return [](int n) -> double { return n; };
|
||||
case oNSquared:
|
||||
return [](int n) -> double { return std::pow(n, 2); };
|
||||
case oNCubed:
|
||||
return [](int n) -> double { return std::pow(n, 3); };
|
||||
case oLogN:
|
||||
return [](int n) { return std::log2(n); };
|
||||
case oNLogN:
|
||||
return [](int n) { return n * std::log2(n); };
|
||||
case o1:
|
||||
default:
|
||||
return [](int) { return 1.0; };
|
||||
}
|
||||
}
|
||||
|
||||
// Function to return an string for the calculated complexity
|
||||
std::string GetBigOString(BigO complexity) {
|
||||
switch (complexity) {
|
||||
case oN:
|
||||
return "N";
|
||||
case oNSquared:
|
||||
return "N^2";
|
||||
case oNCubed:
|
||||
return "N^3";
|
||||
case oLogN:
|
||||
return "lgN";
|
||||
case oNLogN:
|
||||
return "NlgN";
|
||||
case o1:
|
||||
return "(1)";
|
||||
default:
|
||||
return "f(N)";
|
||||
}
|
||||
}
|
||||
|
||||
// Find the coefficient for the high-order term in the running time, by
|
||||
// minimizing the sum of squares of relative error, for the fitting curve
|
||||
// given by the lambda expresion.
|
||||
// - n : Vector containing the size of the benchmark tests.
|
||||
// - time : Vector containing the times for the benchmark tests.
|
||||
// - fitting_curve : lambda expresion (e.g. [](int n) {return n; };).
|
||||
|
||||
// For a deeper explanation on the algorithm logic, look the README file at
|
||||
// http://github.com/ismaelJimenez/Minimal-Cpp-Least-Squared-Fit
|
||||
|
||||
LeastSq MinimalLeastSq(const std::vector<int>& n,
|
||||
const std::vector<double>& time,
|
||||
BigOFunc* fitting_curve) {
|
||||
double sigma_gn = 0.0;
|
||||
double sigma_gn_squared = 0.0;
|
||||
double sigma_time = 0.0;
|
||||
double sigma_time_gn = 0.0;
|
||||
|
||||
// Calculate least square fitting parameter
|
||||
for (size_t i = 0; i < n.size(); ++i) {
|
||||
double gn_i = fitting_curve(n[i]);
|
||||
sigma_gn += gn_i;
|
||||
sigma_gn_squared += gn_i * gn_i;
|
||||
sigma_time += time[i];
|
||||
sigma_time_gn += time[i] * gn_i;
|
||||
}
|
||||
|
||||
LeastSq result;
|
||||
result.complexity = oLambda;
|
||||
|
||||
// Calculate complexity.
|
||||
result.coef = sigma_time_gn / sigma_gn_squared;
|
||||
|
||||
// Calculate RMS
|
||||
double rms = 0.0;
|
||||
for (size_t i = 0; i < n.size(); ++i) {
|
||||
double fit = result.coef * fitting_curve(n[i]);
|
||||
rms += pow((time[i] - fit), 2);
|
||||
}
|
||||
|
||||
// Normalized RMS by the mean of the observed values
|
||||
double mean = sigma_time / n.size();
|
||||
result.rms = sqrt(rms / n.size()) / mean;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Find the coefficient for the high-order term in the running time, by
|
||||
// minimizing the sum of squares of relative error.
|
||||
// - n : Vector containing the size of the benchmark tests.
|
||||
// - time : Vector containing the times for the benchmark tests.
|
||||
// - complexity : If different than oAuto, the fitting curve will stick to
|
||||
// this one. If it is oAuto, it will be calculated the best
|
||||
// fitting curve.
|
||||
LeastSq MinimalLeastSq(const std::vector<int>& n,
|
||||
const std::vector<double>& time, const BigO complexity) {
|
||||
CHECK_EQ(n.size(), time.size());
|
||||
CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two
|
||||
// benchmark runs are given
|
||||
CHECK_NE(complexity, oNone);
|
||||
|
||||
LeastSq best_fit;
|
||||
|
||||
if (complexity == oAuto) {
|
||||
std::vector<BigO> fit_curves = {oLogN, oN, oNLogN, oNSquared, oNCubed};
|
||||
|
||||
// Take o1 as default best fitting curve
|
||||
best_fit = MinimalLeastSq(n, time, FittingCurve(o1));
|
||||
best_fit.complexity = o1;
|
||||
|
||||
// Compute all possible fitting curves and stick to the best one
|
||||
for (const auto& fit : fit_curves) {
|
||||
LeastSq current_fit = MinimalLeastSq(n, time, FittingCurve(fit));
|
||||
if (current_fit.rms < best_fit.rms) {
|
||||
best_fit = current_fit;
|
||||
best_fit.complexity = fit;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
best_fit = MinimalLeastSq(n, time, FittingCurve(complexity));
|
||||
best_fit.complexity = complexity;
|
||||
}
|
||||
|
||||
return best_fit;
|
||||
}
|
||||
|
||||
std::vector<BenchmarkReporter::Run> ComputeStats(
|
||||
const std::vector<BenchmarkReporter::Run>& reports) {
|
||||
typedef BenchmarkReporter::Run Run;
|
||||
std::vector<Run> results;
|
||||
|
||||
auto error_count =
|
||||
std::count_if(reports.begin(), reports.end(),
|
||||
[](Run const& run) { return run.error_occurred; });
|
||||
|
||||
if (reports.size() - error_count < 2) {
|
||||
// We don't report aggregated data if there was a single run.
|
||||
return results;
|
||||
}
|
||||
// Accumulators.
|
||||
Stat1_d real_accumulated_time_stat;
|
||||
Stat1_d cpu_accumulated_time_stat;
|
||||
Stat1_d bytes_per_second_stat;
|
||||
Stat1_d items_per_second_stat;
|
||||
// All repetitions should be run with the same number of iterations so we
|
||||
// can take this information from the first benchmark.
|
||||
int64_t const run_iterations = reports.front().iterations;
|
||||
|
||||
// Populate the accumulators.
|
||||
for (Run const& run : reports) {
|
||||
CHECK_EQ(reports[0].benchmark_name, run.benchmark_name);
|
||||
CHECK_EQ(run_iterations, run.iterations);
|
||||
if (run.error_occurred) continue;
|
||||
real_accumulated_time_stat +=
|
||||
Stat1_d(run.real_accumulated_time / run.iterations, run.iterations);
|
||||
cpu_accumulated_time_stat +=
|
||||
Stat1_d(run.cpu_accumulated_time / run.iterations, run.iterations);
|
||||
items_per_second_stat += Stat1_d(run.items_per_second, run.iterations);
|
||||
bytes_per_second_stat += Stat1_d(run.bytes_per_second, run.iterations);
|
||||
}
|
||||
|
||||
// Get the data from the accumulator to BenchmarkReporter::Run's.
|
||||
Run mean_data;
|
||||
mean_data.benchmark_name = reports[0].benchmark_name + "_mean";
|
||||
mean_data.iterations = run_iterations;
|
||||
mean_data.real_accumulated_time =
|
||||
real_accumulated_time_stat.Mean() * run_iterations;
|
||||
mean_data.cpu_accumulated_time =
|
||||
cpu_accumulated_time_stat.Mean() * run_iterations;
|
||||
mean_data.bytes_per_second = bytes_per_second_stat.Mean();
|
||||
mean_data.items_per_second = items_per_second_stat.Mean();
|
||||
mean_data.time_unit = reports[0].time_unit;
|
||||
|
||||
// Only add label to mean/stddev if it is same for all runs
|
||||
mean_data.report_label = reports[0].report_label;
|
||||
for (std::size_t i = 1; i < reports.size(); i++) {
|
||||
if (reports[i].report_label != reports[0].report_label) {
|
||||
mean_data.report_label = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Run stddev_data;
|
||||
stddev_data.benchmark_name = reports[0].benchmark_name + "_stddev";
|
||||
stddev_data.report_label = mean_data.report_label;
|
||||
stddev_data.iterations = 0;
|
||||
stddev_data.real_accumulated_time = real_accumulated_time_stat.StdDev();
|
||||
stddev_data.cpu_accumulated_time = cpu_accumulated_time_stat.StdDev();
|
||||
stddev_data.bytes_per_second = bytes_per_second_stat.StdDev();
|
||||
stddev_data.items_per_second = items_per_second_stat.StdDev();
|
||||
stddev_data.time_unit = reports[0].time_unit;
|
||||
|
||||
results.push_back(mean_data);
|
||||
results.push_back(stddev_data);
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<BenchmarkReporter::Run> ComputeBigO(
|
||||
const std::vector<BenchmarkReporter::Run>& reports) {
|
||||
typedef BenchmarkReporter::Run Run;
|
||||
std::vector<Run> results;
|
||||
|
||||
if (reports.size() < 2) return results;
|
||||
|
||||
// Accumulators.
|
||||
std::vector<int> n;
|
||||
std::vector<double> real_time;
|
||||
std::vector<double> cpu_time;
|
||||
|
||||
// Populate the accumulators.
|
||||
for (const Run& run : reports) {
|
||||
CHECK_GT(run.complexity_n, 0) << "Did you forget to call SetComplexityN?";
|
||||
n.push_back(run.complexity_n);
|
||||
real_time.push_back(run.real_accumulated_time / run.iterations);
|
||||
cpu_time.push_back(run.cpu_accumulated_time / run.iterations);
|
||||
}
|
||||
|
||||
LeastSq result_cpu;
|
||||
LeastSq result_real;
|
||||
|
||||
if (reports[0].complexity == oLambda) {
|
||||
result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity_lambda);
|
||||
result_real = MinimalLeastSq(n, real_time, reports[0].complexity_lambda);
|
||||
} else {
|
||||
result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity);
|
||||
result_real = MinimalLeastSq(n, real_time, result_cpu.complexity);
|
||||
}
|
||||
std::string benchmark_name =
|
||||
reports[0].benchmark_name.substr(0, reports[0].benchmark_name.find('/'));
|
||||
|
||||
// Get the data from the accumulator to BenchmarkReporter::Run's.
|
||||
Run big_o;
|
||||
big_o.benchmark_name = benchmark_name + "_BigO";
|
||||
big_o.iterations = 0;
|
||||
big_o.real_accumulated_time = result_real.coef;
|
||||
big_o.cpu_accumulated_time = result_cpu.coef;
|
||||
big_o.report_big_o = true;
|
||||
big_o.complexity = result_cpu.complexity;
|
||||
|
||||
double multiplier = GetTimeUnitMultiplier(reports[0].time_unit);
|
||||
|
||||
// Only add label to mean/stddev if it is same for all runs
|
||||
Run rms;
|
||||
big_o.report_label = reports[0].report_label;
|
||||
rms.benchmark_name = benchmark_name + "_RMS";
|
||||
rms.report_label = big_o.report_label;
|
||||
rms.iterations = 0;
|
||||
rms.real_accumulated_time = result_real.rms / multiplier;
|
||||
rms.cpu_accumulated_time = result_cpu.rms / multiplier;
|
||||
rms.report_rms = true;
|
||||
rms.complexity = result_cpu.complexity;
|
||||
|
||||
results.push_back(big_o);
|
||||
results.push_back(rms);
|
||||
return results;
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Source project : https://github.com/ismaelJimenez/cpp.leastsq
|
||||
// Adapted to be used with google benchmark
|
||||
|
||||
#ifndef COMPLEXITY_H_
|
||||
#define COMPLEXITY_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
#include "benchmark/reporter.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
// Return a vector containing the mean and standard devation information for
|
||||
// the specified list of reports. If 'reports' contains less than two
|
||||
// non-errored runs an empty vector is returned
|
||||
std::vector<BenchmarkReporter::Run> ComputeStats(
|
||||
const std::vector<BenchmarkReporter::Run>& reports);
|
||||
|
||||
// Return a vector containing the bigO and RMS information for the specified
|
||||
// list of reports. If 'reports.size() < 2' an empty vector is returned.
|
||||
std::vector<BenchmarkReporter::Run> ComputeBigO(
|
||||
const std::vector<BenchmarkReporter::Run>& reports);
|
||||
|
||||
// This data structure will contain the result returned by MinimalLeastSq
|
||||
// - coef : Estimated coeficient for the high-order term as
|
||||
// interpolated from data.
|
||||
// - rms : Normalized Root Mean Squared Error.
|
||||
// - complexity : Scalability form (e.g. oN, oNLogN). In case a scalability
|
||||
// form has been provided to MinimalLeastSq this will return
|
||||
// the same value. In case BigO::oAuto has been selected, this
|
||||
// parameter will return the best fitting curve detected.
|
||||
|
||||
struct LeastSq {
|
||||
LeastSq() : coef(0.0), rms(0.0), complexity(oNone) {}
|
||||
|
||||
double coef;
|
||||
double rms;
|
||||
BigO complexity;
|
||||
};
|
||||
|
||||
// Function to return an string for the calculated complexity
|
||||
std::string GetBigOString(BigO complexity);
|
||||
|
||||
} // end namespace benchmark
|
||||
#endif // COMPLEXITY_H_
|
||||
@@ -1,132 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "benchmark/reporter.h"
|
||||
#include "complexity.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "check.h"
|
||||
#include "colorprint.h"
|
||||
#include "commandlineflags.h"
|
||||
#include "internal_macros.h"
|
||||
#include "string_util.h"
|
||||
#include "timers.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
bool ConsoleReporter::ReportContext(const Context& context) {
|
||||
name_field_width_ = context.name_field_width;
|
||||
|
||||
PrintBasicContext(&GetErrorStream(), context);
|
||||
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
if (color_output_ && &std::cout != &GetOutputStream()) {
|
||||
GetErrorStream()
|
||||
<< "Color printing is only supported for stdout on windows."
|
||||
" Disabling color printing\n";
|
||||
color_output_ = false;
|
||||
}
|
||||
#endif
|
||||
std::string str =
|
||||
FormatString("%-*s %13s %13s %10s\n", static_cast<int>(name_field_width_),
|
||||
"Benchmark", "Time", "CPU", "Iterations");
|
||||
GetOutputStream() << str << std::string(str.length() - 1, '-') << "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) {
|
||||
for (const auto& run : reports) PrintRunData(run);
|
||||
}
|
||||
|
||||
static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt,
|
||||
...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
out << FormatString(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void ConsoleReporter::PrintRunData(const Run& result) {
|
||||
typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...);
|
||||
auto& Out = GetOutputStream();
|
||||
PrinterFn* printer =
|
||||
color_output_ ? (PrinterFn*)ColorPrintf : IgnoreColorPrint;
|
||||
auto name_color =
|
||||
(result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN;
|
||||
printer(Out, name_color, "%-*s ", name_field_width_,
|
||||
result.benchmark_name.c_str());
|
||||
|
||||
if (result.error_occurred) {
|
||||
printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'",
|
||||
result.error_message.c_str());
|
||||
printer(Out, COLOR_DEFAULT, "\n");
|
||||
return;
|
||||
}
|
||||
// Format bytes per second
|
||||
std::string rate;
|
||||
if (result.bytes_per_second > 0) {
|
||||
rate = StrCat(" ", HumanReadableNumber(result.bytes_per_second), "B/s");
|
||||
}
|
||||
|
||||
// Format items per second
|
||||
std::string items;
|
||||
if (result.items_per_second > 0) {
|
||||
items =
|
||||
StrCat(" ", HumanReadableNumber(result.items_per_second), " items/s");
|
||||
}
|
||||
|
||||
const double real_time = result.GetAdjustedRealTime();
|
||||
const double cpu_time = result.GetAdjustedCPUTime();
|
||||
|
||||
if (result.report_big_o) {
|
||||
std::string big_o = GetBigOString(result.complexity);
|
||||
printer(Out, COLOR_YELLOW, "%10.2f %s %10.2f %s ", real_time, big_o.c_str(),
|
||||
cpu_time, big_o.c_str());
|
||||
} else if (result.report_rms) {
|
||||
printer(Out, COLOR_YELLOW, "%10.0f %% %10.0f %% ", real_time * 100,
|
||||
cpu_time * 100);
|
||||
} else {
|
||||
const char* timeLabel = GetTimeUnitString(result.time_unit);
|
||||
printer(Out, COLOR_YELLOW, "%10.0f %s %10.0f %s ", real_time, timeLabel,
|
||||
cpu_time, timeLabel);
|
||||
}
|
||||
|
||||
if (!result.report_big_o && !result.report_rms) {
|
||||
printer(Out, COLOR_CYAN, "%10lld", result.iterations);
|
||||
}
|
||||
|
||||
if (!rate.empty()) {
|
||||
printer(Out, COLOR_DEFAULT, " %*s", 13, rate.c_str());
|
||||
}
|
||||
|
||||
if (!items.empty()) {
|
||||
printer(Out, COLOR_DEFAULT, " %*s", 18, items.c_str());
|
||||
}
|
||||
|
||||
if (!result.report_label.empty()) {
|
||||
printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str());
|
||||
}
|
||||
|
||||
printer(Out, COLOR_DEFAULT, "\n");
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "benchmark/reporter.h"
|
||||
#include "complexity.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "string_util.h"
|
||||
#include "timers.h"
|
||||
|
||||
// File format reference: http://edoceo.com/utilitas/csv-file-format.
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
namespace {
|
||||
std::vector<std::string> elements = {
|
||||
"name", "iterations", "real_time", "cpu_time",
|
||||
"time_unit", "bytes_per_second", "items_per_second", "label",
|
||||
"error_occurred", "error_message"};
|
||||
}
|
||||
|
||||
bool CSVReporter::ReportContext(const Context& context) {
|
||||
PrintBasicContext(&GetErrorStream(), context);
|
||||
|
||||
std::ostream& Out = GetOutputStream();
|
||||
for (auto B = elements.begin(); B != elements.end();) {
|
||||
Out << *B++;
|
||||
if (B != elements.end()) Out << ",";
|
||||
}
|
||||
Out << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSVReporter::ReportRuns(const std::vector<Run>& reports) {
|
||||
for (const auto& run : reports) PrintRunData(run);
|
||||
}
|
||||
|
||||
void CSVReporter::PrintRunData(const Run& run) {
|
||||
std::ostream& Out = GetOutputStream();
|
||||
|
||||
// Field with embedded double-quote characters must be doubled and the field
|
||||
// delimited with double-quotes.
|
||||
std::string name = run.benchmark_name;
|
||||
ReplaceAll(&name, "\"", "\"\"");
|
||||
Out << '"' << name << "\",";
|
||||
if (run.error_occurred) {
|
||||
Out << std::string(elements.size() - 3, ',');
|
||||
Out << "true,";
|
||||
std::string msg = run.error_message;
|
||||
ReplaceAll(&msg, "\"", "\"\"");
|
||||
Out << '"' << msg << "\"\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not print iteration on bigO and RMS report
|
||||
if (!run.report_big_o && !run.report_rms) {
|
||||
Out << run.iterations;
|
||||
}
|
||||
Out << ",";
|
||||
|
||||
Out << run.GetAdjustedRealTime() << ",";
|
||||
Out << run.GetAdjustedCPUTime() << ",";
|
||||
|
||||
// Do not print timeLabel on bigO and RMS report
|
||||
if (run.report_big_o) {
|
||||
Out << GetBigOString(run.complexity);
|
||||
} else if (!run.report_rms) {
|
||||
Out << GetTimeUnitString(run.time_unit);
|
||||
}
|
||||
Out << ",";
|
||||
|
||||
if (run.bytes_per_second > 0.0) {
|
||||
Out << run.bytes_per_second;
|
||||
}
|
||||
Out << ",";
|
||||
if (run.items_per_second > 0.0) {
|
||||
Out << run.items_per_second;
|
||||
}
|
||||
Out << ",";
|
||||
if (!run.report_label.empty()) {
|
||||
// Field with embedded double-quote characters must be doubled and the field
|
||||
// delimited with double-quotes.
|
||||
std::string label = run.report_label;
|
||||
ReplaceAll(&label, "\"", "\"\"");
|
||||
Out << "\"" << label << "\"";
|
||||
}
|
||||
Out << ",,"; // for error_occurred and error_message
|
||||
Out << '\n';
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,145 +0,0 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// CycleClock
|
||||
// A CycleClock tells you the current time in Cycles. The "time"
|
||||
// is actually time since power-on. This is like time() but doesn't
|
||||
// involve a system call and is much more precise.
|
||||
//
|
||||
// NOTE: Not all cpu/platform/kernel combinations guarantee that this
|
||||
// clock increments at a constant rate or is synchronized across all logical
|
||||
// cpus in a system.
|
||||
//
|
||||
// If you need the above guarantees, please consider using a different
|
||||
// API. There are efforts to provide an interface which provides a millisecond
|
||||
// granularity and implemented as a memory read. A memory read is generally
|
||||
// cheaper than the CycleClock for many architectures.
|
||||
//
|
||||
// Also, in some out of order CPU implementations, the CycleClock is not
|
||||
// serializing. So if you're trying to count at cycles granularity, your
|
||||
// data might be inaccurate due to out of order instruction execution.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#ifndef BENCHMARK_CYCLECLOCK_H_
|
||||
#define BENCHMARK_CYCLECLOCK_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "benchmark/macros.h"
|
||||
#include "internal_macros.h"
|
||||
|
||||
#if defined(BENCHMARK_OS_MACOSX)
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
// For MSVC, we want to use '_asm rdtsc' when possible (since it works
|
||||
// with even ancient MSVC compilers), and when not possible the
|
||||
// __rdtsc intrinsic, declared in <intrin.h>. Unfortunately, in some
|
||||
// environments, <windows.h> and <intrin.h> have conflicting
|
||||
// declarations of some other intrinsics, breaking compilation.
|
||||
// Therefore, we simply declare __rdtsc ourselves. See also
|
||||
// http://connect.microsoft.com/VisualStudio/feedback/details/262047
|
||||
#if defined(COMPILER_MSVC) && !defined(_M_IX86)
|
||||
extern "C" uint64_t __rdtsc();
|
||||
#pragma intrinsic(__rdtsc)
|
||||
#endif
|
||||
|
||||
#ifndef BENCHMARK_OS_WINDOWS
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
namespace benchmark {
|
||||
// NOTE: only i386 and x86_64 have been well tested.
|
||||
// PPC, sparc, alpha, and ia64 are based on
|
||||
// http://peter.kuscsik.com/wordpress/?p=14
|
||||
// with modifications by m3b. See also
|
||||
// https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h
|
||||
namespace cycleclock {
|
||||
// This should return the number of cycles since power-on. Thread-safe.
|
||||
inline BENCHMARK_ALWAYS_INLINE int64_t Now() {
|
||||
#if defined(BENCHMARK_OS_MACOSX)
|
||||
// this goes at the top because we need ALL Macs, regardless of
|
||||
// architecture, to return the number of "mach time units" that
|
||||
// have passed since startup. See sysinfo.cc where
|
||||
// InitializeSystemInfo() sets the supposed cpu clock frequency of
|
||||
// macs to the number of mach time units per second, not actual
|
||||
// CPU clock frequency (which can change in the face of CPU
|
||||
// frequency scaling). Also note that when the Mac sleeps, this
|
||||
// counter pauses; it does not continue counting, nor does it
|
||||
// reset to zero.
|
||||
return mach_absolute_time();
|
||||
#elif defined(__i386__)
|
||||
int64_t ret;
|
||||
__asm__ volatile("rdtsc" : "=A"(ret));
|
||||
return ret;
|
||||
#elif defined(__x86_64__) || defined(__amd64__)
|
||||
uint64_t low, high;
|
||||
__asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
|
||||
return (high << 32) | low;
|
||||
#elif defined(__powerpc__) || defined(__ppc__)
|
||||
// This returns a time-base, which is not always precisely a cycle-count.
|
||||
int64_t tbl, tbu0, tbu1;
|
||||
asm("mftbu %0" : "=r"(tbu0));
|
||||
asm("mftb %0" : "=r"(tbl));
|
||||
asm("mftbu %0" : "=r"(tbu1));
|
||||
tbl &= -static_cast<int64>(tbu0 == tbu1);
|
||||
// high 32 bits in tbu1; low 32 bits in tbl (tbu0 is garbage)
|
||||
return (tbu1 << 32) | tbl;
|
||||
#elif defined(__sparc__)
|
||||
int64_t tick;
|
||||
asm(".byte 0x83, 0x41, 0x00, 0x00");
|
||||
asm("mov %%g1, %0" : "=r"(tick));
|
||||
return tick;
|
||||
#elif defined(__ia64__)
|
||||
int64_t itc;
|
||||
asm("mov %0 = ar.itc" : "=r"(itc));
|
||||
return itc;
|
||||
#elif defined(COMPILER_MSVC) && defined(_M_IX86)
|
||||
// Older MSVC compilers (like 7.x) don't seem to support the
|
||||
// __rdtsc intrinsic properly, so I prefer to use _asm instead
|
||||
// when I know it will work. Otherwise, I'll use __rdtsc and hope
|
||||
// the code is being compiled with a non-ancient compiler.
|
||||
_asm rdtsc
|
||||
#elif defined(COMPILER_MSVC)
|
||||
return __rdtsc();
|
||||
#elif defined(__aarch64__)
|
||||
// System timer of ARMv8 runs at a different frequency than the CPU's.
|
||||
// The frequency is fixed, typically in the range 1-50MHz. It can be
|
||||
// read at CNTFRQ special register. We assume the OS has set up
|
||||
// the virtual timer properly.
|
||||
int64_t virtual_timer_value;
|
||||
asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
|
||||
return virtual_timer_value;
|
||||
#elif defined(__ARM_ARCH)
|
||||
#if (__ARM_ARCH >= 6) // V6 is the earliest arch that has a standard cyclecount
|
||||
uint32_t pmccntr;
|
||||
uint32_t pmuseren;
|
||||
uint32_t pmcntenset;
|
||||
// Read the user mode perf monitor counter access permissions.
|
||||
asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
|
||||
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
|
||||
asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
|
||||
if (pmcntenset & 0x80000000ul) { // Is it counting?
|
||||
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
|
||||
// The counter is set up to count every 64th cycle
|
||||
return static_cast<int64_t>(pmccntr) * 64; // Should optimize to << 6
|
||||
}
|
||||
}
|
||||
#endif
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
|
||||
#elif defined(__mips__)
|
||||
// mips apparently only allows rdtsc for superusers, so we fall
|
||||
// back to gettimeofday. It's possible clock_gettime would be better.
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
|
||||
#else
|
||||
// The soft failover to a generic implementation is automatic only for ARM.
|
||||
// For other platforms the developer is expected to make an attempt to create
|
||||
// a fast implementation and use generic version if nothing better is available.
|
||||
#error You need to define CycleTimer for your OS and CPU
|
||||
#endif
|
||||
}
|
||||
} // end namespace cycleclock
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_CYCLECLOCK_H_
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef BENCHMARK_INTERNAL_MACROS_H_
|
||||
#define BENCHMARK_INTERNAL_MACROS_H_
|
||||
|
||||
#include "benchmark/macros.h"
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#define COMPILER_CLANG
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_MSVC
|
||||
#elif defined(__GNUC__)
|
||||
#define COMPILER_GCC
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_attributes)
|
||||
#define BENCHMARK_NORETURN [[noreturn]]
|
||||
#elif defined(__GNUC__)
|
||||
#define BENCHMARK_NORETURN __attribute__((noreturn))
|
||||
#elif defined(COMPILER_MSVC)
|
||||
#define BENCHMARK_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define BENCHMARK_NORETURN
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
#define BENCHMARK_OS_CYGWIN 1
|
||||
#elif defined(_WIN32)
|
||||
#define BENCHMARK_OS_WINDOWS 1
|
||||
#elif defined(__APPLE__)
|
||||
// TODO(ericwf) This doesn't actually check that it is a Mac OSX system. Just
|
||||
// that it is an apple system.
|
||||
#define BENCHMARK_OS_MACOSX 1
|
||||
#elif defined(__FreeBSD__)
|
||||
#define BENCHMARK_OS_FREEBSD 1
|
||||
#elif defined(__linux__)
|
||||
#define BENCHMARK_OS_LINUX 1
|
||||
#endif
|
||||
|
||||
#endif // BENCHMARK_INTERNAL_MACROS_H_
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "benchmark/reporter.h"
|
||||
#include "complexity.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "string_util.h"
|
||||
#include "timers.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string FormatKV(std::string const& key, std::string const& value) {
|
||||
return StringPrintF("\"%s\": \"%s\"", key.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
std::string FormatKV(std::string const& key, const char* value) {
|
||||
return StringPrintF("\"%s\": \"%s\"", key.c_str(), value);
|
||||
}
|
||||
|
||||
std::string FormatKV(std::string const& key, bool value) {
|
||||
return StringPrintF("\"%s\": %s", key.c_str(), value ? "true" : "false");
|
||||
}
|
||||
|
||||
std::string FormatKV(std::string const& key, int64_t value) {
|
||||
std::stringstream ss;
|
||||
ss << '"' << key << "\": " << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string FormatKV(std::string const& key, double value) {
|
||||
return StringPrintF("\"%s\": %.2f", key.c_str(), value);
|
||||
}
|
||||
|
||||
int64_t RoundDouble(double v) { return static_cast<int64_t>(v + 0.5); }
|
||||
|
||||
} // end namespace
|
||||
|
||||
bool JSONReporter::ReportContext(const Context& context) {
|
||||
std::ostream& out = GetOutputStream();
|
||||
|
||||
out << "{\n";
|
||||
std::string inner_indent(2, ' ');
|
||||
|
||||
// Open context block and print context information.
|
||||
out << inner_indent << "\"context\": {\n";
|
||||
std::string indent(4, ' ');
|
||||
|
||||
std::string walltime_value = LocalDateTimeString();
|
||||
out << indent << FormatKV("date", walltime_value) << ",\n";
|
||||
|
||||
out << indent << FormatKV("num_cpus", static_cast<int64_t>(context.num_cpus))
|
||||
<< ",\n";
|
||||
out << indent << FormatKV("mhz_per_cpu", RoundDouble(context.mhz_per_cpu))
|
||||
<< ",\n";
|
||||
out << indent << FormatKV("cpu_scaling_enabled", context.cpu_scaling_enabled)
|
||||
<< ",\n";
|
||||
|
||||
#if defined(NDEBUG)
|
||||
const char build_type[] = "release";
|
||||
#else
|
||||
const char build_type[] = "debug";
|
||||
#endif
|
||||
out << indent << FormatKV("library_build_type", build_type) << "\n";
|
||||
// Close context block and open the list of benchmarks.
|
||||
out << inner_indent << "},\n";
|
||||
out << inner_indent << "\"benchmarks\": [\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
void JSONReporter::ReportRuns(std::vector<Run> const& reports) {
|
||||
if (reports.empty()) {
|
||||
return;
|
||||
}
|
||||
std::string indent(4, ' ');
|
||||
std::ostream& out = GetOutputStream();
|
||||
if (!first_report_) {
|
||||
out << ",\n";
|
||||
}
|
||||
first_report_ = false;
|
||||
|
||||
for (auto it = reports.begin(); it != reports.end(); ++it) {
|
||||
out << indent << "{\n";
|
||||
PrintRunData(*it);
|
||||
out << indent << '}';
|
||||
auto it_cp = it;
|
||||
if (++it_cp != reports.end()) {
|
||||
out << ",\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JSONReporter::Finalize() {
|
||||
// Close the list of benchmarks and the top level object.
|
||||
GetOutputStream() << "\n ]\n}\n";
|
||||
}
|
||||
|
||||
void JSONReporter::PrintRunData(Run const& run) {
|
||||
std::string indent(6, ' ');
|
||||
std::ostream& out = GetOutputStream();
|
||||
out << indent << FormatKV("name", run.benchmark_name) << ",\n";
|
||||
if (run.error_occurred) {
|
||||
out << indent << FormatKV("error_occurred", run.error_occurred) << ",\n";
|
||||
out << indent << FormatKV("error_message", run.error_message) << ",\n";
|
||||
}
|
||||
if (!run.report_big_o && !run.report_rms) {
|
||||
out << indent << FormatKV("iterations", run.iterations) << ",\n";
|
||||
out << indent
|
||||
<< FormatKV("real_time", RoundDouble(run.GetAdjustedRealTime()))
|
||||
<< ",\n";
|
||||
out << indent
|
||||
<< FormatKV("cpu_time", RoundDouble(run.GetAdjustedCPUTime()));
|
||||
out << ",\n"
|
||||
<< indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
|
||||
} else if (run.report_big_o) {
|
||||
out << indent
|
||||
<< FormatKV("cpu_coefficient", RoundDouble(run.GetAdjustedCPUTime()))
|
||||
<< ",\n";
|
||||
out << indent
|
||||
<< FormatKV("real_coefficient", RoundDouble(run.GetAdjustedRealTime()))
|
||||
<< ",\n";
|
||||
out << indent << FormatKV("big_o", GetBigOString(run.complexity)) << ",\n";
|
||||
out << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
|
||||
} else if (run.report_rms) {
|
||||
out << indent
|
||||
<< FormatKV("rms", run.GetAdjustedCPUTime());
|
||||
}
|
||||
if (run.bytes_per_second > 0.0) {
|
||||
out << ",\n"
|
||||
<< indent
|
||||
<< FormatKV("bytes_per_second", RoundDouble(run.bytes_per_second));
|
||||
}
|
||||
if (run.items_per_second > 0.0) {
|
||||
out << ",\n"
|
||||
<< indent
|
||||
<< FormatKV("items_per_second", RoundDouble(run.items_per_second));
|
||||
}
|
||||
if (!run.report_label.empty()) {
|
||||
out << ",\n" << indent << FormatKV("label", run.report_label);
|
||||
}
|
||||
out << '\n';
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,73 +0,0 @@
|
||||
#ifndef BENCHMARK_LOG_H_
|
||||
#define BENCHMARK_LOG_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
#include "benchmark/macros.h"
|
||||
|
||||
namespace benchmark {
|
||||
namespace internal {
|
||||
|
||||
typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);
|
||||
|
||||
class LogType {
|
||||
friend LogType& GetNullLogInstance();
|
||||
friend LogType& GetErrorLogInstance();
|
||||
|
||||
// FIXME: Add locking to output.
|
||||
template <class Tp>
|
||||
friend LogType& operator<<(LogType&, Tp const&);
|
||||
friend LogType& operator<<(LogType&, EndLType*);
|
||||
|
||||
private:
|
||||
LogType(std::ostream* out) : out_(out) {}
|
||||
std::ostream* out_;
|
||||
BENCHMARK_DISALLOW_COPY_AND_ASSIGN(LogType);
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
LogType& operator<<(LogType& log, Tp const& value) {
|
||||
if (log.out_) {
|
||||
*log.out_ << value;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
inline LogType& operator<<(LogType& log, EndLType* m) {
|
||||
if (log.out_) {
|
||||
*log.out_ << m;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
inline int& LogLevel() {
|
||||
static int log_level = 0;
|
||||
return log_level;
|
||||
}
|
||||
|
||||
inline LogType& GetNullLogInstance() {
|
||||
static LogType log(nullptr);
|
||||
return log;
|
||||
}
|
||||
|
||||
inline LogType& GetErrorLogInstance() {
|
||||
static LogType log(&std::clog);
|
||||
return log;
|
||||
}
|
||||
|
||||
inline LogType& GetLogInstanceForLevel(int level) {
|
||||
if (level <= LogLevel()) {
|
||||
return GetErrorLogInstance();
|
||||
}
|
||||
return GetNullLogInstance();
|
||||
}
|
||||
|
||||
} // end namespace internal
|
||||
} // end namespace benchmark
|
||||
|
||||
#define VLOG(x) \
|
||||
(::benchmark::internal::GetLogInstanceForLevel(x) << "-- LOG(" << x << "):" \
|
||||
" ")
|
||||
|
||||
#endif
|
||||
@@ -1,155 +0,0 @@
|
||||
#ifndef BENCHMARK_MUTEX_H_
|
||||
#define BENCHMARK_MUTEX_H_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "check.h"
|
||||
|
||||
// Enable thread safety attributes only with clang.
|
||||
// The attributes can be safely erased when compiling with other compilers.
|
||||
#if defined(HAVE_THREAD_SAFETY_ATTRIBUTES)
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
#else
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
#endif
|
||||
|
||||
#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
|
||||
|
||||
#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
|
||||
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
|
||||
#define ACQUIRED_BEFORE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRED_AFTER(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||
|
||||
#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
|
||||
|
||||
#define ASSERT_SHARED_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
|
||||
|
||||
#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
typedef std::condition_variable Condition;
|
||||
|
||||
// NOTE: Wrappers for std::mutex and std::unique_lock are provided so that
|
||||
// we can annotate them with thread safety attributes and use the
|
||||
// -Wthread-safety warning with clang. The standard library types cannot be
|
||||
// used directly because they do not provided the required annotations.
|
||||
class CAPABILITY("mutex") Mutex {
|
||||
public:
|
||||
Mutex() {}
|
||||
|
||||
void lock() ACQUIRE() { mut_.lock(); }
|
||||
void unlock() RELEASE() { mut_.unlock(); }
|
||||
std::mutex& native_handle() { return mut_; }
|
||||
|
||||
private:
|
||||
std::mutex mut_;
|
||||
};
|
||||
|
||||
class SCOPED_CAPABILITY MutexLock {
|
||||
typedef std::unique_lock<std::mutex> MutexLockImp;
|
||||
|
||||
public:
|
||||
MutexLock(Mutex& m) ACQUIRE(m) : ml_(m.native_handle()) {}
|
||||
~MutexLock() RELEASE() {}
|
||||
MutexLockImp& native_handle() { return ml_; }
|
||||
|
||||
private:
|
||||
MutexLockImp ml_;
|
||||
};
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
Barrier(int num_threads) : running_threads_(num_threads) {}
|
||||
|
||||
// Called by each thread
|
||||
bool wait() EXCLUDES(lock_) {
|
||||
bool last_thread = false;
|
||||
{
|
||||
MutexLock ml(lock_);
|
||||
last_thread = createBarrier(ml);
|
||||
}
|
||||
if (last_thread) phase_condition_.notify_all();
|
||||
return last_thread;
|
||||
}
|
||||
|
||||
void removeThread() EXCLUDES(lock_) {
|
||||
MutexLock ml(lock_);
|
||||
--running_threads_;
|
||||
if (entered_ != 0) phase_condition_.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex lock_;
|
||||
Condition phase_condition_;
|
||||
int running_threads_;
|
||||
|
||||
// State for barrier management
|
||||
int phase_number_ = 0;
|
||||
int entered_ = 0; // Number of threads that have entered this barrier
|
||||
|
||||
// Enter the barrier and wait until all other threads have also
|
||||
// entered the barrier. Returns iff this is the last thread to
|
||||
// enter the barrier.
|
||||
bool createBarrier(MutexLock& ml) REQUIRES(lock_) {
|
||||
CHECK_LT(entered_, running_threads_);
|
||||
entered_++;
|
||||
if (entered_ < running_threads_) {
|
||||
// Wait for all threads to enter
|
||||
int phase_number_cp = phase_number_;
|
||||
auto cb = [this, phase_number_cp]() {
|
||||
return this->phase_number_ > phase_number_cp ||
|
||||
entered_ == running_threads_; // A thread has aborted in error
|
||||
};
|
||||
phase_condition_.wait(ml.native_handle(), cb);
|
||||
if (phase_number_ > phase_number_cp) return false;
|
||||
// else (running_threads_ == entered_) and we are the last thread.
|
||||
}
|
||||
// Last thread has reached the barrier
|
||||
phase_number_++;
|
||||
entered_ = 0;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_MUTEX_H_
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef BENCHMARK_RE_H_
|
||||
#define BENCHMARK_RE_H_
|
||||
|
||||
#if defined(HAVE_STD_REGEX)
|
||||
#include <regex>
|
||||
#elif defined(HAVE_GNU_POSIX_REGEX)
|
||||
#include <gnuregex.h>
|
||||
#elif defined(HAVE_POSIX_REGEX)
|
||||
#include <regex.h>
|
||||
#else
|
||||
#error No regular expression backend was found!
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
#include "check.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
// A wrapper around the POSIX regular expression API that provides automatic
|
||||
// cleanup
|
||||
class Regex {
|
||||
public:
|
||||
Regex() : init_(false) {}
|
||||
|
||||
~Regex();
|
||||
|
||||
// Compile a regular expression matcher from spec. Returns true on success.
|
||||
//
|
||||
// On failure (and if error is not nullptr), error is populated with a human
|
||||
// readable error message if an error occurs.
|
||||
bool Init(const std::string& spec, std::string* error);
|
||||
|
||||
// Returns whether str matches the compiled regular expression.
|
||||
bool Match(const std::string& str);
|
||||
|
||||
private:
|
||||
bool init_;
|
||||
// Underlying regular expression object
|
||||
#if defined(HAVE_STD_REGEX)
|
||||
std::regex re_;
|
||||
#elif defined(HAVE_POSIX_REGEX) || defined(HAVE_GNU_POSIX_REGEX)
|
||||
regex_t re_;
|
||||
#else
|
||||
#error No regular expression backend implementation available
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(HAVE_STD_REGEX)
|
||||
|
||||
inline bool Regex::Init(const std::string& spec, std::string* error) {
|
||||
try {
|
||||
re_ = std::regex(spec, std::regex_constants::extended);
|
||||
|
||||
init_ = true;
|
||||
} catch (const std::regex_error& e) {
|
||||
if (error) {
|
||||
*error = e.what();
|
||||
}
|
||||
}
|
||||
return init_;
|
||||
}
|
||||
|
||||
inline Regex::~Regex() {}
|
||||
|
||||
inline bool Regex::Match(const std::string& str) {
|
||||
if (!init_) {
|
||||
return false;
|
||||
}
|
||||
return std::regex_search(str, re_);
|
||||
}
|
||||
|
||||
#else
|
||||
inline bool Regex::Init(const std::string& spec, std::string* error) {
|
||||
int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB);
|
||||
if (ec != 0) {
|
||||
if (error) {
|
||||
size_t needed = regerror(ec, &re_, nullptr, 0);
|
||||
char* errbuf = new char[needed];
|
||||
regerror(ec, &re_, errbuf, needed);
|
||||
|
||||
// regerror returns the number of bytes necessary to null terminate
|
||||
// the string, so we move that when assigning to error.
|
||||
CHECK_NE(needed, 0);
|
||||
error->assign(errbuf, needed - 1);
|
||||
|
||||
delete[] errbuf;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
init_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Regex::~Regex() {
|
||||
if (init_) {
|
||||
regfree(&re_);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Regex::Match(const std::string& str) {
|
||||
if (!init_) {
|
||||
return false;
|
||||
}
|
||||
return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_RE_H_
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "benchmark/reporter.h"
|
||||
#include "timers.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "check.h"
|
||||
#include "stat.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
BenchmarkReporter::BenchmarkReporter()
|
||||
: output_stream_(&std::cout), error_stream_(&std::cerr) {}
|
||||
|
||||
BenchmarkReporter::~BenchmarkReporter() {}
|
||||
|
||||
void BenchmarkReporter::PrintBasicContext(std::ostream *out_ptr,
|
||||
Context const &context) {
|
||||
CHECK(out_ptr) << "cannot be null";
|
||||
auto &Out = *out_ptr;
|
||||
|
||||
Out << "Run on (" << context.num_cpus << " X " << context.mhz_per_cpu
|
||||
<< " MHz CPU " << ((context.num_cpus > 1) ? "s" : "") << ")\n";
|
||||
|
||||
Out << LocalDateTimeString() << "\n";
|
||||
|
||||
if (context.cpu_scaling_enabled) {
|
||||
Out << "***WARNING*** CPU scaling is enabled, the benchmark "
|
||||
"real time measurements may be noisy and will incur extra "
|
||||
"overhead.\n";
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
Out << "***WARNING*** Library was built as DEBUG. Timings may be "
|
||||
"affected.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
double BenchmarkReporter::Run::GetAdjustedRealTime() const {
|
||||
double new_time = real_accumulated_time * GetTimeUnitMultiplier(time_unit);
|
||||
if (iterations != 0) new_time /= static_cast<double>(iterations);
|
||||
return new_time;
|
||||
}
|
||||
|
||||
double BenchmarkReporter::Run::GetAdjustedCPUTime() const {
|
||||
double new_time = cpu_accumulated_time * GetTimeUnitMultiplier(time_unit);
|
||||
if (iterations != 0) new_time /= static_cast<double>(iterations);
|
||||
return new_time;
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "sleep.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
|
||||
#include "internal_macros.h"
|
||||
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace benchmark {
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
// Window's Sleep takes milliseconds argument.
|
||||
void SleepForMilliseconds(int milliseconds) { Sleep(milliseconds); }
|
||||
void SleepForSeconds(double seconds) {
|
||||
SleepForMilliseconds(static_cast<int>(kNumMillisPerSecond * seconds));
|
||||
}
|
||||
#else // BENCHMARK_OS_WINDOWS
|
||||
void SleepForMicroseconds(int microseconds) {
|
||||
struct timespec sleep_time;
|
||||
sleep_time.tv_sec = microseconds / kNumMicrosPerSecond;
|
||||
sleep_time.tv_nsec = (microseconds % kNumMicrosPerSecond) * kNumNanosPerMicro;
|
||||
while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR)
|
||||
; // Ignore signals and wait for the full interval to elapse.
|
||||
}
|
||||
|
||||
void SleepForMilliseconds(int milliseconds) {
|
||||
SleepForMicroseconds(static_cast<int>(milliseconds) * kNumMicrosPerMilli);
|
||||
}
|
||||
|
||||
void SleepForSeconds(double seconds) {
|
||||
SleepForMicroseconds(static_cast<int>(seconds * kNumMicrosPerSecond));
|
||||
}
|
||||
#endif // BENCHMARK_OS_WINDOWS
|
||||
} // end namespace benchmark
|
||||
@@ -1,17 +0,0 @@
|
||||
#ifndef BENCHMARK_SLEEP_H_
|
||||
#define BENCHMARK_SLEEP_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace benchmark {
|
||||
const int64_t kNumMillisPerSecond = 1000LL;
|
||||
const int64_t kNumMicrosPerMilli = 1000LL;
|
||||
const int64_t kNumMicrosPerSecond = kNumMillisPerSecond * 1000LL;
|
||||
const int64_t kNumNanosPerMicro = 1000LL;
|
||||
const int64_t kNumNanosPerSecond = kNumNanosPerMicro * kNumMicrosPerSecond;
|
||||
|
||||
void SleepForMilliseconds(int milliseconds);
|
||||
void SleepForSeconds(double seconds);
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_SLEEP_H_
|
||||
@@ -1,306 +0,0 @@
|
||||
#ifndef BENCHMARK_STAT_H_
|
||||
#define BENCHMARK_STAT_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
template <typename VType, typename NumType>
|
||||
class Stat1;
|
||||
|
||||
template <typename VType, typename NumType>
|
||||
class Stat1MinMax;
|
||||
|
||||
typedef Stat1<float, int64_t> Stat1_f;
|
||||
typedef Stat1<double, int64_t> Stat1_d;
|
||||
typedef Stat1MinMax<float, int64_t> Stat1MinMax_f;
|
||||
typedef Stat1MinMax<double, int64_t> Stat1MinMax_d;
|
||||
|
||||
template <typename VType>
|
||||
class Vector2;
|
||||
template <typename VType>
|
||||
class Vector3;
|
||||
template <typename VType>
|
||||
class Vector4;
|
||||
|
||||
template <typename VType, typename NumType>
|
||||
class Stat1 {
|
||||
public:
|
||||
typedef Stat1<VType, NumType> Self;
|
||||
|
||||
Stat1() { Clear(); }
|
||||
// Create a sample of value dat and weight 1
|
||||
explicit Stat1(const VType &dat) {
|
||||
sum_ = dat;
|
||||
sum_squares_ = Sqr(dat);
|
||||
numsamples_ = 1;
|
||||
}
|
||||
// Create statistics for all the samples between begin (included)
|
||||
// and end(excluded)
|
||||
explicit Stat1(const VType *begin, const VType *end) {
|
||||
Clear();
|
||||
for (const VType *item = begin; item < end; ++item) {
|
||||
(*this) += Stat1(*item);
|
||||
}
|
||||
}
|
||||
// Create a sample of value dat and weight w
|
||||
Stat1(const VType &dat, const NumType &w) {
|
||||
sum_ = w * dat;
|
||||
sum_squares_ = w * Sqr(dat);
|
||||
numsamples_ = w;
|
||||
}
|
||||
// Copy operator
|
||||
Stat1(const Self &stat) {
|
||||
sum_ = stat.sum_;
|
||||
sum_squares_ = stat.sum_squares_;
|
||||
numsamples_ = stat.numsamples_;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
numsamples_ = NumType();
|
||||
sum_squares_ = sum_ = VType();
|
||||
}
|
||||
|
||||
Self &operator=(const Self &stat) {
|
||||
sum_ = stat.sum_;
|
||||
sum_squares_ = stat.sum_squares_;
|
||||
numsamples_ = stat.numsamples_;
|
||||
return (*this);
|
||||
}
|
||||
// Merge statistics from two sample sets.
|
||||
Self &operator+=(const Self &stat) {
|
||||
sum_ += stat.sum_;
|
||||
sum_squares_ += stat.sum_squares_;
|
||||
numsamples_ += stat.numsamples_;
|
||||
return (*this);
|
||||
}
|
||||
// The operation opposite to +=
|
||||
Self &operator-=(const Self &stat) {
|
||||
sum_ -= stat.sum_;
|
||||
sum_squares_ -= stat.sum_squares_;
|
||||
numsamples_ -= stat.numsamples_;
|
||||
return (*this);
|
||||
}
|
||||
// Multiply the weight of the set of samples by a factor k
|
||||
Self &operator*=(const VType &k) {
|
||||
sum_ *= k;
|
||||
sum_squares_ *= k;
|
||||
numsamples_ *= k;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// Merge statistics from two sample sets.
|
||||
Self operator+(const Self &stat) const { return Self(*this) += stat; }
|
||||
|
||||
// The operation opposite to +
|
||||
Self operator-(const Self &stat) const { return Self(*this) -= stat; }
|
||||
|
||||
// Multiply the weight of the set of samples by a factor k
|
||||
Self operator*(const VType &k) const { return Self(*this) *= k; }
|
||||
|
||||
// Return the total weight of this sample set
|
||||
NumType numSamples() const { return numsamples_; }
|
||||
|
||||
// Return the sum of this sample set
|
||||
VType Sum() const { return sum_; }
|
||||
|
||||
// Return the mean of this sample set
|
||||
VType Mean() const {
|
||||
if (numsamples_ == 0) return VType();
|
||||
return sum_ * (1.0 / numsamples_);
|
||||
}
|
||||
|
||||
// Return the mean of this sample set and compute the standard deviation at
|
||||
// the same time.
|
||||
VType Mean(VType *stddev) const {
|
||||
if (numsamples_ == 0) return VType();
|
||||
VType mean = sum_ * (1.0 / numsamples_);
|
||||
if (stddev) {
|
||||
VType avg_squares = sum_squares_ * (1.0 / numsamples_);
|
||||
*stddev = Sqrt(avg_squares - Sqr(mean));
|
||||
}
|
||||
return mean;
|
||||
}
|
||||
|
||||
// Return the standard deviation of the sample set
|
||||
VType StdDev() const {
|
||||
if (numsamples_ == 0) return VType();
|
||||
VType mean = Mean();
|
||||
VType avg_squares = sum_squares_ * (1.0 / numsamples_);
|
||||
return Sqrt(avg_squares - Sqr(mean));
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(std::is_integral<NumType>::value &&
|
||||
!std::is_same<NumType, bool>::value,
|
||||
"NumType must be an integral type that is not bool.");
|
||||
// Let i be the index of the samples provided (using +=)
|
||||
// and weight[i],value[i] be the data of sample #i
|
||||
// then the variables have the following meaning:
|
||||
NumType numsamples_; // sum of weight[i];
|
||||
VType sum_; // sum of weight[i]*value[i];
|
||||
VType sum_squares_; // sum of weight[i]*value[i]^2;
|
||||
|
||||
// Template function used to square a number.
|
||||
// For a vector we square all components
|
||||
template <typename SType>
|
||||
static inline SType Sqr(const SType &dat) {
|
||||
return dat * dat;
|
||||
}
|
||||
|
||||
template <typename SType>
|
||||
static inline Vector2<SType> Sqr(const Vector2<SType> &dat) {
|
||||
return dat.MulComponents(dat);
|
||||
}
|
||||
|
||||
template <typename SType>
|
||||
static inline Vector3<SType> Sqr(const Vector3<SType> &dat) {
|
||||
return dat.MulComponents(dat);
|
||||
}
|
||||
|
||||
template <typename SType>
|
||||
static inline Vector4<SType> Sqr(const Vector4<SType> &dat) {
|
||||
return dat.MulComponents(dat);
|
||||
}
|
||||
|
||||
// Template function used to take the square root of a number.
|
||||
// For a vector we square all components
|
||||
template <typename SType>
|
||||
static inline SType Sqrt(const SType &dat) {
|
||||
// Avoid NaN due to imprecision in the calculations
|
||||
if (dat < 0) return 0;
|
||||
return sqrt(dat);
|
||||
}
|
||||
|
||||
template <typename SType>
|
||||
static inline Vector2<SType> Sqrt(const Vector2<SType> &dat) {
|
||||
// Avoid NaN due to imprecision in the calculations
|
||||
return Max(dat, Vector2<SType>()).Sqrt();
|
||||
}
|
||||
|
||||
template <typename SType>
|
||||
static inline Vector3<SType> Sqrt(const Vector3<SType> &dat) {
|
||||
// Avoid NaN due to imprecision in the calculations
|
||||
return Max(dat, Vector3<SType>()).Sqrt();
|
||||
}
|
||||
|
||||
template <typename SType>
|
||||
static inline Vector4<SType> Sqrt(const Vector4<SType> &dat) {
|
||||
// Avoid NaN due to imprecision in the calculations
|
||||
return Max(dat, Vector4<SType>()).Sqrt();
|
||||
}
|
||||
};
|
||||
|
||||
// Useful printing function
|
||||
template <typename VType, typename NumType>
|
||||
std::ostream &operator<<(std::ostream &out, const Stat1<VType, NumType> &s) {
|
||||
out << "{ avg = " << s.Mean() << " std = " << s.StdDev()
|
||||
<< " nsamples = " << s.NumSamples() << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
// Stat1MinMax: same as Stat1, but it also
|
||||
// keeps the Min and Max values; the "-"
|
||||
// operator is disabled because it cannot be implemented
|
||||
// efficiently
|
||||
template <typename VType, typename NumType>
|
||||
class Stat1MinMax : public Stat1<VType, NumType> {
|
||||
public:
|
||||
typedef Stat1MinMax<VType, NumType> Self;
|
||||
|
||||
Stat1MinMax() { Clear(); }
|
||||
// Create a sample of value dat and weight 1
|
||||
explicit Stat1MinMax(const VType &dat) : Stat1<VType, NumType>(dat) {
|
||||
max_ = dat;
|
||||
min_ = dat;
|
||||
}
|
||||
// Create statistics for all the samples between begin (included)
|
||||
// and end(excluded)
|
||||
explicit Stat1MinMax(const VType *begin, const VType *end) {
|
||||
Clear();
|
||||
for (const VType *item = begin; item < end; ++item) {
|
||||
(*this) += Stat1MinMax(*item);
|
||||
}
|
||||
}
|
||||
// Create a sample of value dat and weight w
|
||||
Stat1MinMax(const VType &dat, const NumType &w)
|
||||
: Stat1<VType, NumType>(dat, w) {
|
||||
max_ = dat;
|
||||
min_ = dat;
|
||||
}
|
||||
// Copy operator
|
||||
Stat1MinMax(const Self &stat) : Stat1<VType, NumType>(stat) {
|
||||
max_ = stat.max_;
|
||||
min_ = stat.min_;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
Stat1<VType, NumType>::Clear();
|
||||
if (std::numeric_limits<VType>::has_infinity) {
|
||||
min_ = std::numeric_limits<VType>::infinity();
|
||||
max_ = -std::numeric_limits<VType>::infinity();
|
||||
} else {
|
||||
min_ = std::numeric_limits<VType>::max();
|
||||
max_ = std::numeric_limits<VType>::min();
|
||||
}
|
||||
}
|
||||
|
||||
Self &operator=(const Self &stat) {
|
||||
this->Stat1<VType, NumType>::operator=(stat);
|
||||
max_ = stat.max_;
|
||||
min_ = stat.min_;
|
||||
return (*this);
|
||||
}
|
||||
// Merge statistics from two sample sets.
|
||||
Self &operator+=(const Self &stat) {
|
||||
this->Stat1<VType, NumType>::operator+=(stat);
|
||||
if (stat.max_ > max_) max_ = stat.max_;
|
||||
if (stat.min_ < min_) min_ = stat.min_;
|
||||
return (*this);
|
||||
}
|
||||
// Multiply the weight of the set of samples by a factor k
|
||||
Self &operator*=(const VType &stat) {
|
||||
this->Stat1<VType, NumType>::operator*=(stat);
|
||||
return (*this);
|
||||
}
|
||||
// Merge statistics from two sample sets.
|
||||
Self operator+(const Self &stat) const { return Self(*this) += stat; }
|
||||
// Multiply the weight of the set of samples by a factor k
|
||||
Self operator*(const VType &k) const { return Self(*this) *= k; }
|
||||
|
||||
// Return the maximal value in this sample set
|
||||
VType Max() const { return max_; }
|
||||
// Return the minimal value in this sample set
|
||||
VType Min() const { return min_; }
|
||||
|
||||
private:
|
||||
// The - operation makes no sense with Min/Max
|
||||
// unless we keep the full list of values (but we don't)
|
||||
// make it private, and let it undefined so nobody can call it
|
||||
Self &operator-=(const Self &stat); // senseless. let it undefined.
|
||||
|
||||
// The operation opposite to -
|
||||
Self operator-(const Self &stat) const; // senseless. let it undefined.
|
||||
|
||||
// Let i be the index of the samples provided (using +=)
|
||||
// and weight[i],value[i] be the data of sample #i
|
||||
// then the variables have the following meaning:
|
||||
VType max_; // max of value[i]
|
||||
VType min_; // min of value[i]
|
||||
};
|
||||
|
||||
// Useful printing function
|
||||
template <typename VType, typename NumType>
|
||||
std::ostream &operator<<(std::ostream &out,
|
||||
const Stat1MinMax<VType, NumType> &s) {
|
||||
out << "{ avg = " << s.Mean() << " std = " << s.StdDev()
|
||||
<< " nsamples = " << s.NumSamples() << " min = " << s.Min()
|
||||
<< " max = " << s.Max() << "}";
|
||||
return out;
|
||||
}
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_STAT_H_
|
||||
@@ -1,168 +0,0 @@
|
||||
#include "string_util.h"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "arraysize.h"
|
||||
|
||||
namespace benchmark {
|
||||
namespace {
|
||||
|
||||
// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta.
|
||||
const char kBigSIUnits[] = "kMGTPEZY";
|
||||
// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi.
|
||||
const char kBigIECUnits[] = "KMGTPEZY";
|
||||
// milli, micro, nano, pico, femto, atto, zepto, yocto.
|
||||
const char kSmallSIUnits[] = "munpfazy";
|
||||
|
||||
// We require that all three arrays have the same size.
|
||||
static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits),
|
||||
"SI and IEC unit arrays must be the same size");
|
||||
static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits),
|
||||
"Small SI and Big SI unit arrays must be the same size");
|
||||
|
||||
static const int64_t kUnitsSize = arraysize(kBigSIUnits);
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
void ToExponentAndMantissa(double val, double thresh, int precision,
|
||||
double one_k, std::string* mantissa,
|
||||
int64_t* exponent) {
|
||||
std::stringstream mantissa_stream;
|
||||
|
||||
if (val < 0) {
|
||||
mantissa_stream << "-";
|
||||
val = -val;
|
||||
}
|
||||
|
||||
// Adjust threshold so that it never excludes things which can't be rendered
|
||||
// in 'precision' digits.
|
||||
const double adjusted_threshold =
|
||||
std::max(thresh, 1.0 / std::pow(10.0, precision));
|
||||
const double big_threshold = adjusted_threshold * one_k;
|
||||
const double small_threshold = adjusted_threshold;
|
||||
|
||||
if (val > big_threshold) {
|
||||
// Positive powers
|
||||
double scaled = val;
|
||||
for (size_t i = 0; i < arraysize(kBigSIUnits); ++i) {
|
||||
scaled /= one_k;
|
||||
if (scaled <= big_threshold) {
|
||||
mantissa_stream << scaled;
|
||||
*exponent = i + 1;
|
||||
*mantissa = mantissa_stream.str();
|
||||
return;
|
||||
}
|
||||
}
|
||||
mantissa_stream << val;
|
||||
*exponent = 0;
|
||||
} else if (val < small_threshold) {
|
||||
// Negative powers
|
||||
double scaled = val;
|
||||
for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) {
|
||||
scaled *= one_k;
|
||||
if (scaled >= small_threshold) {
|
||||
mantissa_stream << scaled;
|
||||
*exponent = -static_cast<int64_t>(i + 1);
|
||||
*mantissa = mantissa_stream.str();
|
||||
return;
|
||||
}
|
||||
}
|
||||
mantissa_stream << val;
|
||||
*exponent = 0;
|
||||
} else {
|
||||
mantissa_stream << val;
|
||||
*exponent = 0;
|
||||
}
|
||||
*mantissa = mantissa_stream.str();
|
||||
}
|
||||
|
||||
std::string ExponentToPrefix(int64_t exponent, bool iec) {
|
||||
if (exponent == 0) return "";
|
||||
|
||||
const int64_t index = (exponent > 0 ? exponent - 1 : -exponent - 1);
|
||||
if (index >= kUnitsSize) return "";
|
||||
|
||||
const char* array =
|
||||
(exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits);
|
||||
if (iec)
|
||||
return array[index] + std::string("i");
|
||||
else
|
||||
return std::string(1, array[index]);
|
||||
}
|
||||
|
||||
std::string ToBinaryStringFullySpecified(double value, double threshold,
|
||||
int precision) {
|
||||
std::string mantissa;
|
||||
int64_t exponent;
|
||||
ToExponentAndMantissa(value, threshold, precision, 1024.0, &mantissa,
|
||||
&exponent);
|
||||
return mantissa + ExponentToPrefix(exponent, false);
|
||||
}
|
||||
|
||||
void AppendHumanReadable(int n, std::string* str) {
|
||||
std::stringstream ss;
|
||||
// Round down to the nearest SI prefix.
|
||||
ss << ToBinaryStringFullySpecified(n, 1.0, 0);
|
||||
*str += ss.str();
|
||||
}
|
||||
|
||||
std::string HumanReadableNumber(double n) {
|
||||
// 1.1 means that figures up to 1.1k should be shown with the next unit down;
|
||||
// this softens edge effects.
|
||||
// 1 means that we should show one decimal place of precision.
|
||||
return ToBinaryStringFullySpecified(n, 1.1, 1);
|
||||
}
|
||||
|
||||
std::string StringPrintFImp(const char* msg, va_list args) {
|
||||
// we might need a second shot at this, so pre-emptivly make a copy
|
||||
va_list args_cp;
|
||||
va_copy(args_cp, args);
|
||||
|
||||
// TODO(ericwf): use std::array for first attempt to avoid one memory
|
||||
// allocation guess what the size might be
|
||||
std::array<char, 256> local_buff;
|
||||
std::size_t size = local_buff.size();
|
||||
// 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
|
||||
// in the android-ndk
|
||||
auto ret = vsnprintf(local_buff.data(), size, msg, args_cp);
|
||||
|
||||
va_end(args_cp);
|
||||
|
||||
// handle empty expansion
|
||||
if (ret == 0) return std::string{};
|
||||
if (static_cast<std::size_t>(ret) < size)
|
||||
return std::string(local_buff.data());
|
||||
|
||||
// we did not provide a long enough buffer on our first attempt.
|
||||
// add 1 to size to account for null-byte in size cast to prevent overflow
|
||||
size = static_cast<std::size_t>(ret) + 1;
|
||||
auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
|
||||
// 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
|
||||
// in the android-ndk
|
||||
ret = vsnprintf(buff_ptr.get(), size, msg, args);
|
||||
return std::string(buff_ptr.get());
|
||||
}
|
||||
|
||||
std::string StringPrintF(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
std::string tmp = StringPrintFImp(format, args);
|
||||
va_end(args);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void ReplaceAll(std::string* str, const std::string& from,
|
||||
const std::string& to) {
|
||||
std::size_t start = 0;
|
||||
while ((start = str->find(from, start)) != std::string::npos) {
|
||||
str->replace(start, from.length(), to);
|
||||
start += to.length();
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef BENCHMARK_STRING_UTIL_H_
|
||||
#define BENCHMARK_STRING_UTIL_H_
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "internal_macros.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
void AppendHumanReadable(int n, std::string* str);
|
||||
|
||||
std::string HumanReadableNumber(double n);
|
||||
|
||||
std::string StringPrintF(const char* format, ...);
|
||||
|
||||
inline std::ostream& StringCatImp(std::ostream& out) BENCHMARK_NOEXCEPT {
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class First, class... Rest>
|
||||
inline std::ostream& StringCatImp(std::ostream& out, First&& f,
|
||||
Rest&&... rest) {
|
||||
out << std::forward<First>(f);
|
||||
return StringCatImp(out, std::forward<Rest>(rest)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
inline std::string StrCat(Args&&... args) {
|
||||
std::ostringstream ss;
|
||||
StringCatImp(ss, std::forward<Args>(args)...);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void ReplaceAll(std::string* str, const std::string& from,
|
||||
const std::string& to);
|
||||
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_STRING_UTIL_H_
|
||||
@@ -1,348 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "sysinfo.h"
|
||||
#include "internal_macros.h"
|
||||
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
#include <Shlwapi.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
|
||||
#include <unistd.h>
|
||||
#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
|
||||
#include "arraysize.h"
|
||||
#include "check.h"
|
||||
#include "cycleclock.h"
|
||||
#include "internal_macros.h"
|
||||
#include "log.h"
|
||||
#include "sleep.h"
|
||||
#include "string_util.h"
|
||||
|
||||
namespace benchmark {
|
||||
namespace {
|
||||
std::once_flag cpuinfo_init;
|
||||
double cpuinfo_cycles_per_second = 1.0;
|
||||
int cpuinfo_num_cpus = 1; // Conservative guess
|
||||
|
||||
#if !defined BENCHMARK_OS_MACOSX
|
||||
const int64_t estimate_time_ms = 1000;
|
||||
|
||||
// Helper function estimates cycles/sec by observing cycles elapsed during
|
||||
// sleep(). Using small sleep time decreases accuracy significantly.
|
||||
int64_t EstimateCyclesPerSecond() {
|
||||
const int64_t start_ticks = cycleclock::Now();
|
||||
SleepForMilliseconds(estimate_time_ms);
|
||||
return cycleclock::Now() - start_ticks;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
|
||||
// Helper function for reading an int from a file. Returns true if successful
|
||||
// and the memory location pointed to by value is set to the value read.
|
||||
bool ReadIntFromFile(const char* file, long* value) {
|
||||
bool ret = false;
|
||||
int fd = open(file, O_RDONLY);
|
||||
if (fd != -1) {
|
||||
char line[1024];
|
||||
char* err;
|
||||
memset(line, '\0', sizeof(line));
|
||||
CHECK(read(fd, line, sizeof(line) - 1));
|
||||
const long temp_value = strtol(line, &err, 10);
|
||||
if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
|
||||
*value = temp_value;
|
||||
ret = true;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
|
||||
static std::string convertToLowerCase(std::string s) {
|
||||
for (auto& ch : s)
|
||||
ch = std::tolower(ch);
|
||||
return s;
|
||||
}
|
||||
static bool startsWithKey(std::string Value, std::string Key,
|
||||
bool IgnoreCase = true) {
|
||||
if (IgnoreCase) {
|
||||
Key = convertToLowerCase(std::move(Key));
|
||||
Value = convertToLowerCase(std::move(Value));
|
||||
}
|
||||
return Value.compare(0, Key.size(), Key) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitializeSystemInfo() {
|
||||
#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
|
||||
char line[1024];
|
||||
char* err;
|
||||
long freq;
|
||||
|
||||
bool saw_mhz = false;
|
||||
|
||||
// If the kernel is exporting the tsc frequency use that. There are issues
|
||||
// where cpuinfo_max_freq cannot be relied on because the BIOS may be
|
||||
// exporintg an invalid p-state (on x86) or p-states may be used to put the
|
||||
// processor in a new mode (turbo mode). Essentially, those frequencies
|
||||
// cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
|
||||
// well.
|
||||
if (!saw_mhz &&
|
||||
ReadIntFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
|
||||
// The value is in kHz (as the file name suggests). For example, on a
|
||||
// 2GHz warpstation, the file contains the value "2000000".
|
||||
cpuinfo_cycles_per_second = freq * 1000.0;
|
||||
saw_mhz = true;
|
||||
}
|
||||
|
||||
// If CPU scaling is in effect, we want to use the *maximum* frequency,
|
||||
// not whatever CPU speed some random processor happens to be using now.
|
||||
if (!saw_mhz &&
|
||||
ReadIntFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
|
||||
&freq)) {
|
||||
// The value is in kHz. For example, on a 2GHz warpstation, the file
|
||||
// contains the value "2000000".
|
||||
cpuinfo_cycles_per_second = freq * 1000.0;
|
||||
saw_mhz = true;
|
||||
}
|
||||
|
||||
// Read /proc/cpuinfo for other values, and if there is no cpuinfo_max_freq.
|
||||
const char* pname = "/proc/cpuinfo";
|
||||
int fd = open(pname, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror(pname);
|
||||
if (!saw_mhz) {
|
||||
cpuinfo_cycles_per_second =
|
||||
static_cast<double>(EstimateCyclesPerSecond());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
double bogo_clock = 1.0;
|
||||
bool saw_bogo = false;
|
||||
long max_cpu_id = 0;
|
||||
int num_cpus = 0;
|
||||
line[0] = line[1] = '\0';
|
||||
size_t chars_read = 0;
|
||||
do { // we'll exit when the last read didn't read anything
|
||||
// Move the next line to the beginning of the buffer
|
||||
const size_t oldlinelen = strlen(line);
|
||||
if (sizeof(line) == oldlinelen + 1) // oldlinelen took up entire line
|
||||
line[0] = '\0';
|
||||
else // still other lines left to save
|
||||
memmove(line, line + oldlinelen + 1, sizeof(line) - (oldlinelen + 1));
|
||||
// Terminate the new line, reading more if we can't find the newline
|
||||
char* newline = strchr(line, '\n');
|
||||
if (newline == nullptr) {
|
||||
const size_t linelen = strlen(line);
|
||||
const size_t bytes_to_read = sizeof(line) - 1 - linelen;
|
||||
CHECK(bytes_to_read > 0); // because the memmove recovered >=1 bytes
|
||||
chars_read = read(fd, line + linelen, bytes_to_read);
|
||||
line[linelen + chars_read] = '\0';
|
||||
newline = strchr(line, '\n');
|
||||
}
|
||||
if (newline != nullptr) *newline = '\0';
|
||||
|
||||
// When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
|
||||
// accept postive values. Some environments (virtual machines) report zero,
|
||||
// which would cause infinite looping in WallTime_Init.
|
||||
if (!saw_mhz && startsWithKey(line, "cpu MHz")) {
|
||||
const char* freqstr = strchr(line, ':');
|
||||
if (freqstr) {
|
||||
cpuinfo_cycles_per_second = strtod(freqstr + 1, &err) * 1000000.0;
|
||||
if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0)
|
||||
saw_mhz = true;
|
||||
}
|
||||
} else if (startsWithKey(line, "bogomips")) {
|
||||
const char* freqstr = strchr(line, ':');
|
||||
if (freqstr) {
|
||||
bogo_clock = strtod(freqstr + 1, &err) * 1000000.0;
|
||||
if (freqstr[1] != '\0' && *err == '\0' && bogo_clock > 0)
|
||||
saw_bogo = true;
|
||||
}
|
||||
} else if (startsWithKey(line, "processor", /*IgnoreCase*/false)) {
|
||||
// The above comparison is case-sensitive because ARM kernels often
|
||||
// include a "Processor" line that tells you about the CPU, distinct
|
||||
// from the usual "processor" lines that give you CPU ids. No current
|
||||
// Linux architecture is using "Processor" for CPU ids.
|
||||
num_cpus++; // count up every time we see an "processor :" entry
|
||||
const char* id_str = strchr(line, ':');
|
||||
if (id_str) {
|
||||
const long cpu_id = strtol(id_str + 1, &err, 10);
|
||||
if (id_str[1] != '\0' && *err == '\0' && max_cpu_id < cpu_id)
|
||||
max_cpu_id = cpu_id;
|
||||
}
|
||||
}
|
||||
} while (chars_read > 0);
|
||||
close(fd);
|
||||
|
||||
if (!saw_mhz) {
|
||||
if (saw_bogo) {
|
||||
// If we didn't find anything better, we'll use bogomips, but
|
||||
// we're not happy about it.
|
||||
cpuinfo_cycles_per_second = bogo_clock;
|
||||
} else {
|
||||
// If we don't even have bogomips, we'll use the slow estimation.
|
||||
cpuinfo_cycles_per_second =
|
||||
static_cast<double>(EstimateCyclesPerSecond());
|
||||
}
|
||||
}
|
||||
if (num_cpus == 0) {
|
||||
fprintf(stderr, "Failed to read num. CPUs correctly from /proc/cpuinfo\n");
|
||||
} else {
|
||||
if ((max_cpu_id + 1) != num_cpus) {
|
||||
fprintf(stderr,
|
||||
"CPU ID assignments in /proc/cpuinfo seem messed up."
|
||||
" This is usually caused by a bad BIOS.\n");
|
||||
}
|
||||
cpuinfo_num_cpus = num_cpus;
|
||||
}
|
||||
|
||||
#elif defined BENCHMARK_OS_FREEBSD
|
||||
// For this sysctl to work, the machine must be configured without
|
||||
// SMP, APIC, or APM support. hz should be 64-bit in freebsd 7.0
|
||||
// and later. Before that, it's a 32-bit quantity (and gives the
|
||||
// wrong answer on machines faster than 2^32 Hz). See
|
||||
// http://lists.freebsd.org/pipermail/freebsd-i386/2004-November/001846.html
|
||||
// But also compare FreeBSD 7.0:
|
||||
// http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG70#L223
|
||||
// 231 error = sysctl_handle_quad(oidp, &freq, 0, req);
|
||||
// To FreeBSD 6.3 (it's the same in 6-STABLE):
|
||||
// http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG6#L131
|
||||
// 139 error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
|
||||
#if __FreeBSD__ >= 7
|
||||
uint64_t hz = 0;
|
||||
#else
|
||||
unsigned int hz = 0;
|
||||
#endif
|
||||
size_t sz = sizeof(hz);
|
||||
const char* sysctl_path = "machdep.tsc_freq";
|
||||
if (sysctlbyname(sysctl_path, &hz, &sz, nullptr, 0) != 0) {
|
||||
fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
|
||||
sysctl_path, strerror(errno));
|
||||
cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
|
||||
} else {
|
||||
cpuinfo_cycles_per_second = hz;
|
||||
}
|
||||
// TODO: also figure out cpuinfo_num_cpus
|
||||
|
||||
#elif defined BENCHMARK_OS_WINDOWS
|
||||
// In NT, read MHz from the registry. If we fail to do so or we're in win9x
|
||||
// then make a crude estimate.
|
||||
DWORD data, data_size = sizeof(data);
|
||||
if (IsWindowsXPOrGreater() &&
|
||||
SUCCEEDED(
|
||||
SHGetValueA(HKEY_LOCAL_MACHINE,
|
||||
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
|
||||
"~MHz", nullptr, &data, &data_size)))
|
||||
cpuinfo_cycles_per_second =
|
||||
static_cast<double>((int64_t)data * (int64_t)(1000 * 1000)); // was mhz
|
||||
else
|
||||
cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
|
||||
|
||||
SYSTEM_INFO sysinfo;
|
||||
// Use memset as opposed to = {} to avoid GCC missing initializer false
|
||||
// positives.
|
||||
std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
|
||||
GetSystemInfo(&sysinfo);
|
||||
cpuinfo_num_cpus = sysinfo.dwNumberOfProcessors; // number of logical
|
||||
// processors in the current
|
||||
// group
|
||||
|
||||
#elif defined BENCHMARK_OS_MACOSX
|
||||
int32_t num_cpus = 0;
|
||||
size_t size = sizeof(num_cpus);
|
||||
if (::sysctlbyname("hw.ncpu", &num_cpus, &size, nullptr, 0) == 0 &&
|
||||
(size == sizeof(num_cpus))) {
|
||||
cpuinfo_num_cpus = num_cpus;
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
int64_t cpu_freq = 0;
|
||||
size = sizeof(cpu_freq);
|
||||
if (::sysctlbyname("hw.cpufrequency", &cpu_freq, &size, nullptr, 0) == 0 &&
|
||||
(size == sizeof(cpu_freq))) {
|
||||
cpuinfo_cycles_per_second = cpu_freq;
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
#else
|
||||
// Generic cycles per second counter
|
||||
cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
double CyclesPerSecond(void) {
|
||||
std::call_once(cpuinfo_init, InitializeSystemInfo);
|
||||
return cpuinfo_cycles_per_second;
|
||||
}
|
||||
|
||||
int NumCPUs(void) {
|
||||
std::call_once(cpuinfo_init, InitializeSystemInfo);
|
||||
return cpuinfo_num_cpus;
|
||||
}
|
||||
|
||||
// The ""'s catch people who don't pass in a literal for "str"
|
||||
#define strliterallen(str) (sizeof("" str "") - 1)
|
||||
|
||||
// Must use a string literal for prefix.
|
||||
#define memprefix(str, len, prefix) \
|
||||
((((len) >= strliterallen(prefix)) && \
|
||||
std::memcmp(str, prefix, strliterallen(prefix)) == 0) \
|
||||
? str + strliterallen(prefix) \
|
||||
: nullptr)
|
||||
|
||||
bool CpuScalingEnabled() {
|
||||
#ifndef BENCHMARK_OS_WINDOWS
|
||||
// On Linux, the CPUfreq subsystem exposes CPU information as files on the
|
||||
// local file system. If reading the exported files fails, then we may not be
|
||||
// running on Linux, so we silently ignore all the read errors.
|
||||
for (int cpu = 0, num_cpus = NumCPUs(); cpu < num_cpus; ++cpu) {
|
||||
std::string governor_file =
|
||||
StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor");
|
||||
FILE* file = fopen(governor_file.c_str(), "r");
|
||||
if (!file) break;
|
||||
char buff[16];
|
||||
size_t bytes_read = fread(buff, 1, sizeof(buff), file);
|
||||
fclose(file);
|
||||
if (memprefix(buff, bytes_read, "performance") == nullptr) return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef BENCHMARK_SYSINFO_H_
|
||||
#define BENCHMARK_SYSINFO_H_
|
||||
|
||||
namespace benchmark {
|
||||
int NumCPUs();
|
||||
double CyclesPerSecond();
|
||||
bool CpuScalingEnabled();
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_SYSINFO_H_
|
||||
@@ -1,195 +0,0 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "timers.h"
|
||||
#include "internal_macros.h"
|
||||
|
||||
#ifdef BENCHMARK_OS_WINDOWS
|
||||
#include <Shlwapi.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD
|
||||
#include <unistd.h>
|
||||
#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#if defined(BENCHMARK_OS_MACOSX)
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/thread_act.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
|
||||
#include "check.h"
|
||||
#include "log.h"
|
||||
#include "sleep.h"
|
||||
#include "string_util.h"
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
// Suppress unused warnings on helper functions.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#if defined(BENCHMARK_OS_WINDOWS)
|
||||
double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) {
|
||||
ULARGE_INTEGER kernel;
|
||||
ULARGE_INTEGER user;
|
||||
kernel.HighPart = kernel_time.dwHighDateTime;
|
||||
kernel.LowPart = kernel_time.dwLowDateTime;
|
||||
user.HighPart = user_time.dwHighDateTime;
|
||||
user.LowPart = user_time.dwLowDateTime;
|
||||
return (static_cast<double>(kernel.QuadPart) +
|
||||
static_cast<double>(user.QuadPart)) *
|
||||
1e-7;
|
||||
}
|
||||
#else
|
||||
double MakeTime(struct rusage const& ru) {
|
||||
return (static_cast<double>(ru.ru_utime.tv_sec) +
|
||||
static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 +
|
||||
static_cast<double>(ru.ru_stime.tv_sec) +
|
||||
static_cast<double>(ru.ru_stime.tv_usec) * 1e-6);
|
||||
}
|
||||
#endif
|
||||
#if defined(BENCHMARK_OS_MACOSX)
|
||||
double MakeTime(thread_basic_info_data_t const& info) {
|
||||
return (static_cast<double>(info.user_time.seconds) +
|
||||
static_cast<double>(info.user_time.microseconds) * 1e-6 +
|
||||
static_cast<double>(info.system_time.seconds) +
|
||||
static_cast<double>(info.system_time.microseconds) * 1e-6);
|
||||
}
|
||||
#endif
|
||||
#if defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_THREAD_CPUTIME_ID)
|
||||
double MakeTime(struct timespec const& ts) {
|
||||
return ts.tv_sec + (static_cast<double>(ts.tv_nsec) * 1e-9);
|
||||
}
|
||||
#endif
|
||||
|
||||
BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) {
|
||||
std::cerr << "ERROR: " << msg << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
double ProcessCPUUsage() {
|
||||
// FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See
|
||||
// https://github.com/google/benchmark/pull/292
|
||||
#if defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)
|
||||
struct timespec spec;
|
||||
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0)
|
||||
return MakeTime(spec);
|
||||
DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");
|
||||
#elif defined(BENCHMARK_OS_WINDOWS)
|
||||
HANDLE proc = GetCurrentProcess();
|
||||
FILETIME creation_time;
|
||||
FILETIME exit_time;
|
||||
FILETIME kernel_time;
|
||||
FILETIME user_time;
|
||||
if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time,
|
||||
&user_time))
|
||||
return MakeTime(kernel_time, user_time);
|
||||
DiagnoseAndExit("GetProccessTimes() failed");
|
||||
#else
|
||||
struct rusage ru;
|
||||
if (getrusage(RUSAGE_SELF, &ru) == 0) return MakeTime(ru);
|
||||
DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
double ThreadCPUUsage() {
|
||||
// FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See
|
||||
// https://github.com/google/benchmark/pull/292
|
||||
#if defined(CLOCK_THREAD_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts);
|
||||
DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed");
|
||||
#elif defined(BENCHMARK_OS_WINDOWS)
|
||||
HANDLE this_thread = GetCurrentThread();
|
||||
FILETIME creation_time;
|
||||
FILETIME exit_time;
|
||||
FILETIME kernel_time;
|
||||
FILETIME user_time;
|
||||
GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time,
|
||||
&user_time);
|
||||
return MakeTime(kernel_time, user_time);
|
||||
#elif defined(BENCHMARK_OS_MACOSX)
|
||||
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
|
||||
thread_basic_info_data_t info;
|
||||
mach_port_t thread = pthread_mach_thread_np(pthread_self());
|
||||
if (thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&info, &count) ==
|
||||
KERN_SUCCESS) {
|
||||
return MakeTime(info);
|
||||
}
|
||||
DiagnoseAndExit("ThreadCPUUsage() failed when evaluating thread_info");
|
||||
#else
|
||||
#error Per-thread timing is not available on your system.
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string DateTimeString(bool local) {
|
||||
typedef std::chrono::system_clock Clock;
|
||||
std::time_t now = Clock::to_time_t(Clock::now());
|
||||
const std::size_t kStorageSize = 128;
|
||||
char storage[kStorageSize];
|
||||
std::size_t written;
|
||||
|
||||
if (local) {
|
||||
#if defined(BENCHMARK_OS_WINDOWS)
|
||||
written =
|
||||
std::strftime(storage, sizeof(storage), "%x %X", ::localtime(&now));
|
||||
#else
|
||||
std::tm timeinfo;
|
||||
std::memset(&timeinfo, 0, sizeof(std::tm));
|
||||
::localtime_r(&now, &timeinfo);
|
||||
written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
|
||||
#endif
|
||||
} else {
|
||||
#if defined(BENCHMARK_OS_WINDOWS)
|
||||
written = std::strftime(storage, sizeof(storage), "%x %X", ::gmtime(&now));
|
||||
#else
|
||||
std::tm timeinfo;
|
||||
std::memset(&timeinfo, 0, sizeof(std::tm));
|
||||
::gmtime_r(&now, &timeinfo);
|
||||
written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
|
||||
#endif
|
||||
}
|
||||
CHECK(written < kStorageSize);
|
||||
((void)written); // prevent unused variable in optimized mode.
|
||||
return std::string(storage);
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
std::string LocalDateTimeString() { return DateTimeString(true); }
|
||||
|
||||
} // end namespace benchmark
|
||||
@@ -1,48 +0,0 @@
|
||||
#ifndef BENCHMARK_TIMERS_H
|
||||
#define BENCHMARK_TIMERS_H
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace benchmark {
|
||||
|
||||
// Return the CPU usage of the current process
|
||||
double ProcessCPUUsage();
|
||||
|
||||
// Return the CPU usage of the children of the current process
|
||||
double ChildrenCPUUsage();
|
||||
|
||||
// Return the CPU usage of the current thread
|
||||
double ThreadCPUUsage();
|
||||
|
||||
#if defined(HAVE_STEADY_CLOCK)
|
||||
template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
|
||||
struct ChooseSteadyClock {
|
||||
typedef std::chrono::high_resolution_clock type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ChooseSteadyClock<false> {
|
||||
typedef std::chrono::steady_clock type;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ChooseClockType {
|
||||
#if defined(HAVE_STEADY_CLOCK)
|
||||
typedef ChooseSteadyClock<>::type type;
|
||||
#else
|
||||
typedef std::chrono::high_resolution_clock type;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline double ChronoClockNow() {
|
||||
typedef ChooseClockType::type ClockType;
|
||||
using FpSeconds = std::chrono::duration<double, std::chrono::seconds::period>;
|
||||
return FpSeconds(ClockType::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
std::string LocalDateTimeString();
|
||||
|
||||
} // end namespace benchmark
|
||||
|
||||
#endif // BENCHMARK_TIMERS_H
|
||||
@@ -1,139 +0,0 @@
|
||||
# Enable the tests
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# NOTE: These flags must be added after find_package(Threads REQUIRED) otherwise
|
||||
# they will break the configuration check.
|
||||
if (DEFINED BENCHMARK_CXX_LINKER_FLAGS)
|
||||
list(APPEND CMAKE_EXE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
|
||||
endif()
|
||||
|
||||
add_library(output_test_helper STATIC output_test_helper.cc)
|
||||
|
||||
macro(compile_benchmark_test name)
|
||||
add_executable(${name} "${name}.cc")
|
||||
target_link_libraries(${name} benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||
endmacro(compile_benchmark_test)
|
||||
|
||||
|
||||
macro(compile_output_test name)
|
||||
add_executable(${name} "${name}.cc" output_test.h)
|
||||
target_link_libraries(${name} output_test_helper benchmark
|
||||
${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
endmacro(compile_output_test)
|
||||
|
||||
|
||||
# Demonstration executable
|
||||
compile_benchmark_test(benchmark_test)
|
||||
add_test(benchmark benchmark_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(filter_test)
|
||||
macro(add_filter_test name filter expect)
|
||||
add_test(${name} filter_test --benchmark_min_time=0.01 --benchmark_filter=${filter} ${expect})
|
||||
add_test(${name}_list_only filter_test --benchmark_list_tests --benchmark_filter=${filter} ${expect})
|
||||
endmacro(add_filter_test)
|
||||
|
||||
add_filter_test(filter_simple "Foo" 3)
|
||||
add_filter_test(filter_suffix "BM_.*" 4)
|
||||
add_filter_test(filter_regex_all ".*" 5)
|
||||
add_filter_test(filter_regex_blank "" 5)
|
||||
add_filter_test(filter_regex_none "monkey" 0)
|
||||
add_filter_test(filter_regex_wildcard ".*Foo.*" 3)
|
||||
add_filter_test(filter_regex_begin "^BM_.*" 4)
|
||||
add_filter_test(filter_regex_begin2 "^N" 1)
|
||||
add_filter_test(filter_regex_end ".*Ba$" 1)
|
||||
|
||||
compile_benchmark_test(options_test)
|
||||
add_test(options_benchmarks options_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(basic_test)
|
||||
add_test(basic_benchmark basic_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(diagnostics_test)
|
||||
add_test(diagnostics_test diagnostics_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(skip_with_error_test)
|
||||
add_test(skip_with_error_test skip_with_error_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(donotoptimize_test)
|
||||
add_test(donotoptimize_test donotoptimize_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(fixture_test)
|
||||
add_test(fixture_test fixture_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(register_benchmark_test)
|
||||
add_test(register_benchmark_test register_benchmark_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(map_test)
|
||||
add_test(map_test map_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_benchmark_test(multiple_ranges_test)
|
||||
add_test(multiple_ranges_test multiple_ranges_test --benchmark_min_time=0.01)
|
||||
|
||||
compile_output_test(reporter_output_test)
|
||||
add_test(reporter_output_test reporter_output_test --benchmark_min_time=0.01)
|
||||
|
||||
check_cxx_compiler_flag(-std=c++03 BENCHMARK_HAS_CXX03_FLAG)
|
||||
if (BENCHMARK_HAS_CXX03_FLAG)
|
||||
set(CXX03_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
string(REPLACE "-std=c++11" "-std=c++03" CXX03_FLAGS "${CXX03_FLAGS}")
|
||||
string(REPLACE "-std=c++0x" "-std=c++03" CXX03_FLAGS "${CXX03_FLAGS}")
|
||||
|
||||
compile_benchmark_test(cxx03_test)
|
||||
set_target_properties(cxx03_test
|
||||
PROPERTIES COMPILE_FLAGS "${CXX03_FLAGS}")
|
||||
add_test(cxx03 cxx03_test --benchmark_min_time=0.01)
|
||||
endif()
|
||||
|
||||
# Attempt to work around flaky test failures when running on Appveyor servers.
|
||||
if (DEFINED ENV{APPVEYOR})
|
||||
set(COMPLEXITY_MIN_TIME "0.5")
|
||||
else()
|
||||
set(COMPLEXITY_MIN_TIME "0.01")
|
||||
endif()
|
||||
compile_output_test(complexity_test)
|
||||
add_test(complexity_benchmark complexity_test --benchmark_min_time=${COMPLEXITY_MIN_TIME})
|
||||
|
||||
# Add the coverage command(s)
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
|
||||
endif()
|
||||
if (${CMAKE_BUILD_TYPE_LOWER} MATCHES "coverage")
|
||||
find_program(GCOV gcov)
|
||||
find_program(LCOV lcov)
|
||||
find_program(GENHTML genhtml)
|
||||
find_program(CTEST ctest)
|
||||
if (GCOV AND LCOV AND GENHTML AND CTEST AND HAVE_CXX_FLAG_COVERAGE)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/lcov/index.html
|
||||
COMMAND ${LCOV} -q -z -d .
|
||||
COMMAND ${LCOV} -q --no-external -c -b "${CMAKE_SOURCE_DIR}" -d . -o before.lcov -i
|
||||
COMMAND ${CTEST} --force-new-ctest-process
|
||||
COMMAND ${LCOV} -q --no-external -c -b "${CMAKE_SOURCE_DIR}" -d . -o after.lcov
|
||||
COMMAND ${LCOV} -q -a before.lcov -a after.lcov --output-file final.lcov
|
||||
COMMAND ${LCOV} -q -r final.lcov "'${CMAKE_SOURCE_DIR}/test/*'" -o final.lcov
|
||||
COMMAND ${GENHTML} final.lcov -o lcov --demangle-cpp --sort -p "${CMAKE_BINARY_DIR}" -t benchmark
|
||||
DEPENDS filter_test benchmark_test options_test basic_test fixture_test cxx03_test complexity_test
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Running LCOV"
|
||||
)
|
||||
add_custom_target(coverage
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/lcov/index.html
|
||||
COMMENT "LCOV report at lcov/index.html"
|
||||
)
|
||||
message(STATUS "Coverage command added")
|
||||
else()
|
||||
if (HAVE_CXX_FLAG_COVERAGE)
|
||||
set(CXX_FLAG_COVERAGE_MESSAGE supported)
|
||||
else()
|
||||
set(CXX_FLAG_COVERAGE_MESSAGE unavailable)
|
||||
endif()
|
||||
message(WARNING
|
||||
"Coverage not available:\n"
|
||||
" gcov: ${GCOV}\n"
|
||||
" lcov: ${LCOV}\n"
|
||||
" genhtml: ${GENHTML}\n"
|
||||
" ctest: ${CTEST}\n"
|
||||
" --coverage flag: ${CXX_FLAG_COVERAGE_MESSAGE}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,99 +0,0 @@
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
#define BASIC_BENCHMARK_TEST(x) BENCHMARK(x)->Arg(8)->Arg(512)->Arg(8192)
|
||||
|
||||
void BM_empty(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(state.iterations());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_empty);
|
||||
BENCHMARK(BM_empty)->ThreadPerCpu();
|
||||
|
||||
void BM_spin_empty(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
for (int x = 0; x < state.range(0); ++x) {
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
BASIC_BENCHMARK_TEST(BM_spin_empty);
|
||||
BASIC_BENCHMARK_TEST(BM_spin_empty)->ThreadPerCpu();
|
||||
|
||||
void BM_spin_pause_before(benchmark::State& state) {
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_before);
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_before)->ThreadPerCpu();
|
||||
|
||||
void BM_spin_pause_during(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
state.ResumeTiming();
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_during);
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_during)->ThreadPerCpu();
|
||||
|
||||
void BM_pause_during(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_pause_during);
|
||||
BENCHMARK(BM_pause_during)->ThreadPerCpu();
|
||||
BENCHMARK(BM_pause_during)->UseRealTime();
|
||||
BENCHMARK(BM_pause_during)->UseRealTime()->ThreadPerCpu();
|
||||
|
||||
void BM_spin_pause_after(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
}
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_after);
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_after)->ThreadPerCpu();
|
||||
|
||||
void BM_spin_pause_before_and_after(benchmark::State& state) {
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
benchmark::DoNotOptimize(i);
|
||||
}
|
||||
}
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_before_and_after);
|
||||
BASIC_BENCHMARK_TEST(BM_spin_pause_before_and_after)->ThreadPerCpu();
|
||||
|
||||
void BM_empty_stop_start(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_empty_stop_start);
|
||||
BENCHMARK(BM_empty_stop_start)->ThreadPerCpu();
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,241 +0,0 @@
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define BENCHMARK_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#define BENCHMARK_NOINLINE
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
int BENCHMARK_NOINLINE Factorial(uint32_t n) {
|
||||
return (n == 1) ? 1 : n * Factorial(n - 1);
|
||||
}
|
||||
|
||||
double CalculatePi(int depth) {
|
||||
double pi = 0.0;
|
||||
for (int i = 0; i < depth; ++i) {
|
||||
double numerator = static_cast<double>(((i % 2) * 2) - 1);
|
||||
double denominator = static_cast<double>((2 * i) - 1);
|
||||
pi += numerator / denominator;
|
||||
}
|
||||
return (pi - 1.0) * 4;
|
||||
}
|
||||
|
||||
std::set<int> ConstructRandomSet(int size) {
|
||||
std::set<int> s;
|
||||
for (int i = 0; i < size; ++i) s.insert(i);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::mutex test_vector_mu;
|
||||
std::vector<int>* test_vector = nullptr;
|
||||
|
||||
} // end namespace
|
||||
|
||||
static void BM_Factorial(benchmark::State& state) {
|
||||
int fac_42 = 0;
|
||||
while (state.KeepRunning()) fac_42 = Factorial(8);
|
||||
// Prevent compiler optimizations
|
||||
std::stringstream ss;
|
||||
ss << fac_42;
|
||||
state.SetLabel(ss.str());
|
||||
}
|
||||
BENCHMARK(BM_Factorial);
|
||||
BENCHMARK(BM_Factorial)->UseRealTime();
|
||||
|
||||
static void BM_CalculatePiRange(benchmark::State& state) {
|
||||
double pi = 0.0;
|
||||
while (state.KeepRunning()) pi = CalculatePi(state.range(0));
|
||||
std::stringstream ss;
|
||||
ss << pi;
|
||||
state.SetLabel(ss.str());
|
||||
}
|
||||
BENCHMARK_RANGE(BM_CalculatePiRange, 1, 1024 * 1024);
|
||||
|
||||
static void BM_CalculatePi(benchmark::State& state) {
|
||||
static const int depth = 1024;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(CalculatePi(depth));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_CalculatePi)->Threads(8);
|
||||
BENCHMARK(BM_CalculatePi)->ThreadRange(1, 32);
|
||||
BENCHMARK(BM_CalculatePi)->ThreadPerCpu();
|
||||
|
||||
static void BM_SetInsert(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
std::set<int> data = ConstructRandomSet(state.range(0));
|
||||
state.ResumeTiming();
|
||||
for (int j = 0; j < state.range(1); ++j) data.insert(rand());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * state.range(1));
|
||||
state.SetBytesProcessed(state.iterations() * state.range(1) * sizeof(int));
|
||||
}
|
||||
BENCHMARK(BM_SetInsert)->Ranges({{1 << 10, 8 << 10}, {1, 10}});
|
||||
|
||||
template <typename Container,
|
||||
typename ValueType = typename Container::value_type>
|
||||
static void BM_Sequential(benchmark::State& state) {
|
||||
ValueType v = 42;
|
||||
while (state.KeepRunning()) {
|
||||
Container c;
|
||||
for (int i = state.range(0); --i;) c.push_back(v);
|
||||
}
|
||||
const size_t items_processed = state.iterations() * state.range(0);
|
||||
state.SetItemsProcessed(items_processed);
|
||||
state.SetBytesProcessed(items_processed * sizeof(v));
|
||||
}
|
||||
BENCHMARK_TEMPLATE2(BM_Sequential, std::vector<int>, int)
|
||||
->Range(1 << 0, 1 << 10);
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, std::list<int>)->Range(1 << 0, 1 << 10);
|
||||
// Test the variadic version of BENCHMARK_TEMPLATE in C++11 and beyond.
|
||||
#if __cplusplus >= 201103L
|
||||
BENCHMARK_TEMPLATE(BM_Sequential, std::vector<int>, int)->Arg(512);
|
||||
#endif
|
||||
|
||||
static void BM_StringCompare(benchmark::State& state) {
|
||||
std::string s1(state.range(0), '-');
|
||||
std::string s2(state.range(0), '-');
|
||||
while (state.KeepRunning()) benchmark::DoNotOptimize(s1.compare(s2));
|
||||
}
|
||||
BENCHMARK(BM_StringCompare)->Range(1, 1 << 20);
|
||||
|
||||
static void BM_SetupTeardown(benchmark::State& state) {
|
||||
if (state.thread_index == 0) {
|
||||
// No need to lock test_vector_mu here as this is running single-threaded.
|
||||
test_vector = new std::vector<int>();
|
||||
}
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
std::lock_guard<std::mutex> l(test_vector_mu);
|
||||
if (i % 2 == 0)
|
||||
test_vector->push_back(i);
|
||||
else
|
||||
test_vector->pop_back();
|
||||
++i;
|
||||
}
|
||||
if (state.thread_index == 0) {
|
||||
delete test_vector;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_SetupTeardown)->ThreadPerCpu();
|
||||
|
||||
static void BM_LongTest(benchmark::State& state) {
|
||||
double tracker = 0.0;
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
benchmark::DoNotOptimize(tracker += i);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_LongTest)->Range(1 << 16, 1 << 28);
|
||||
|
||||
static void BM_ParallelMemset(benchmark::State& state) {
|
||||
int size = state.range(0) / sizeof(int);
|
||||
int thread_size = size / state.threads;
|
||||
int from = thread_size * state.thread_index;
|
||||
int to = from + thread_size;
|
||||
|
||||
if (state.thread_index == 0) {
|
||||
test_vector = new std::vector<int>(size);
|
||||
}
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = from; i < to; i++) {
|
||||
// No need to lock test_vector_mu as ranges
|
||||
// do not overlap between threads.
|
||||
benchmark::DoNotOptimize(test_vector->at(i) = 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.thread_index == 0) {
|
||||
delete test_vector;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParallelMemset)->Arg(10 << 20)->ThreadRange(1, 4);
|
||||
|
||||
static void BM_ManualTiming(benchmark::State& state) {
|
||||
size_t slept_for = 0;
|
||||
int microseconds = state.range(0);
|
||||
std::chrono::duration<double, std::micro> sleep_duration{
|
||||
static_cast<double>(microseconds)};
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
// Simulate some useful workload with a sleep
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration));
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto elapsed =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
|
||||
|
||||
state.SetIterationTime(elapsed.count());
|
||||
slept_for += microseconds;
|
||||
}
|
||||
state.SetItemsProcessed(slept_for);
|
||||
}
|
||||
BENCHMARK(BM_ManualTiming)->Range(1, 1 << 14)->UseRealTime();
|
||||
BENCHMARK(BM_ManualTiming)->Range(1, 1 << 14)->UseManualTime();
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
|
||||
template <class... Args>
|
||||
void BM_with_args(benchmark::State& state, Args&&...) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_with_args, int_test, 42, 43, 44);
|
||||
BENCHMARK_CAPTURE(BM_with_args, string_and_pair_test, std::string("abc"),
|
||||
std::pair<int, double>(42, 3.8));
|
||||
|
||||
void BM_non_template_args(benchmark::State& state, int, double) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK_CAPTURE(BM_non_template_args, basic_test, 0, 0);
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
static void BM_DenseThreadRanges(benchmark::State& st) {
|
||||
switch (st.range(0)) {
|
||||
case 1:
|
||||
assert(st.threads == 1 || st.threads == 2 || st.threads == 3);
|
||||
break;
|
||||
case 2:
|
||||
assert(st.threads == 1 || st.threads == 3 || st.threads == 4);
|
||||
break;
|
||||
case 3:
|
||||
assert(st.threads == 5 || st.threads == 8 || st.threads == 11 ||
|
||||
st.threads == 14);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Invalid test case number");
|
||||
}
|
||||
while (st.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_DenseThreadRanges)->Arg(1)->DenseThreadRange(1, 3);
|
||||
BENCHMARK(BM_DenseThreadRanges)->Arg(2)->DenseThreadRange(1, 4, 2);
|
||||
BENCHMARK(BM_DenseThreadRanges)->Arg(3)->DenseThreadRange(5, 14, 3);
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,167 +0,0 @@
|
||||
#undef NDEBUG
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "output_test.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#define ADD_COMPLEXITY_CASES(...) \
|
||||
int CONCAT(dummy, __LINE__) = AddComplexityTest(__VA_ARGS__)
|
||||
|
||||
int AddComplexityTest(std::string big_o_test_name, std::string rms_test_name,
|
||||
std::string big_o) {
|
||||
SetSubstitutions({{"%bigo_name", big_o_test_name},
|
||||
{"%rms_name", rms_test_name},
|
||||
{"%bigo_str", "[ ]* %float " + big_o},
|
||||
{"%bigo", big_o},
|
||||
{"%rms", "[ ]*[0-9]+ %"}});
|
||||
AddCases(
|
||||
TC_ConsoleOut,
|
||||
{{"^%bigo_name %bigo_str %bigo_str[ ]*$"},
|
||||
{"^%bigo_name", MR_Not}, // Assert we we didn't only matched a name.
|
||||
{"^%rms_name %rms %rms[ ]*$", MR_Next}});
|
||||
AddCases(TC_JSONOut, {{"\"name\": \"%bigo_name\",$"},
|
||||
{"\"cpu_coefficient\": [0-9]+,$", MR_Next},
|
||||
{"\"real_coefficient\": [0-9]{1,5},$", MR_Next},
|
||||
{"\"big_o\": \"%bigo\",$", MR_Next},
|
||||
{"\"time_unit\": \"ns\"$", MR_Next},
|
||||
{"}", MR_Next},
|
||||
{"\"name\": \"%rms_name\",$"},
|
||||
{"\"rms\": %float$", MR_Next},
|
||||
{"}", MR_Next}});
|
||||
AddCases(TC_CSVOut, {{"^\"%bigo_name\",,%float,%float,%bigo,,,,,$"},
|
||||
{"^\"%bigo_name\"", MR_Not},
|
||||
{"^\"%rms_name\",,%float,%float,,,,,,$", MR_Next}});
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
// ========================================================================= //
|
||||
// --------------------------- Testing BigO O(1) --------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_Complexity_O1(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < 1024; ++i) {
|
||||
benchmark::DoNotOptimize(&i);
|
||||
}
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity(benchmark::o1);
|
||||
BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity();
|
||||
BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity([](int) {
|
||||
return 1.0;
|
||||
});
|
||||
|
||||
const char *big_o_1_test_name = "BM_Complexity_O1_BigO";
|
||||
const char *rms_o_1_test_name = "BM_Complexity_O1_RMS";
|
||||
const char *enum_big_o_1 = "\\([0-9]+\\)";
|
||||
// FIXME: Tolerate both '(1)' and 'lgN' as output when the complexity is auto
|
||||
// deduced.
|
||||
// See https://github.com/google/benchmark/issues/272
|
||||
const char *auto_big_o_1 = "(\\([0-9]+\\))|(lgN)";
|
||||
const char *lambda_big_o_1 = "f\\(N\\)";
|
||||
|
||||
// Add enum tests
|
||||
ADD_COMPLEXITY_CASES(big_o_1_test_name, rms_o_1_test_name, enum_big_o_1);
|
||||
|
||||
// Add auto enum tests
|
||||
ADD_COMPLEXITY_CASES(big_o_1_test_name, rms_o_1_test_name, auto_big_o_1);
|
||||
|
||||
// Add lambda tests
|
||||
ADD_COMPLEXITY_CASES(big_o_1_test_name, rms_o_1_test_name, lambda_big_o_1);
|
||||
|
||||
// ========================================================================= //
|
||||
// --------------------------- Testing BigO O(N) --------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
std::vector<int> ConstructRandomVector(int size) {
|
||||
std::vector<int> v;
|
||||
v.reserve(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
v.push_back(std::rand() % size);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void BM_Complexity_O_N(benchmark::State& state) {
|
||||
auto v = ConstructRandomVector(state.range(0));
|
||||
const int item_not_in_vector =
|
||||
state.range(0) * 2; // Test worst case scenario (item not in vector)
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(std::find(v.begin(), v.end(), item_not_in_vector));
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_Complexity_O_N)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16)
|
||||
->Complexity(benchmark::oN);
|
||||
BENCHMARK(BM_Complexity_O_N)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16)
|
||||
->Complexity([](int n) -> double { return n; });
|
||||
BENCHMARK(BM_Complexity_O_N)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16)
|
||||
->Complexity();
|
||||
|
||||
const char *big_o_n_test_name = "BM_Complexity_O_N_BigO";
|
||||
const char *rms_o_n_test_name = "BM_Complexity_O_N_RMS";
|
||||
const char *enum_auto_big_o_n = "N";
|
||||
const char *lambda_big_o_n = "f\\(N\\)";
|
||||
|
||||
// Add enum tests
|
||||
ADD_COMPLEXITY_CASES(big_o_n_test_name, rms_o_n_test_name, enum_auto_big_o_n);
|
||||
|
||||
// Add lambda tests
|
||||
ADD_COMPLEXITY_CASES(big_o_n_test_name, rms_o_n_test_name, lambda_big_o_n);
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------- Testing BigO O(N*lgN) ------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
static void BM_Complexity_O_N_log_N(benchmark::State& state) {
|
||||
auto v = ConstructRandomVector(state.range(0));
|
||||
while (state.KeepRunning()) {
|
||||
std::sort(v.begin(), v.end());
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_Complexity_O_N_log_N)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16)
|
||||
->Complexity(benchmark::oNLogN);
|
||||
BENCHMARK(BM_Complexity_O_N_log_N)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16)
|
||||
->Complexity([](int n) { return n * std::log2(n); });
|
||||
BENCHMARK(BM_Complexity_O_N_log_N)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16)
|
||||
->Complexity();
|
||||
|
||||
const char *big_o_n_lg_n_test_name = "BM_Complexity_O_N_log_N_BigO";
|
||||
const char *rms_o_n_lg_n_test_name = "BM_Complexity_O_N_log_N_RMS";
|
||||
const char *enum_auto_big_o_n_lg_n = "NlgN";
|
||||
const char *lambda_big_o_n_lg_n = "f\\(N\\)";
|
||||
|
||||
// Add enum tests
|
||||
ADD_COMPLEXITY_CASES(big_o_n_lg_n_test_name, rms_o_n_lg_n_test_name,
|
||||
enum_auto_big_o_n_lg_n);
|
||||
|
||||
// Add lambda tests
|
||||
ADD_COMPLEXITY_CASES(big_o_n_lg_n_test_name, rms_o_n_lg_n_test_name,
|
||||
lambda_big_o_n_lg_n);
|
||||
|
||||
// ========================================================================= //
|
||||
// --------------------------- TEST CASES END ------------------------------ //
|
||||
// ========================================================================= //
|
||||
|
||||
int main(int argc, char *argv[]) { RunOutputTests(argc, argv); }
|
||||
@@ -1,42 +0,0 @@
|
||||
#undef NDEBUG
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#error C++11 or greater detected. Should be C++03.
|
||||
#endif
|
||||
|
||||
void BM_empty(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
volatile std::size_t x = state.iterations();
|
||||
((void)x);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_empty);
|
||||
|
||||
// The new C++11 interface for args/ranges requires initializer list support.
|
||||
// Therefore we provide the old interface to support C++03.
|
||||
void BM_old_arg_range_interface(benchmark::State& state) {
|
||||
assert((state.range(0) == 1 && state.range(1) == 2) ||
|
||||
(state.range(0) == 5 && state.range(1) == 6));
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_old_arg_range_interface)->ArgPair(1, 2)->RangePair(5, 5, 6, 6);
|
||||
|
||||
template <class T, class U>
|
||||
void BM_template2(benchmark::State& state) {
|
||||
BM_empty(state);
|
||||
}
|
||||
BENCHMARK_TEMPLATE2(BM_template2, int, long);
|
||||
|
||||
template <class T>
|
||||
void BM_template1(benchmark::State& state) {
|
||||
BM_empty(state);
|
||||
}
|
||||
BENCHMARK_TEMPLATE(BM_template1, long);
|
||||
BENCHMARK_TEMPLATE1(BM_template1, int);
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,64 +0,0 @@
|
||||
// Testing:
|
||||
// State::PauseTiming()
|
||||
// State::ResumeTiming()
|
||||
// Test that CHECK's within these function diagnose when they are called
|
||||
// outside of the KeepRunning() loop.
|
||||
//
|
||||
// NOTE: Users should NOT include or use src/check.h. This is only done in
|
||||
// order to test library internals.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../src/check.h"
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
|
||||
#define TEST_HAS_NO_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
void TestHandler() {
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
throw std::logic_error("");
|
||||
#else
|
||||
std::abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
void try_invalid_pause_resume(benchmark::State& state) {
|
||||
#if !defined(NDEBUG) && !defined(TEST_HAS_NO_EXCEPTIONS)
|
||||
try {
|
||||
state.PauseTiming();
|
||||
std::abort();
|
||||
} catch (std::logic_error const&) {
|
||||
}
|
||||
try {
|
||||
state.ResumeTiming();
|
||||
std::abort();
|
||||
} catch (std::logic_error const&) {
|
||||
}
|
||||
#else
|
||||
(void)state; // avoid unused warning
|
||||
#endif
|
||||
}
|
||||
|
||||
void BM_diagnostic_test(benchmark::State& state) {
|
||||
static bool called_once = false;
|
||||
|
||||
if (called_once == false) try_invalid_pause_resume(state);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(state.iterations());
|
||||
}
|
||||
|
||||
if (called_once == false) try_invalid_pause_resume(state);
|
||||
|
||||
called_once = true;
|
||||
}
|
||||
BENCHMARK(BM_diagnostic_test);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
benchmark::internal::GetAbortHandler() = &TestHandler;
|
||||
benchmark::Initialize(&argc, argv);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
#if defined(__GNUC__)
|
||||
std::uint64_t double_up(const std::uint64_t x) __attribute__((const));
|
||||
#endif
|
||||
std::uint64_t double_up(const std::uint64_t x) { return x * 2; }
|
||||
}
|
||||
|
||||
int main(int, char*[]) {
|
||||
// this test verifies compilation of DoNotOptimize() for some types
|
||||
|
||||
char buffer8[8];
|
||||
benchmark::DoNotOptimize(buffer8);
|
||||
|
||||
char buffer20[20];
|
||||
benchmark::DoNotOptimize(buffer20);
|
||||
|
||||
char buffer1024[1024];
|
||||
benchmark::DoNotOptimize(buffer1024);
|
||||
benchmark::DoNotOptimize(&buffer1024[0]);
|
||||
|
||||
int x = 123;
|
||||
benchmark::DoNotOptimize(x);
|
||||
benchmark::DoNotOptimize(&x);
|
||||
benchmark::DoNotOptimize(x += 42);
|
||||
|
||||
benchmark::DoNotOptimize(double_up(x));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
class TestReporter : public benchmark::ConsoleReporter {
|
||||
public:
|
||||
virtual bool ReportContext(const Context& context) {
|
||||
return ConsoleReporter::ReportContext(context);
|
||||
};
|
||||
|
||||
virtual void ReportRuns(const std::vector<Run>& report) {
|
||||
++count_;
|
||||
ConsoleReporter::ReportRuns(report);
|
||||
};
|
||||
|
||||
TestReporter() : count_(0) {}
|
||||
|
||||
virtual ~TestReporter() {}
|
||||
|
||||
size_t GetCount() const { return count_; }
|
||||
|
||||
private:
|
||||
mutable size_t count_;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
static void NoPrefix(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(NoPrefix);
|
||||
|
||||
static void BM_Foo(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Foo);
|
||||
|
||||
static void BM_Bar(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Bar);
|
||||
|
||||
static void BM_FooBar(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FooBar);
|
||||
|
||||
static void BM_FooBa(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FooBa);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool list_only = false;
|
||||
for (int i = 0; i < argc; ++i)
|
||||
list_only |= std::string(argv[i]).find("--benchmark_list_tests") !=
|
||||
std::string::npos;
|
||||
|
||||
benchmark::Initialize(&argc, argv);
|
||||
|
||||
TestReporter test_reporter;
|
||||
const size_t returned_count =
|
||||
benchmark::RunSpecifiedBenchmarks(&test_reporter);
|
||||
|
||||
if (argc == 2) {
|
||||
// Make sure we ran all of the tests
|
||||
std::stringstream ss(argv[1]);
|
||||
size_t expected_return;
|
||||
ss >> expected_return;
|
||||
|
||||
if (returned_count != expected_return) {
|
||||
std::cerr << "ERROR: Expected " << expected_return
|
||||
<< " tests to match the filter but returned_count = "
|
||||
<< returned_count << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t expected_reports = list_only ? 0 : expected_return;
|
||||
const size_t reports_count = test_reporter.GetCount();
|
||||
if (reports_count != expected_reports) {
|
||||
std::cerr << "ERROR: Expected " << expected_reports
|
||||
<< " tests to be run but reported_count = " << reports_count
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
class MyFixture : public ::benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
if (state.thread_index == 0) {
|
||||
assert(data.get() == nullptr);
|
||||
data.reset(new int(42));
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& state) {
|
||||
if (state.thread_index == 0) {
|
||||
assert(data.get() != nullptr);
|
||||
data.reset();
|
||||
}
|
||||
}
|
||||
|
||||
~MyFixture() { assert(data == nullptr); }
|
||||
|
||||
std::unique_ptr<int> data;
|
||||
};
|
||||
|
||||
BENCHMARK_F(MyFixture, Foo)(benchmark::State &st) {
|
||||
assert(data.get() != nullptr);
|
||||
assert(*data == 42);
|
||||
while (st.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, Bar)(benchmark::State& st) {
|
||||
if (st.thread_index == 0) {
|
||||
assert(data.get() != nullptr);
|
||||
assert(*data == 42);
|
||||
}
|
||||
while (st.KeepRunning()) {
|
||||
assert(data.get() != nullptr);
|
||||
assert(*data == 42);
|
||||
}
|
||||
st.SetItemsProcessed(st.range(0));
|
||||
}
|
||||
BENCHMARK_REGISTER_F(MyFixture, Bar)->Arg(42);
|
||||
BENCHMARK_REGISTER_F(MyFixture, Bar)->Arg(42)->ThreadPerCpu();
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<int, int> ConstructRandomMap(int size) {
|
||||
std::map<int, int> m;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
m.insert(std::make_pair(rand() % size, rand() % size));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Basic version.
|
||||
static void BM_MapLookup(benchmark::State& state) {
|
||||
const int size = state.range(0);
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
std::map<int, int> m = ConstructRandomMap(size);
|
||||
state.ResumeTiming();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
benchmark::DoNotOptimize(m.find(rand() % size));
|
||||
}
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * size);
|
||||
}
|
||||
BENCHMARK(BM_MapLookup)->Range(1 << 3, 1 << 12);
|
||||
|
||||
// Using fixtures.
|
||||
class MapFixture : public ::benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& st) {
|
||||
m = ConstructRandomMap(st.range(0));
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State&) { m.clear(); }
|
||||
|
||||
std::map<int, int> m;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(MapFixture, Lookup)(benchmark::State& state) {
|
||||
const int size = state.range(0);
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
benchmark::DoNotOptimize(m.find(rand() % size));
|
||||
}
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * size);
|
||||
}
|
||||
BENCHMARK_REGISTER_F(MapFixture, Lookup)->Range(1 << 3, 1 << 12);
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,74 +0,0 @@
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
|
||||
class MultipleRangesFixture : public ::benchmark::Fixture {
|
||||
public:
|
||||
MultipleRangesFixture()
|
||||
: expectedValues({{1, 3, 5},
|
||||
{1, 3, 8},
|
||||
{1, 3, 15},
|
||||
{2, 3, 5},
|
||||
{2, 3, 8},
|
||||
{2, 3, 15},
|
||||
{1, 4, 5},
|
||||
{1, 4, 8},
|
||||
{1, 4, 15},
|
||||
{2, 4, 5},
|
||||
{2, 4, 8},
|
||||
{2, 4, 15},
|
||||
{1, 7, 5},
|
||||
{1, 7, 8},
|
||||
{1, 7, 15},
|
||||
{2, 7, 5},
|
||||
{2, 7, 8},
|
||||
{2, 7, 15},
|
||||
{7, 6, 3}}) {}
|
||||
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::vector<int> ranges = {state.range(0), state.range(1), state.range(2)};
|
||||
|
||||
assert(expectedValues.find(ranges) != expectedValues.end());
|
||||
|
||||
actualValues.insert(ranges);
|
||||
}
|
||||
|
||||
virtual ~MultipleRangesFixture() {
|
||||
assert(actualValues.size() == expectedValues.size());
|
||||
}
|
||||
|
||||
std::set<std::vector<int>> expectedValues;
|
||||
std::set<std::vector<int>> actualValues;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(MultipleRangesFixture, Empty)(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
int product = state.range(0) * state.range(1) * state.range(2);
|
||||
for (int x = 0; x < product; x++) {
|
||||
benchmark::DoNotOptimize(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(MultipleRangesFixture, Empty)
|
||||
->RangeMultiplier(2)
|
||||
->Ranges({{1, 2}, {3, 7}, {5, 15}})
|
||||
->Args({7, 6, 3});
|
||||
|
||||
void BM_CheckDefaultArgument(benchmark::State& state) {
|
||||
// Test that the 'range()' without an argument is the same as 'range(0)'.
|
||||
assert(state.range() == state.range(0));
|
||||
assert(state.range() != state.range(1));
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_CheckDefaultArgument)->Ranges({{1, 5}, {6, 10}});
|
||||
|
||||
static void BM_MultipleRanges(benchmark::State& st) {
|
||||
while (st.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_MultipleRanges)->Ranges({{5, 5}, {6, 6}});
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,43 +0,0 @@
|
||||
#include "benchmark/benchmark_api.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
void BM_basic(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
|
||||
void BM_basic_slow(benchmark::State& state) {
|
||||
std::chrono::milliseconds sleep_duration(state.range(0));
|
||||
while (state.KeepRunning()) {
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_basic);
|
||||
BENCHMARK(BM_basic)->Arg(42);
|
||||
BENCHMARK(BM_basic_slow)->Arg(10)->Unit(benchmark::kNanosecond);
|
||||
BENCHMARK(BM_basic_slow)->Arg(100)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(BM_basic_slow)->Arg(1000)->Unit(benchmark::kMillisecond);
|
||||
BENCHMARK(BM_basic)->Range(1, 8);
|
||||
BENCHMARK(BM_basic)->RangeMultiplier(2)->Range(1, 8);
|
||||
BENCHMARK(BM_basic)->DenseRange(10, 15);
|
||||
BENCHMARK(BM_basic)->Args({42, 42});
|
||||
BENCHMARK(BM_basic)->Ranges({{64, 512}, {64, 512}});
|
||||
BENCHMARK(BM_basic)->MinTime(0.7);
|
||||
BENCHMARK(BM_basic)->UseRealTime();
|
||||
BENCHMARK(BM_basic)->ThreadRange(2, 4);
|
||||
BENCHMARK(BM_basic)->ThreadPerCpu();
|
||||
BENCHMARK(BM_basic)->Repetitions(3);
|
||||
|
||||
void CustomArgs(benchmark::internal::Benchmark* b) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
b->Arg(i);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_basic)->Apply(CustomArgs);
|
||||
|
||||
BENCHMARK_MAIN()
|
||||
@@ -1,71 +0,0 @@
|
||||
#ifndef TEST_OUTPUT_TEST_H
|
||||
#define TEST_OUTPUT_TEST_H
|
||||
|
||||
#undef NDEBUG
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../src/re.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#define CONCAT2(x, y) x##y
|
||||
#define CONCAT(x, y) CONCAT2(x, y)
|
||||
|
||||
#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = ::AddCases(__VA_ARGS__)
|
||||
|
||||
#define SET_SUBSTITUTIONS(...) \
|
||||
int CONCAT(dummy, __LINE__) = ::SetSubstitutions(__VA_ARGS__)
|
||||
|
||||
enum MatchRules {
|
||||
MR_Default, // Skip non-matching lines until a match is found.
|
||||
MR_Next, // Match must occur on the next line.
|
||||
MR_Not // No line between the current position and the next match matches
|
||||
// the regex
|
||||
};
|
||||
|
||||
struct TestCase {
|
||||
TestCase(std::string re, int rule = MR_Default);
|
||||
|
||||
std::string regex_str;
|
||||
int match_rule;
|
||||
std::string substituted_regex;
|
||||
std::shared_ptr<benchmark::Regex> regex;
|
||||
};
|
||||
|
||||
enum TestCaseID {
|
||||
TC_ConsoleOut,
|
||||
TC_ConsoleErr,
|
||||
TC_JSONOut,
|
||||
TC_JSONErr,
|
||||
TC_CSVOut,
|
||||
TC_CSVErr,
|
||||
|
||||
TC_NumID // PRIVATE
|
||||
};
|
||||
|
||||
// Add a list of test cases to be run against the output specified by
|
||||
// 'ID'
|
||||
int AddCases(TestCaseID ID, std::initializer_list<TestCase> il);
|
||||
|
||||
// Add or set a list of substitutions to be performed on constructed regex's
|
||||
// See 'output_test_helper.cc' for a list of default substitutions.
|
||||
int SetSubstitutions(
|
||||
std::initializer_list<std::pair<std::string, std::string>> il);
|
||||
|
||||
// Run all output tests.
|
||||
void RunOutputTests(int argc, char* argv[]);
|
||||
|
||||
// ========================================================================= //
|
||||
// --------------------------- Misc Utilities ------------------------------ //
|
||||
// ========================================================================= //
|
||||
|
||||
namespace {
|
||||
|
||||
const char* const dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
|
||||
|
||||
} // end namespace
|
||||
|
||||
#endif // TEST_OUTPUT_TEST_H
|
||||
@@ -1,234 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "../src/check.h" // NOTE: check.h is for internal use only!
|
||||
#include "../src/re.h" // NOTE: re.h is for internal use only
|
||||
#include "output_test.h"
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------------ Internals -------------------------------- //
|
||||
// ========================================================================= //
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
using TestCaseList = std::vector<TestCase>;
|
||||
|
||||
// Use a vector because the order elements are added matters during iteration.
|
||||
// std::map/unordered_map don't guarantee that.
|
||||
// For example:
|
||||
// SetSubstitutions({{"%HelloWorld", "Hello"}, {"%Hello", "Hi"}});
|
||||
// Substitute("%HelloWorld") // Always expands to Hello.
|
||||
using SubMap = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
TestCaseList& GetTestCaseList(TestCaseID ID) {
|
||||
// Uses function-local statics to ensure initialization occurs
|
||||
// before first use.
|
||||
static TestCaseList lists[TC_NumID];
|
||||
return lists[ID];
|
||||
}
|
||||
|
||||
SubMap& GetSubstitutions() {
|
||||
// Don't use 'dec_re' from header because it may not yet be initialized.
|
||||
static std::string dec_re = "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?";
|
||||
static SubMap map = {
|
||||
{"%float", "[0-9]*[.]?[0-9]+([eE][-+][0-9]+)?"},
|
||||
{"%int", "[ ]*[0-9]+"},
|
||||
{" %s ", "[ ]+"},
|
||||
{"%time", "[ ]*[0-9]{1,5} ns"},
|
||||
{"%console_report", "[ ]*[0-9]{1,5} ns [ ]*[0-9]{1,5} ns [ ]*[0-9]+"},
|
||||
{"%console_us_report", "[ ]*[0-9] us [ ]*[0-9] us [ ]*[0-9]+"},
|
||||
{"%csv_report", "[0-9]+," + dec_re + "," + dec_re + ",ns,,,,,"},
|
||||
{"%csv_us_report", "[0-9]+," + dec_re + "," + dec_re + ",us,,,,,"},
|
||||
{"%csv_bytes_report",
|
||||
"[0-9]+," + dec_re + "," + dec_re + ",ns," + dec_re + ",,,,"},
|
||||
{"%csv_items_report",
|
||||
"[0-9]+," + dec_re + "," + dec_re + ",ns,," + dec_re + ",,,"},
|
||||
{"%csv_label_report_begin", "[0-9]+," + dec_re + "," + dec_re + ",ns,,,"},
|
||||
{"%csv_label_report_end", ",,"}};
|
||||
return map;
|
||||
}
|
||||
|
||||
std::string PerformSubstitutions(std::string source) {
|
||||
SubMap const& subs = GetSubstitutions();
|
||||
using SizeT = std::string::size_type;
|
||||
for (auto const& KV : subs) {
|
||||
SizeT pos;
|
||||
SizeT next_start = 0;
|
||||
while ((pos = source.find(KV.first, next_start)) != std::string::npos) {
|
||||
next_start = pos + KV.second.size();
|
||||
source.replace(pos, KV.first.size(), KV.second);
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
void CheckCase(std::stringstream& remaining_output, TestCase const& TC,
|
||||
TestCaseList const& not_checks) {
|
||||
std::string first_line;
|
||||
bool on_first = true;
|
||||
std::string line;
|
||||
while (remaining_output.eof() == false) {
|
||||
CHECK(remaining_output.good());
|
||||
std::getline(remaining_output, line);
|
||||
if (on_first) {
|
||||
first_line = line;
|
||||
on_first = false;
|
||||
}
|
||||
for (const auto& NC : not_checks) {
|
||||
CHECK(!NC.regex->Match(line))
|
||||
<< "Unexpected match for line \"" << line << "\" for MR_Not regex \""
|
||||
<< NC.regex_str << "\""
|
||||
<< "\n actual regex string \"" << TC.substituted_regex << "\""
|
||||
<< "\n started matching near: " << first_line;
|
||||
}
|
||||
if (TC.regex->Match(line)) return;
|
||||
CHECK(TC.match_rule != MR_Next)
|
||||
<< "Expected line \"" << line << "\" to match regex \"" << TC.regex_str
|
||||
<< "\""
|
||||
<< "\n actual regex string \"" << TC.substituted_regex << "\""
|
||||
<< "\n started matching near: " << first_line;
|
||||
}
|
||||
CHECK(remaining_output.eof() == false)
|
||||
<< "End of output reached before match for regex \"" << TC.regex_str
|
||||
<< "\" was found"
|
||||
<< "\n actual regex string \"" << TC.substituted_regex << "\""
|
||||
<< "\n started matching near: " << first_line;
|
||||
}
|
||||
|
||||
void CheckCases(TestCaseList const& checks, std::stringstream& output) {
|
||||
std::vector<TestCase> not_checks;
|
||||
for (size_t i = 0; i < checks.size(); ++i) {
|
||||
const auto& TC = checks[i];
|
||||
if (TC.match_rule == MR_Not) {
|
||||
not_checks.push_back(TC);
|
||||
continue;
|
||||
}
|
||||
CheckCase(output, TC, not_checks);
|
||||
not_checks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class TestReporter : public benchmark::BenchmarkReporter {
|
||||
public:
|
||||
TestReporter(std::vector<benchmark::BenchmarkReporter*> reps)
|
||||
: reporters_(reps) {}
|
||||
|
||||
virtual bool ReportContext(const Context& context) {
|
||||
bool last_ret = false;
|
||||
bool first = true;
|
||||
for (auto rep : reporters_) {
|
||||
bool new_ret = rep->ReportContext(context);
|
||||
CHECK(first || new_ret == last_ret)
|
||||
<< "Reports return different values for ReportContext";
|
||||
first = false;
|
||||
last_ret = new_ret;
|
||||
}
|
||||
(void)first;
|
||||
return last_ret;
|
||||
}
|
||||
|
||||
void ReportRuns(const std::vector<Run>& report) {
|
||||
for (auto rep : reporters_) rep->ReportRuns(report);
|
||||
}
|
||||
void Finalize() {
|
||||
for (auto rep : reporters_) rep->Finalize();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<benchmark::BenchmarkReporter *> reporters_;
|
||||
};
|
||||
}
|
||||
} // end namespace internal
|
||||
|
||||
// ========================================================================= //
|
||||
// -------------------------- Public API Definitions------------------------ //
|
||||
// ========================================================================= //
|
||||
|
||||
TestCase::TestCase(std::string re, int rule)
|
||||
: regex_str(std::move(re)),
|
||||
match_rule(rule),
|
||||
substituted_regex(internal::PerformSubstitutions(regex_str)),
|
||||
regex(std::make_shared<benchmark::Regex>()) {
|
||||
std::string err_str;
|
||||
regex->Init(substituted_regex,& err_str);
|
||||
CHECK(err_str.empty()) << "Could not construct regex \"" << substituted_regex
|
||||
<< "\""
|
||||
<< "\n originally \"" << regex_str << "\""
|
||||
<< "\n got error: " << err_str;
|
||||
}
|
||||
|
||||
int AddCases(TestCaseID ID, std::initializer_list<TestCase> il) {
|
||||
auto& L = internal::GetTestCaseList(ID);
|
||||
L.insert(L.end(), il);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SetSubstitutions(
|
||||
std::initializer_list<std::pair<std::string, std::string>> il) {
|
||||
auto& subs = internal::GetSubstitutions();
|
||||
for (auto KV : il) {
|
||||
bool exists = false;
|
||||
KV.second = internal::PerformSubstitutions(KV.second);
|
||||
for (auto& EKV : subs) {
|
||||
if (EKV.first == KV.first) {
|
||||
EKV.second = std::move(KV.second);
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) subs.push_back(std::move(KV));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RunOutputTests(int argc, char* argv[]) {
|
||||
using internal::GetTestCaseList;
|
||||
benchmark::Initialize(&argc, argv);
|
||||
benchmark::ConsoleReporter CR(benchmark::ConsoleReporter::OO_None);
|
||||
benchmark::JSONReporter JR;
|
||||
benchmark::CSVReporter CSVR;
|
||||
struct ReporterTest {
|
||||
const char* name;
|
||||
std::vector<TestCase>& output_cases;
|
||||
std::vector<TestCase>& error_cases;
|
||||
benchmark::BenchmarkReporter& reporter;
|
||||
std::stringstream out_stream;
|
||||
std::stringstream err_stream;
|
||||
|
||||
ReporterTest(const char* n, std::vector<TestCase>& out_tc,
|
||||
std::vector<TestCase>& err_tc,
|
||||
benchmark::BenchmarkReporter& br)
|
||||
: name(n), output_cases(out_tc), error_cases(err_tc), reporter(br) {
|
||||
reporter.SetOutputStream(&out_stream);
|
||||
reporter.SetErrorStream(&err_stream);
|
||||
}
|
||||
} TestCases[] = {
|
||||
{"ConsoleReporter", GetTestCaseList(TC_ConsoleOut),
|
||||
GetTestCaseList(TC_ConsoleErr), CR},
|
||||
{"JSONReporter", GetTestCaseList(TC_JSONOut), GetTestCaseList(TC_JSONErr),
|
||||
JR},
|
||||
{"CSVReporter", GetTestCaseList(TC_CSVOut), GetTestCaseList(TC_CSVErr),
|
||||
CSVR},
|
||||
};
|
||||
|
||||
// Create the test reporter and run the benchmarks.
|
||||
std::cout << "Running benchmarks...\n";
|
||||
internal::TestReporter test_rep({&CR, &JR, &CSVR});
|
||||
benchmark::RunSpecifiedBenchmarks(&test_rep);
|
||||
|
||||
for (auto& rep_test : TestCases) {
|
||||
std::string msg = std::string("\nTesting ") + rep_test.name + " Output\n";
|
||||
std::string banner(msg.size() - 1, '-');
|
||||
std::cout << banner << msg << banner << "\n";
|
||||
|
||||
std::cerr << rep_test.err_stream.str();
|
||||
std::cout << rep_test.out_stream.str();
|
||||
|
||||
internal::CheckCases(rep_test.error_cases, rep_test.err_stream);
|
||||
internal::CheckCases(rep_test.output_cases, rep_test.out_stream);
|
||||
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
|
||||
#undef NDEBUG
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "../src/check.h" // NOTE: check.h is for internal use only!
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class TestReporter : public benchmark::ConsoleReporter {
|
||||
public:
|
||||
virtual void ReportRuns(const std::vector<Run>& report) {
|
||||
all_runs_.insert(all_runs_.end(), begin(report), end(report));
|
||||
ConsoleReporter::ReportRuns(report);
|
||||
}
|
||||
|
||||
std::vector<Run> all_runs_;
|
||||
};
|
||||
|
||||
struct TestCase {
|
||||
std::string name;
|
||||
const char* label;
|
||||
// Note: not explicit as we rely on it being converted through ADD_CASES.
|
||||
TestCase(const char* xname) : TestCase(xname, nullptr) {}
|
||||
TestCase(const char* xname, const char* xlabel)
|
||||
: name(xname), label(xlabel) {}
|
||||
|
||||
typedef benchmark::BenchmarkReporter::Run Run;
|
||||
|
||||
void CheckRun(Run const& run) const {
|
||||
CHECK(name == run.benchmark_name) << "expected " << name << " got "
|
||||
<< run.benchmark_name;
|
||||
if (label) {
|
||||
CHECK(run.report_label == label) << "expected " << label << " got "
|
||||
<< run.report_label;
|
||||
} else {
|
||||
CHECK(run.report_label == "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<TestCase> ExpectedResults;
|
||||
|
||||
int AddCases(std::initializer_list<TestCase> const& v) {
|
||||
for (auto N : v) {
|
||||
ExpectedResults.push_back(N);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CONCAT(x, y) CONCAT2(x, y)
|
||||
#define CONCAT2(x, y) x##y
|
||||
#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = AddCases({__VA_ARGS__})
|
||||
|
||||
} // end namespace
|
||||
|
||||
typedef benchmark::internal::Benchmark* ReturnVal;
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// Test RegisterBenchmark with no additional arguments
|
||||
//----------------------------------------------------------------------------//
|
||||
void BM_function(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_function);
|
||||
ReturnVal dummy = benchmark::RegisterBenchmark(
|
||||
"BM_function_manual_registration", BM_function);
|
||||
ADD_CASES({"BM_function"}, {"BM_function_manual_registration"});
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// Test RegisterBenchmark with additional arguments
|
||||
// Note: GCC <= 4.8 do not support this form of RegisterBenchmark because they
|
||||
// reject the variadic pack expansion of lambda captures.
|
||||
//----------------------------------------------------------------------------//
|
||||
#ifndef BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||
|
||||
void BM_extra_args(benchmark::State& st, const char* label) {
|
||||
while (st.KeepRunning()) {
|
||||
}
|
||||
st.SetLabel(label);
|
||||
}
|
||||
int RegisterFromFunction() {
|
||||
std::pair<const char*, const char*> cases[] = {
|
||||
{"test1", "One"}, {"test2", "Two"}, {"test3", "Three"}};
|
||||
for (auto const& c : cases)
|
||||
benchmark::RegisterBenchmark(c.first, &BM_extra_args, c.second);
|
||||
return 0;
|
||||
}
|
||||
int dummy2 = RegisterFromFunction();
|
||||
ADD_CASES({"test1", "One"}, {"test2", "Two"}, {"test3", "Three"});
|
||||
|
||||
#endif // BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// Test RegisterBenchmark with different callable types
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
struct CustomFixture {
|
||||
void operator()(benchmark::State& st) {
|
||||
while (st.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void TestRegistrationAtRuntime() {
|
||||
#ifdef BENCHMARK_HAS_CXX11
|
||||
{
|
||||
CustomFixture fx;
|
||||
benchmark::RegisterBenchmark("custom_fixture", fx);
|
||||
AddCases({"custom_fixture"});
|
||||
}
|
||||
#endif
|
||||
#ifndef BENCHMARK_HAS_NO_VARIADIC_REGISTER_BENCHMARK
|
||||
{
|
||||
int x = 42;
|
||||
auto capturing_lam = [=](benchmark::State& st) {
|
||||
while (st.KeepRunning()) {
|
||||
}
|
||||
st.SetLabel(std::to_string(x));
|
||||
};
|
||||
benchmark::RegisterBenchmark("lambda_benchmark", capturing_lam);
|
||||
AddCases({{"lambda_benchmark", "42"}});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
TestRegistrationAtRuntime();
|
||||
|
||||
benchmark::Initialize(&argc, argv);
|
||||
|
||||
TestReporter test_reporter;
|
||||
benchmark::RunSpecifiedBenchmarks(&test_reporter);
|
||||
|
||||
typedef benchmark::BenchmarkReporter::Run Run;
|
||||
auto EB = ExpectedResults.begin();
|
||||
|
||||
for (Run const& run : test_reporter.all_runs_) {
|
||||
assert(EB != ExpectedResults.end());
|
||||
EB->CheckRun(run);
|
||||
++EB;
|
||||
}
|
||||
assert(EB == ExpectedResults.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
|
||||
#undef NDEBUG
|
||||
#include <utility>
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "output_test.h"
|
||||
|
||||
// ========================================================================= //
|
||||
// ---------------------- Testing Prologue Output -------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
ADD_CASES(TC_ConsoleOut, {{"^Benchmark %s Time %s CPU %s Iterations$", MR_Next},
|
||||
{"^[-]+$", MR_Next}});
|
||||
ADD_CASES(TC_CSVOut,
|
||||
{{"name,iterations,real_time,cpu_time,time_unit,bytes_per_second,"
|
||||
"items_per_second,label,error_occurred,error_message"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Basic Output --------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_basic(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_basic);
|
||||
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_basic %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_basic\",$"},
|
||||
{"\"iterations\": %int,$", MR_Next},
|
||||
{"\"real_time\": %int,$", MR_Next},
|
||||
{"\"cpu_time\": %int,$", MR_Next},
|
||||
{"\"time_unit\": \"ns\"$", MR_Next},
|
||||
{"}", MR_Next}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_basic\",%csv_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Bytes per Second Output ---------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_bytes_per_second(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
state.SetBytesProcessed(1);
|
||||
}
|
||||
BENCHMARK(BM_bytes_per_second);
|
||||
|
||||
ADD_CASES(TC_ConsoleOut,
|
||||
{{"^BM_bytes_per_second %console_report +%floatB/s$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_bytes_per_second\",$"},
|
||||
{"\"iterations\": %int,$", MR_Next},
|
||||
{"\"real_time\": %int,$", MR_Next},
|
||||
{"\"cpu_time\": %int,$", MR_Next},
|
||||
{"\"time_unit\": \"ns\",$", MR_Next},
|
||||
{"\"bytes_per_second\": %int$", MR_Next},
|
||||
{"}", MR_Next}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_bytes_per_second\",%csv_bytes_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Items per Second Output ---------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_items_per_second(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
state.SetItemsProcessed(1);
|
||||
}
|
||||
BENCHMARK(BM_items_per_second);
|
||||
|
||||
ADD_CASES(TC_ConsoleOut,
|
||||
{{"^BM_items_per_second %console_report +%float items/s$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_items_per_second\",$"},
|
||||
{"\"iterations\": %int,$", MR_Next},
|
||||
{"\"real_time\": %int,$", MR_Next},
|
||||
{"\"cpu_time\": %int,$", MR_Next},
|
||||
{"\"time_unit\": \"ns\",$", MR_Next},
|
||||
{"\"items_per_second\": %int$", MR_Next},
|
||||
{"}", MR_Next}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_items_per_second\",%csv_items_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Label Output --------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_label(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
state.SetLabel("some label");
|
||||
}
|
||||
BENCHMARK(BM_label);
|
||||
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_label %console_report some label$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_label\",$"},
|
||||
{"\"iterations\": %int,$", MR_Next},
|
||||
{"\"real_time\": %int,$", MR_Next},
|
||||
{"\"cpu_time\": %int,$", MR_Next},
|
||||
{"\"time_unit\": \"ns\",$", MR_Next},
|
||||
{"\"label\": \"some label\"$", MR_Next},
|
||||
{"}", MR_Next}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_label\",%csv_label_report_begin\"some "
|
||||
"label\"%csv_label_report_end$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Error Output --------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_error(benchmark::State& state) {
|
||||
state.SkipWithError("message");
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_error);
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_error[ ]+ERROR OCCURRED: 'message'$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_error\",$"},
|
||||
{"\"error_occurred\": true,$", MR_Next},
|
||||
{"\"error_message\": \"message\",$", MR_Next}});
|
||||
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_error\",,,,,,,,true,\"message\"$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing No Arg Name Output -----------------------
|
||||
// //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_no_arg_name(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_no_arg_name)->Arg(3);
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_no_arg_name/3 %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_no_arg_name/3\",$"}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_no_arg_name/3\",%csv_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Arg Name Output ----------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_arg_name(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_arg_name)->ArgName("first")->Arg(3);
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_arg_name/first:3 %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_arg_name/first:3\",$"}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_arg_name/first:3\",%csv_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ------------------------ Testing Arg Names Output ----------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_arg_names(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_arg_names)->Args({2, 5, 4})->ArgNames({"first", "", "third"});
|
||||
ADD_CASES(TC_ConsoleOut,
|
||||
{{"^BM_arg_names/first:2/5/third:4 %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_arg_names/first:2/5/third:4\",$"}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_arg_names/first:2/5/third:4\",%csv_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ----------------------- Testing Complexity Output ----------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
void BM_Complexity_O1(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_Complexity_O1)->Range(1, 1 << 18)->Complexity(benchmark::o1);
|
||||
SET_SUBSTITUTIONS({{"%bigOStr", "[ ]* %float \\([0-9]+\\)"},
|
||||
{"%RMS", "[ ]*[0-9]+ %"}});
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_Complexity_O1_BigO %bigOStr %bigOStr[ ]*$"},
|
||||
{"^BM_Complexity_O1_RMS %RMS %RMS[ ]*$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// ----------------------- Testing Aggregate Output ------------------------ //
|
||||
// ========================================================================= //
|
||||
|
||||
// Test that non-aggregate data is printed by default
|
||||
void BM_Repeat(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Repeat)->Repetitions(3);
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_Repeat/repeats:3 %console_report$"},
|
||||
{"^BM_Repeat/repeats:3 %console_report$"},
|
||||
{"^BM_Repeat/repeats:3 %console_report$"},
|
||||
{"^BM_Repeat/repeats:3_mean %console_report$"},
|
||||
{"^BM_Repeat/repeats:3_stddev %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_Repeat/repeats:3\",$"},
|
||||
{"\"name\": \"BM_Repeat/repeats:3\",$"},
|
||||
{"\"name\": \"BM_Repeat/repeats:3\",$"},
|
||||
{"\"name\": \"BM_Repeat/repeats:3_mean\",$"},
|
||||
{"\"name\": \"BM_Repeat/repeats:3_stddev\",$"}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_Repeat/repeats:3\",%csv_report$"},
|
||||
{"^\"BM_Repeat/repeats:3\",%csv_report$"},
|
||||
{"^\"BM_Repeat/repeats:3\",%csv_report$"},
|
||||
{"^\"BM_Repeat/repeats:3_mean\",%csv_report$"},
|
||||
{"^\"BM_Repeat/repeats:3_stddev\",%csv_report$"}});
|
||||
|
||||
// Test that a non-repeated test still prints non-aggregate results even when
|
||||
// only-aggregate reports have been requested
|
||||
void BM_RepeatOnce(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_RepeatOnce)->Repetitions(1)->ReportAggregatesOnly();
|
||||
ADD_CASES(TC_ConsoleOut, {{"^BM_RepeatOnce/repeats:1 %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{"\"name\": \"BM_RepeatOnce/repeats:1\",$"}});
|
||||
ADD_CASES(TC_CSVOut, {{"^\"BM_RepeatOnce/repeats:1\",%csv_report$"}});
|
||||
|
||||
// Test that non-aggregate data is not reported
|
||||
void BM_SummaryRepeat(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_SummaryRepeat)->Repetitions(3)->ReportAggregatesOnly();
|
||||
ADD_CASES(TC_ConsoleOut,
|
||||
{{".*BM_SummaryRepeat/repeats:3 ", MR_Not},
|
||||
{"^BM_SummaryRepeat/repeats:3_mean %console_report$"},
|
||||
{"^BM_SummaryRepeat/repeats:3_stddev %console_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{".*BM_SummaryRepeat/repeats:3 ", MR_Not},
|
||||
{"\"name\": \"BM_SummaryRepeat/repeats:3_mean\",$"},
|
||||
{"\"name\": \"BM_SummaryRepeat/repeats:3_stddev\",$"}});
|
||||
ADD_CASES(TC_CSVOut, {{".*BM_SummaryRepeat/repeats:3 ", MR_Not},
|
||||
{"^\"BM_SummaryRepeat/repeats:3_mean\",%csv_report$"},
|
||||
{"^\"BM_SummaryRepeat/repeats:3_stddev\",%csv_report$"}});
|
||||
|
||||
void BM_RepeatTimeUnit(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_RepeatTimeUnit)
|
||||
->Repetitions(3)
|
||||
->ReportAggregatesOnly()
|
||||
->Unit(benchmark::kMicrosecond);
|
||||
ADD_CASES(TC_ConsoleOut,
|
||||
{{".*BM_RepeatTimeUnit/repeats:3 ", MR_Not},
|
||||
{"^BM_RepeatTimeUnit/repeats:3_mean %console_us_report$"},
|
||||
{"^BM_RepeatTimeUnit/repeats:3_stddev %console_us_report$"}});
|
||||
ADD_CASES(TC_JSONOut, {{".*BM_RepeatTimeUnit/repeats:3 ", MR_Not},
|
||||
{"\"name\": \"BM_RepeatTimeUnit/repeats:3_mean\",$"},
|
||||
{"\"time_unit\": \"us\",?$"},
|
||||
{"\"name\": \"BM_RepeatTimeUnit/repeats:3_stddev\",$"},
|
||||
{"\"time_unit\": \"us\",?$"}});
|
||||
ADD_CASES(TC_CSVOut,
|
||||
{{".*BM_RepeatTimeUnit/repeats:3 ", MR_Not},
|
||||
{"^\"BM_RepeatTimeUnit/repeats:3_mean\",%csv_us_report$"},
|
||||
{"^\"BM_RepeatTimeUnit/repeats:3_stddev\",%csv_us_report$"}});
|
||||
|
||||
// ========================================================================= //
|
||||
// --------------------------- TEST CASES END ------------------------------ //
|
||||
// ========================================================================= //
|
||||
|
||||
int main(int argc, char* argv[]) { RunOutputTests(argc, argv); }
|
||||
@@ -1,150 +0,0 @@
|
||||
|
||||
#undef NDEBUG
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "../src/check.h" // NOTE: check.h is for internal use only!
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class TestReporter : public benchmark::ConsoleReporter {
|
||||
public:
|
||||
virtual bool ReportContext(const Context& context) {
|
||||
return ConsoleReporter::ReportContext(context);
|
||||
};
|
||||
|
||||
virtual void ReportRuns(const std::vector<Run>& report) {
|
||||
all_runs_.insert(all_runs_.end(), begin(report), end(report));
|
||||
ConsoleReporter::ReportRuns(report);
|
||||
}
|
||||
|
||||
TestReporter() {}
|
||||
virtual ~TestReporter() {}
|
||||
|
||||
mutable std::vector<Run> all_runs_;
|
||||
};
|
||||
|
||||
struct TestCase {
|
||||
std::string name;
|
||||
bool error_occurred;
|
||||
std::string error_message;
|
||||
|
||||
typedef benchmark::BenchmarkReporter::Run Run;
|
||||
|
||||
void CheckRun(Run const& run) const {
|
||||
CHECK(name == run.benchmark_name) << "expected " << name << " got "
|
||||
<< run.benchmark_name;
|
||||
CHECK(error_occurred == run.error_occurred);
|
||||
CHECK(error_message == run.error_message);
|
||||
if (error_occurred) {
|
||||
// CHECK(run.iterations == 0);
|
||||
} else {
|
||||
CHECK(run.iterations != 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<TestCase> ExpectedResults;
|
||||
|
||||
int AddCases(const char* base_name, std::initializer_list<TestCase> const& v) {
|
||||
for (auto TC : v) {
|
||||
TC.name = base_name + TC.name;
|
||||
ExpectedResults.push_back(std::move(TC));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CONCAT(x, y) CONCAT2(x, y)
|
||||
#define CONCAT2(x, y) x##y
|
||||
#define ADD_CASES(...) int CONCAT(dummy, __LINE__) = AddCases(__VA_ARGS__)
|
||||
|
||||
} // end namespace
|
||||
|
||||
void BM_error_before_running(benchmark::State& state) {
|
||||
state.SkipWithError("error message");
|
||||
while (state.KeepRunning()) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_error_before_running);
|
||||
ADD_CASES("BM_error_before_running", {{"", true, "error message"}});
|
||||
|
||||
void BM_error_during_running(benchmark::State& state) {
|
||||
int first_iter = true;
|
||||
while (state.KeepRunning()) {
|
||||
if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
|
||||
assert(first_iter);
|
||||
first_iter = false;
|
||||
state.SkipWithError("error message");
|
||||
} else {
|
||||
state.PauseTiming();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_error_during_running)->Arg(1)->Arg(2)->ThreadRange(1, 8);
|
||||
ADD_CASES("BM_error_during_running", {{"/1/threads:1", true, "error message"},
|
||||
{"/1/threads:2", true, "error message"},
|
||||
{"/1/threads:4", true, "error message"},
|
||||
{"/1/threads:8", true, "error message"},
|
||||
{"/2/threads:1", false, ""},
|
||||
{"/2/threads:2", false, ""},
|
||||
{"/2/threads:4", false, ""},
|
||||
{"/2/threads:8", false, ""}});
|
||||
|
||||
void BM_error_after_running(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(state.iterations());
|
||||
}
|
||||
if (state.thread_index <= (state.threads / 2))
|
||||
state.SkipWithError("error message");
|
||||
}
|
||||
BENCHMARK(BM_error_after_running)->ThreadRange(1, 8);
|
||||
ADD_CASES("BM_error_after_running", {{"/threads:1", true, "error message"},
|
||||
{"/threads:2", true, "error message"},
|
||||
{"/threads:4", true, "error message"},
|
||||
{"/threads:8", true, "error message"}});
|
||||
|
||||
void BM_error_while_paused(benchmark::State& state) {
|
||||
bool first_iter = true;
|
||||
while (state.KeepRunning()) {
|
||||
if (state.range(0) == 1 && state.thread_index <= (state.threads / 2)) {
|
||||
assert(first_iter);
|
||||
first_iter = false;
|
||||
state.PauseTiming();
|
||||
state.SkipWithError("error message");
|
||||
} else {
|
||||
state.PauseTiming();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_error_while_paused)->Arg(1)->Arg(2)->ThreadRange(1, 8);
|
||||
ADD_CASES("BM_error_while_paused", {{"/1/threads:1", true, "error message"},
|
||||
{"/1/threads:2", true, "error message"},
|
||||
{"/1/threads:4", true, "error message"},
|
||||
{"/1/threads:8", true, "error message"},
|
||||
{"/2/threads:1", false, ""},
|
||||
{"/2/threads:2", false, ""},
|
||||
{"/2/threads:4", false, ""},
|
||||
{"/2/threads:8", false, ""}});
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
benchmark::Initialize(&argc, argv);
|
||||
|
||||
TestReporter test_reporter;
|
||||
benchmark::RunSpecifiedBenchmarks(&test_reporter);
|
||||
|
||||
typedef benchmark::BenchmarkReporter::Run Run;
|
||||
auto EB = ExpectedResults.begin();
|
||||
|
||||
for (Run const& run : test_reporter.all_runs_) {
|
||||
assert(EB != ExpectedResults.end());
|
||||
EB->CheckRun(run);
|
||||
++EB;
|
||||
}
|
||||
assert(EB == ExpectedResults.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
compare_bench.py - Compare two benchmarks or their results and report the
|
||||
difference.
|
||||
"""
|
||||
import sys
|
||||
import gbench
|
||||
from gbench import util, report
|
||||
|
||||
def main():
|
||||
# Parse the command line flags
|
||||
def usage():
|
||||
print('compare_bench.py <test1> <test2> [benchmark options]...')
|
||||
exit(1)
|
||||
if '--help' in sys.argv or len(sys.argv) < 3:
|
||||
usage()
|
||||
tests = sys.argv[1:3]
|
||||
bench_opts = sys.argv[3:]
|
||||
bench_opts = list(bench_opts)
|
||||
# Run the benchmarks and report the results
|
||||
json1 = gbench.util.run_or_load_benchmark(tests[0], bench_opts)
|
||||
json2 = gbench.util.run_or_load_benchmark(tests[1], bench_opts)
|
||||
output_lines = gbench.report.generate_difference_report(json1, json2)
|
||||
print 'Comparing %s to %s' % (tests[0], tests[1])
|
||||
for ln in output_lines:
|
||||
print(ln)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"context": {
|
||||
"date": "2016-08-02 17:44:46",
|
||||
"num_cpus": 4,
|
||||
"mhz_per_cpu": 4228,
|
||||
"cpu_scaling_enabled": false,
|
||||
"library_build_type": "release"
|
||||
},
|
||||
"benchmarks": [
|
||||
{
|
||||
"name": "BM_SameTimes",
|
||||
"iterations": 1000,
|
||||
"real_time": 10,
|
||||
"cpu_time": 10,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_2xFaster",
|
||||
"iterations": 1000,
|
||||
"real_time": 50,
|
||||
"cpu_time": 50,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_2xSlower",
|
||||
"iterations": 1000,
|
||||
"real_time": 50,
|
||||
"cpu_time": 50,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_10PercentFaster",
|
||||
"iterations": 1000,
|
||||
"real_time": 100,
|
||||
"cpu_time": 100,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_10PercentSlower",
|
||||
"iterations": 1000,
|
||||
"real_time": 100,
|
||||
"cpu_time": 100,
|
||||
"time_unit": "ns"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"context": {
|
||||
"date": "2016-08-02 17:44:46",
|
||||
"num_cpus": 4,
|
||||
"mhz_per_cpu": 4228,
|
||||
"cpu_scaling_enabled": false,
|
||||
"library_build_type": "release"
|
||||
},
|
||||
"benchmarks": [
|
||||
{
|
||||
"name": "BM_SameTimes",
|
||||
"iterations": 1000,
|
||||
"real_time": 10,
|
||||
"cpu_time": 10,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_2xFaster",
|
||||
"iterations": 1000,
|
||||
"real_time": 25,
|
||||
"cpu_time": 25,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_2xSlower",
|
||||
"iterations": 20833333,
|
||||
"real_time": 100,
|
||||
"cpu_time": 100,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_10PercentFaster",
|
||||
"iterations": 1000,
|
||||
"real_time": 90,
|
||||
"cpu_time": 90,
|
||||
"time_unit": "ns"
|
||||
},
|
||||
{
|
||||
"name": "BM_10PercentSlower",
|
||||
"iterations": 1000,
|
||||
"real_time": 110,
|
||||
"cpu_time": 110,
|
||||
"time_unit": "ns"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
"""Google Benchmark tooling"""
|
||||
|
||||
__author__ = 'Eric Fiselier'
|
||||
__email__ = 'eric@efcs.ca'
|
||||
__versioninfo__ = (0, 5, 0)
|
||||
__version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev'
|
||||
|
||||
__all__ = []
|
||||
@@ -1,141 +0,0 @@
|
||||
"""report.py - Utilities for reporting statistics about benchmark results
|
||||
"""
|
||||
import os
|
||||
|
||||
class BenchmarkColor(object):
|
||||
def __init__(self, name, code):
|
||||
self.name = name
|
||||
self.code = code
|
||||
|
||||
def __repr__(self):
|
||||
return '%s%r' % (self.__class__.__name__,
|
||||
(self.name, self.code))
|
||||
|
||||
def __format__(self, format):
|
||||
return self.code
|
||||
|
||||
# Benchmark Colors Enumeration
|
||||
BC_NONE = BenchmarkColor('NONE', '')
|
||||
BC_MAGENTA = BenchmarkColor('MAGENTA', '\033[95m')
|
||||
BC_CYAN = BenchmarkColor('CYAN', '\033[96m')
|
||||
BC_OKBLUE = BenchmarkColor('OKBLUE', '\033[94m')
|
||||
BC_HEADER = BenchmarkColor('HEADER', '\033[92m')
|
||||
BC_WARNING = BenchmarkColor('WARNING', '\033[93m')
|
||||
BC_WHITE = BenchmarkColor('WHITE', '\033[97m')
|
||||
BC_FAIL = BenchmarkColor('FAIL', '\033[91m')
|
||||
BC_ENDC = BenchmarkColor('ENDC', '\033[0m')
|
||||
BC_BOLD = BenchmarkColor('BOLD', '\033[1m')
|
||||
BC_UNDERLINE = BenchmarkColor('UNDERLINE', '\033[4m')
|
||||
|
||||
def color_format(use_color, fmt_str, *args, **kwargs):
|
||||
"""
|
||||
Return the result of 'fmt_str.format(*args, **kwargs)' after transforming
|
||||
'args' and 'kwargs' according to the value of 'use_color'. If 'use_color'
|
||||
is False then all color codes in 'args' and 'kwargs' are replaced with
|
||||
the empty string.
|
||||
"""
|
||||
assert use_color is True or use_color is False
|
||||
if not use_color:
|
||||
args = [arg if not isinstance(arg, BenchmarkColor) else BC_NONE
|
||||
for arg in args]
|
||||
kwargs = {key: arg if not isinstance(arg, BenchmarkColor) else BC_NONE
|
||||
for key, arg in kwargs.items()}
|
||||
return fmt_str.format(*args, **kwargs)
|
||||
|
||||
|
||||
def find_longest_name(benchmark_list):
|
||||
"""
|
||||
Return the length of the longest benchmark name in a given list of
|
||||
benchmark JSON objects
|
||||
"""
|
||||
longest_name = 1
|
||||
for bc in benchmark_list:
|
||||
if len(bc['name']) > longest_name:
|
||||
longest_name = len(bc['name'])
|
||||
return longest_name
|
||||
|
||||
|
||||
def calculate_change(old_val, new_val):
|
||||
"""
|
||||
Return a float representing the decimal change between old_val and new_val.
|
||||
"""
|
||||
if old_val == 0 and new_val == 0:
|
||||
return 0.0
|
||||
if old_val == 0:
|
||||
return float(new_val - old_val) / (float(old_val + new_val) / 2)
|
||||
return float(new_val - old_val) / abs(old_val)
|
||||
|
||||
|
||||
def generate_difference_report(json1, json2, use_color=True):
|
||||
"""
|
||||
Calculate and report the difference between each test of two benchmarks
|
||||
runs specified as 'json1' and 'json2'.
|
||||
"""
|
||||
first_col_width = find_longest_name(json1['benchmarks']) + 5
|
||||
def find_test(name):
|
||||
for b in json2['benchmarks']:
|
||||
if b['name'] == name:
|
||||
return b
|
||||
return None
|
||||
first_line = "{:<{}s} Time CPU Old New".format(
|
||||
'Benchmark', first_col_width)
|
||||
output_strs = [first_line, '-' * len(first_line)]
|
||||
for bn in json1['benchmarks']:
|
||||
other_bench = find_test(bn['name'])
|
||||
if not other_bench:
|
||||
continue
|
||||
|
||||
def get_color(res):
|
||||
if res > 0.05:
|
||||
return BC_FAIL
|
||||
elif res > -0.07:
|
||||
return BC_WHITE
|
||||
else:
|
||||
return BC_CYAN
|
||||
fmt_str = "{}{:<{}s}{endc} {}{:+.2f}{endc} {}{:+.2f}{endc} {:4d} {:4d}"
|
||||
tres = calculate_change(bn['real_time'], other_bench['real_time'])
|
||||
cpures = calculate_change(bn['cpu_time'], other_bench['cpu_time'])
|
||||
output_strs += [color_format(use_color, fmt_str,
|
||||
BC_HEADER, bn['name'], first_col_width,
|
||||
get_color(tres), tres, get_color(cpures), cpures,
|
||||
bn['cpu_time'], other_bench['cpu_time'],
|
||||
endc=BC_ENDC)]
|
||||
return output_strs
|
||||
|
||||
###############################################################################
|
||||
# Unit tests
|
||||
|
||||
import unittest
|
||||
|
||||
class TestReportDifference(unittest.TestCase):
|
||||
def load_results(self):
|
||||
import json
|
||||
testInputs = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'Inputs')
|
||||
testOutput1 = os.path.join(testInputs, 'test1_run1.json')
|
||||
testOutput2 = os.path.join(testInputs, 'test1_run2.json')
|
||||
with open(testOutput1, 'r') as f:
|
||||
json1 = json.load(f)
|
||||
with open(testOutput2, 'r') as f:
|
||||
json2 = json.load(f)
|
||||
return json1, json2
|
||||
|
||||
def test_basic(self):
|
||||
expect_lines = [
|
||||
['BM_SameTimes', '+0.00', '+0.00'],
|
||||
['BM_2xFaster', '-0.50', '-0.50'],
|
||||
['BM_2xSlower', '+1.00', '+1.00'],
|
||||
['BM_10PercentFaster', '-0.10', '-0.10'],
|
||||
['BM_10PercentSlower', '+0.10', '+0.10']
|
||||
]
|
||||
json1, json2 = self.load_results()
|
||||
output_lines = generate_difference_report(json1, json2, use_color=False)
|
||||
print output_lines
|
||||
self.assertEqual(len(output_lines), len(expect_lines))
|
||||
for i in xrange(0, len(output_lines)):
|
||||
parts = [x for x in output_lines[i].split(' ') if x]
|
||||
self.assertEqual(len(parts), 3)
|
||||
self.assertEqual(parts, expect_lines[i])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,130 +0,0 @@
|
||||
"""util.py - General utilities for running, loading, and processing benchmarks
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Input file type enumeration
|
||||
IT_Invalid = 0
|
||||
IT_JSON = 1
|
||||
IT_Executable = 2
|
||||
|
||||
_num_magic_bytes = 2 if sys.platform.startswith('win') else 4
|
||||
def is_executable_file(filename):
|
||||
"""
|
||||
Return 'True' if 'filename' names a valid file which is likely
|
||||
an executable. A file is considered an executable if it starts with the
|
||||
magic bytes for a EXE, Mach O, or ELF file.
|
||||
"""
|
||||
if not os.path.isfile(filename):
|
||||
return False
|
||||
with open(filename, 'r') as f:
|
||||
magic_bytes = f.read(_num_magic_bytes)
|
||||
if sys.platform == 'darwin':
|
||||
return magic_bytes in [
|
||||
'\xfe\xed\xfa\xce', # MH_MAGIC
|
||||
'\xce\xfa\xed\xfe', # MH_CIGAM
|
||||
'\xfe\xed\xfa\xcf', # MH_MAGIC_64
|
||||
'\xcf\xfa\xed\xfe', # MH_CIGAM_64
|
||||
'\xca\xfe\xba\xbe', # FAT_MAGIC
|
||||
'\xbe\xba\xfe\xca' # FAT_CIGAM
|
||||
]
|
||||
elif sys.platform.startswith('win'):
|
||||
return magic_bytes == 'MZ'
|
||||
else:
|
||||
return magic_bytes == '\x7FELF'
|
||||
|
||||
|
||||
def is_json_file(filename):
|
||||
"""
|
||||
Returns 'True' if 'filename' names a valid JSON output file.
|
||||
'False' otherwise.
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
json.load(f)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def classify_input_file(filename):
|
||||
"""
|
||||
Return a tuple (type, msg) where 'type' specifies the classified type
|
||||
of 'filename'. If 'type' is 'IT_Invalid' then 'msg' is a human readable
|
||||
string represeting the error.
|
||||
"""
|
||||
ftype = IT_Invalid
|
||||
err_msg = None
|
||||
if not os.path.exists(filename):
|
||||
err_msg = "'%s' does not exist" % filename
|
||||
elif not os.path.isfile(filename):
|
||||
err_msg = "'%s' does not name a file" % filename
|
||||
elif is_executable_file(filename):
|
||||
ftype = IT_Executable
|
||||
elif is_json_file(filename):
|
||||
ftype = IT_JSON
|
||||
else:
|
||||
err_msg = "'%s' does not name a valid benchmark executable or JSON file"
|
||||
return ftype, err_msg
|
||||
|
||||
|
||||
def check_input_file(filename):
|
||||
"""
|
||||
Classify the file named by 'filename' and return the classification.
|
||||
If the file is classified as 'IT_Invalid' print an error message and exit
|
||||
the program.
|
||||
"""
|
||||
ftype, msg = classify_input_file(filename)
|
||||
if ftype == IT_Invalid:
|
||||
print "Invalid input file: %s" % msg
|
||||
sys.exit(1)
|
||||
return ftype
|
||||
|
||||
|
||||
def load_benchmark_results(fname):
|
||||
"""
|
||||
Read benchmark output from a file and return the JSON object.
|
||||
REQUIRES: 'fname' names a file containing JSON benchmark output.
|
||||
"""
|
||||
with open(fname, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def run_benchmark(exe_name, benchmark_flags):
|
||||
"""
|
||||
Run a benchmark specified by 'exe_name' with the specified
|
||||
'benchmark_flags'. The benchmark is run directly as a subprocess to preserve
|
||||
real time console output.
|
||||
RETURNS: A JSON object representing the benchmark output
|
||||
"""
|
||||
thandle, tname = tempfile.mkstemp()
|
||||
os.close(thandle)
|
||||
cmd = [exe_name] + benchmark_flags
|
||||
print("RUNNING: %s" % ' '.join(cmd))
|
||||
exitCode = subprocess.call(cmd + ['--benchmark_out=%s' % tname])
|
||||
if exitCode != 0:
|
||||
print('TEST FAILED...')
|
||||
sys.exit(exitCode)
|
||||
json_res = load_benchmark_results(tname)
|
||||
os.unlink(tname)
|
||||
return json_res
|
||||
|
||||
|
||||
def run_or_load_benchmark(filename, benchmark_flags):
|
||||
"""
|
||||
Get the results for a specified benchmark. If 'filename' specifies
|
||||
an executable benchmark then the results are generated by running the
|
||||
benchmark. Otherwise 'filename' must name a valid JSON output file,
|
||||
which is loaded and the result returned.
|
||||
"""
|
||||
ftype = check_input_file(filename)
|
||||
if ftype == IT_JSON:
|
||||
return load_benchmark_results(filename)
|
||||
elif ftype == IT_Executable:
|
||||
return run_benchmark(filename, benchmark_flags)
|
||||
else:
|
||||
assert False # This branch is unreachable
|
||||
24
build.jam
Normal file
24
build.jam
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright René Ferdinand Rivera Morell 2025
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
require-b2 5.2 ;
|
||||
|
||||
constant boost_dependencies :
|
||||
/boost/assert//boost_assert
|
||||
/boost/charconv//boost_charconv
|
||||
/boost/hana//boost_hana
|
||||
/boost/type_index//boost_type_index ;
|
||||
|
||||
project /boost/parser
|
||||
;
|
||||
|
||||
explicit
|
||||
[ alias boost_parser : : :
|
||||
: <library>$(boost_dependencies) <include>include ]
|
||||
[ alias all : boost_parser test ]
|
||||
;
|
||||
|
||||
call-if : boost-library parser
|
||||
;
|
||||
@@ -1,23 +1,90 @@
|
||||
# Copyright Louis Dionne 2016
|
||||
# Copyright Zach Laine 2016
|
||||
# Copyright Zach Laine 2024
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
###############################################################################
|
||||
# Boost
|
||||
###############################################################################
|
||||
find_package(Boost)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
|
||||
###############################################################################
|
||||
# GoogleTest
|
||||
###############################################################################
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/googletest-release-1.10.0)
|
||||
target_include_directories(gtest INTERFACE ${CMAKE_HOME_DIRECTORY}/googletest-release-1.10.0/googletest/include)
|
||||
target_include_directories(gtest_main INTERFACE ${CMAKE_HOME_DIRECTORY}/googletest-release-1.10.0/googletest/include)
|
||||
if (NOT BOOST_BRANCH)
|
||||
set(BOOST_BRANCH master)
|
||||
endif()
|
||||
|
||||
add_custom_target(boost_clone_superproject
|
||||
DEPENDS
|
||||
${CMAKE_BINARY_DIR}/boost_root/LICENSE_1_0.txt
|
||||
COMMENT
|
||||
"Cloning Boost superproject."
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/boost_root/LICENSE_1_0.txt
|
||||
COMMAND git clone --depth 100 -b ${BOOST_BRANCH}
|
||||
https://github.com/boostorg/boost.git boost_root
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# MSVC determines the compiler and not the target platform
|
||||
# here we need to check WIN32
|
||||
if (WIN32)
|
||||
set(b2_exe b2.exe)
|
||||
else()
|
||||
set(b2_exe b2)
|
||||
endif()
|
||||
|
||||
add_custom_target(boost_clone_deps
|
||||
DEPENDS
|
||||
${CMAKE_BINARY_DIR}/boost_root/${b2_exe}
|
||||
COMMENT
|
||||
"Cloning Boost dependencies."
|
||||
VERBATIM)
|
||||
add_dependencies(boost_clone_deps boost_clone_superproject)
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Google Benchmark
|
||||
###############################################################################
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/benchmark-v1.1.0)
|
||||
target_include_directories(benchmark INTERFACE ${CMAKE_HOME_DIRECTORY}/benchmark-v1.1.0/include)
|
||||
if (WIN32)
|
||||
# Windows:
|
||||
# in order to build 'b2_exe' with the same toolset as configured in the current cmake run,
|
||||
# we need to tell the boost bootstrap process and pass some parameters
|
||||
# - as first parameter msvc, gcc or clang for the compiler type
|
||||
# - additionally, for non-MSVC we need to set the environment variable CXX to the compiler: ${CMAKE_CXX_COMPILER} which
|
||||
# is a fully qualified path name, so that the compiler can be found by the bootstrap_cmd
|
||||
# calling b2.exe should work also for non-MSVC compilers, as during the build, the runtime directory is part of the search PATH
|
||||
|
||||
if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
||||
message(STATUS "configure BOOST for Visual Studio built-in compilers (i.e cl, clang-cl and clang")
|
||||
set(bootstrap_cmd ./bootstrap.bat msvc)
|
||||
# here we do not need to distinguish the different compilers as only the frontend is different
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
message(STATUS "configure BOOST for Clang compiler")
|
||||
set(bootstrap_cmd ./bootstrap.bat clang)
|
||||
set(COMMAND_ENV set CXX=${CMAKE_CXX_COMPILER})
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
message(STATUS "configure BOOST for GCC compiler")
|
||||
set(bootstrap_cmd ./bootstrap.bat gcc)
|
||||
set(COMMAND_ENV set CXX=${CMAKE_CXX_COMPILER})
|
||||
endif ()
|
||||
else()
|
||||
# windres produces relocations that are rejected
|
||||
# by stricter ld configurations used in some distros
|
||||
set(bootstrap_cmd env B2_DONT_EMBED_MANIFEST=true ./bootstrap.sh)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/boost_root/${b2_exe}
|
||||
COMMAND git submodule init libs/assert
|
||||
COMMAND git submodule init libs/config
|
||||
COMMAND git submodule init libs/core
|
||||
COMMAND git submodule init libs/hana
|
||||
COMMAND git submodule init tools/build
|
||||
COMMAND git submodule init libs/headers
|
||||
COMMAND git submodule init tools/boost_install
|
||||
COMMAND git submodule update --jobs 3 --depth 100
|
||||
COMMAND ${COMMAND_ENV}
|
||||
COMMAND ${bootstrap_cmd}
|
||||
COMMAND ./b2 headers
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/boost_root)
|
||||
|
||||
add_library(boost INTERFACE)
|
||||
add_dependencies(boost boost_clone_deps)
|
||||
target_include_directories(boost INTERFACE ${CMAKE_BINARY_DIR}/boost_root)
|
||||
|
||||
@@ -6,24 +6,26 @@ import path ;
|
||||
import doxygen ;
|
||||
import quickbook ;
|
||||
|
||||
using auto-index ;
|
||||
using doxygen ;
|
||||
using quickbook ;
|
||||
using boostbook ;
|
||||
|
||||
path-constant here : . ;
|
||||
|
||||
rule run_doxygen ( files * : name : expand ? )
|
||||
{
|
||||
|
||||
expand ?= <doxygen:param>EXPAND_ONLY_PREDEF=YES ;
|
||||
|
||||
doxygen parser_reference
|
||||
:
|
||||
$(files)
|
||||
:
|
||||
doxygen parser_reference
|
||||
:
|
||||
$(files)
|
||||
:
|
||||
<doxygen:param>EXTRACT_ALL=YES
|
||||
# note that there is no detail::unspecified -- this is a hack to get all
|
||||
# the SFINAE code out of the API docs.
|
||||
<doxygen:param>"PREDEFINED=\"BOOST_PARSER_DOXYGEN=1\" \\
|
||||
\"BOOST_PARSER_USE_CONCEPTS=1\" \\
|
||||
\"enable_if=detail::unspecified\""
|
||||
<doxygen:param>HIDE_UNDOC_MEMBERS=NO
|
||||
<doxygen:param>EXTRACT_PRIVATE=NO
|
||||
@@ -33,15 +35,23 @@ doxygen parser_reference
|
||||
<doxygen:param>SEARCH_INCLUDES=NO
|
||||
<doxygen:param>EXAMPLE_PATH=.
|
||||
<reftitle>$(name)
|
||||
;
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
run_doxygen [ glob ../include/boost/parser/*.hpp ] : "Reference" ;
|
||||
run_doxygen [ glob $(here)/../include/boost/parser/*.hpp : $(here)/../include/boost/parser/concepts.hpp ] : "Headers" ;
|
||||
|
||||
install images_standalone : [ glob *.png ] : <location>html/parser/img ;
|
||||
explicit images_standalone ;
|
||||
|
||||
install images_boostdoc : [ glob *.png ] : <location>../../../doc/html/parser/img ;
|
||||
explicit images_boostdoc ;
|
||||
|
||||
xml parser
|
||||
:
|
||||
parser.qbk
|
||||
:
|
||||
<dependency>parser_reference
|
||||
;
|
||||
|
||||
boostbook standalone
|
||||
@@ -51,8 +61,6 @@ boostbook standalone
|
||||
<dependency>css
|
||||
<dependency>images
|
||||
|
||||
<dependency>parser_reference
|
||||
|
||||
# HTML options first:
|
||||
# Use graphics not text for navigation:
|
||||
<xsl:param>navig.graphics=1
|
||||
@@ -90,18 +98,25 @@ boostbook standalone
|
||||
# better use SVG's instead:
|
||||
# <xsl:param>admon.graphics.extension=".svg"
|
||||
|
||||
<auto-index>on
|
||||
<auto-index-verbose>on
|
||||
<auto-index-internal>on
|
||||
<auto-index-script>parser.idx
|
||||
<quickbook-define>enable_index
|
||||
<auto-index-prefix>..
|
||||
<xsl:param>index.on.type=1
|
||||
# <auto-index>on
|
||||
# <auto-index-verbose>on
|
||||
# <auto-index-internal>on
|
||||
# <auto-index-script>parser.idx
|
||||
# <quickbook-define>enable_index
|
||||
# <auto-index-prefix>..
|
||||
# <xsl:param>index.on.type=1
|
||||
|
||||
<dependency>images_standalone
|
||||
;
|
||||
|
||||
install css : [ glob $(BOOST_ROOT)/doc/src/*.css ]
|
||||
: <location>html ;
|
||||
install images : [ glob $(BOOST_ROOT)/doc/src/images/*.png ]
|
||||
: <location>html/images ;
|
||||
|
||||
alias boostdoc : parser : : : <dependency>images_boostdoc ;
|
||||
explicit boostdoc ;
|
||||
alias boostrelease ;
|
||||
explicit boostrelease ;
|
||||
explicit css ;
|
||||
explicit images ;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2006 João Abecasis
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0. (See
|
||||
# accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
|
||||
##
|
||||
## IMPORTANT NOTE: This file MUST NOT be copied over a boost installation
|
||||
##
|
||||
|
||||
path-constant top : . ;
|
||||
|
||||
import modules ;
|
||||
import path ;
|
||||
|
||||
local boost-root = [ modules.peek : BOOST_ROOT ] ;
|
||||
|
||||
if ! $(boost-root)
|
||||
{
|
||||
local boost-search-dirs = [ modules.peek : BOOST_BUILD_PATH ] ;
|
||||
|
||||
for local dir in $(boost-search-dirs)
|
||||
{
|
||||
if [ path.glob $(dir)/../../../ : boost/version.hpp ]
|
||||
{
|
||||
boost-root += $(dir)/../../../ ;
|
||||
}
|
||||
}
|
||||
|
||||
if $(boost-root)
|
||||
{
|
||||
boost-root = [ path.make $(boost-root[1]) ] ;
|
||||
}
|
||||
else
|
||||
{
|
||||
ECHO "Warning: couldn't find BOOST_ROOT in" $(boost-root) ;
|
||||
}
|
||||
}
|
||||
|
||||
path-constant BOOST_ROOT : $(boost-root) ;
|
||||
modules.poke : QUICKBOOK_ROOT : $(top) ;
|
||||
|
||||
use-project /boost : $(BOOST_ROOT) ;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user