init repo.
Some checks failed
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Release) (push) Waiting to run
linux-arm-gcc / linux-gcc-arm (Debug) (push) Waiting to run
linux-arm-gcc / linux-gcc-arm (Release) (push) Waiting to run
linux-arm-gcc / linux-gcc-armhf (Debug) (push) Waiting to run
linux-arm-gcc / linux-gcc-armhf (Release) (push) Waiting to run
linux-mips-gcc / linux-gcc-mipsel (Debug) (push) Waiting to run
linux-mips-gcc / linux-gcc-mipsel (Release) (push) Waiting to run
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Waiting to run
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Waiting to run
linux-riscv64-gcc / linux-gcc-riscv64 (Debug) (push) Waiting to run
linux-riscv64-gcc / linux-gcc-riscv64 (Release) (push) Waiting to run
linux-x64-clang / linux-clang (Debug) (push) Waiting to run
linux-x64-clang / linux-clang (Release) (push) Waiting to run
linux-x64-gcc / linux-gcc (Debug) (push) Waiting to run
linux-x64-gcc / linux-gcc (Release) (push) Waiting to run
linux-x86-gcc / linux-gcc (Debug) (push) Waiting to run
linux-x86-gcc / linux-gcc (Release) (push) Waiting to run
android / build (push) Failing after 3m51s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Debug) (push) Has been cancelled
Some checks failed
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Release) (push) Waiting to run
linux-arm-gcc / linux-gcc-arm (Debug) (push) Waiting to run
linux-arm-gcc / linux-gcc-arm (Release) (push) Waiting to run
linux-arm-gcc / linux-gcc-armhf (Debug) (push) Waiting to run
linux-arm-gcc / linux-gcc-armhf (Release) (push) Waiting to run
linux-mips-gcc / linux-gcc-mipsel (Debug) (push) Waiting to run
linux-mips-gcc / linux-gcc-mipsel (Release) (push) Waiting to run
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Waiting to run
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Waiting to run
linux-riscv64-gcc / linux-gcc-riscv64 (Debug) (push) Waiting to run
linux-riscv64-gcc / linux-gcc-riscv64 (Release) (push) Waiting to run
linux-x64-clang / linux-clang (Debug) (push) Waiting to run
linux-x64-clang / linux-clang (Release) (push) Waiting to run
linux-x64-gcc / linux-gcc (Debug) (push) Waiting to run
linux-x64-gcc / linux-gcc (Release) (push) Waiting to run
linux-x86-gcc / linux-gcc (Debug) (push) Waiting to run
linux-x86-gcc / linux-gcc (Release) (push) Waiting to run
android / build (push) Failing after 3m51s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Debug) (push) Has been cancelled
This commit is contained in:
commit
3efba12575
78
.clang-format
Normal file
78
.clang-format
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Generated from CLion C/C++ Code Style settings
|
||||||
|
BinPackParameters: false
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignArrayOfStructures: Left
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: true
|
||||||
|
AcrossComments: false
|
||||||
|
|
||||||
|
AlignOperands: DontAlign
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
# 函数和返回类型分两行,方便阅读
|
||||||
|
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
ColumnLimit: 120
|
||||||
|
CompactNamespaces: false
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
SeparateDefinitionBlocks: Always
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 4
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: false
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: CaseSensitive
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 0
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
PenaltyIndentedWhitespace: 1
|
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
72
.gitea/workflows/android.yml
Normal file
72
.gitea/workflows/android.yml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
name: android
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/android.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
- "tile/**"
|
||||||
|
- "third_party/**"
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/android.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
- "tile/**"
|
||||||
|
- "third_party/**"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: android-${{github.ref}}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
TILE_CMAKE_OPTIONS: |
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake \
|
||||||
|
-DANDROID_PLATFORM=android-21 \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=install \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DNCNN_VULKAN=ON \
|
||||||
|
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
apt-get update -y
|
||||||
|
apt-get install -y ninja-build
|
||||||
|
|
||||||
|
- name: armeabi-v7a
|
||||||
|
run: |
|
||||||
|
mkdir build-armeabi-v7a && cd build-armeabi-v7a
|
||||||
|
cmake -GNinja .. ${{ env.TILE_CMAKE_OPTIONS}} -DCMAKE_ABI="armeabi-v7a" -DANDROID_ARM_NEON=ON
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
|
||||||
|
|
||||||
|
- name: arm64-v8a
|
||||||
|
run: |
|
||||||
|
mkdir build-arm64-v8a && cd build-arm64-v8a
|
||||||
|
cmake -GNinja .. ${{ env.TILE_CMAKE_OPTIONS}} -DCMAKE_ABI="arm64-v8a"
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
|
||||||
|
- name: x86
|
||||||
|
run: |
|
||||||
|
mkdir build-x86 && cd build-x86
|
||||||
|
cmake -GNinja .. ${{ env.TILE_CMAKE_OPTIONS}} -DCMAKE_ABI="x86"
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
|
||||||
|
- name: x86_64
|
||||||
|
run: |
|
||||||
|
mkdir build-x86_64 && cd build-x86_64
|
||||||
|
cmake -GNinja .. ${{ env.TILE_CMAKE_OPTIONS}} -DCMAKE_ABI="x86_64"
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
58
.gitea/workflows/linux-aarch64-gcc.yml
Normal file
58
.gitea/workflows/linux-aarch64-gcc.yml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
name: linux-aarch64-cpu-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-aarch64-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "toolchains/aarch64-linux-gnu.toolchain.cmake"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_aarch64.*"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
- "cmake/**"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-aarch64-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "toolchains/aarch64-linux-gnu.toolchain.cmake"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_aarch64.*"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
- "cmake/**"
|
||||||
|
concurrency:
|
||||||
|
group: linux-aarch64-cpu-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
linux-gcc-aarch64:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["Debug", "Release"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y g++-aarch64-linux-gnu qemu-user-binfmt ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_TESTS=ON -DTILE_BUILD_BENCHMARKS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |-
|
||||||
|
cd build
|
||||||
|
sudo ln -sf /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 /lib/ld-linux-aarch64.so.1
|
||||||
|
export LD_LIBRARY_PATH=/usr/aarch64-linux-gnu/lib
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
84
.gitea/workflows/linux-arm-gcc.yml
Normal file
84
.gitea/workflows/linux-arm-gcc.yml
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
name: linux-arm-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-arm-gcc.yml
|
||||||
|
- "cmake/**"
|
||||||
|
- toolchains/arm-linux-gnueabi.toolchain.cmake
|
||||||
|
- toolchains/arm-linux-gnueabihf.toolchain.cmake
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_arm.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-arm-gcc.yml
|
||||||
|
- "cmake/**"
|
||||||
|
- toolchains/arm-linux-gnueabi.toolchain.cmake
|
||||||
|
- toolchains/arm-linux-gnueabihf.toolchain.cmake
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_arm.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
concurrency:
|
||||||
|
group: linux-arm-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
jobs:
|
||||||
|
linux-gcc-arm:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["Debug", "Release"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y g++-arm-linux-gnueabi qemu-user-binfmt ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/arm-linux-gnueabi.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_TESTS=ON -DTILE_BUILD_TESTS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
sudo ln -sf /usr/arm-linux-gnueabi/lib/ld-linux.so.3 /lib/ld-linux.so.3
|
||||||
|
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabi/lib
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
||||||
|
|
||||||
|
linux-gcc-armhf:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["Debug", "Release"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: arm-gnu-toolchain
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y g++-arm-linux-gnueabihf qemu-user-binfmt ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/arm-linux-gnueabihf.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_TESTS=ON -DTILE_BUILD_BENCHMARKS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake --build . -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |-
|
||||||
|
cd build
|
||||||
|
sudo ln -sf /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 /lib/ld-linux-armhf.so.3
|
||||||
|
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib/
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
53
.gitea/workflows/linux-mips-gcc.yml
Normal file
53
.gitea/workflows/linux-mips-gcc.yml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
name: linux-mips-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-mips-gcc.yml
|
||||||
|
- "toolchains/mips-linux-gnu.toolchain.cmake"
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_mips32.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-mips-gcc.yml
|
||||||
|
- "toolchains/mips-linux-gnu.toolchain.cmake"
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_mips32.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
concurrency:
|
||||||
|
group: linux-mips-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
permissions: read-all
|
||||||
|
jobs:
|
||||||
|
linux-gcc-mipsel:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: [Debug, Release]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y g++-mipsel-linux-gnu qemu-user-binfmt ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/mips-linux-gnu.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_TESTS=ON -DTILE_BUILD_BENCHMARKS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: cmake --build build --target all -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |-
|
||||||
|
cd build
|
||||||
|
sudo ln -sf /usr/mipsel-linux-gnu/lib/ld.so.1 /lib/ld.so.1
|
||||||
|
export LD_LIBRARY_PATH=/usr/mipsel-linux-gnu/lib/
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
54
.gitea/workflows/linux-mips64-gcc.yml
Normal file
54
.gitea/workflows/linux-mips64-gcc.yml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: linux-mips64-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-mips64-gcc.yml
|
||||||
|
- toolchains/mips64el-linux-gnuabi64.toolchain.cmake
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_mips64.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-mips64-gcc.yml
|
||||||
|
- "cmake/**"
|
||||||
|
- toolchains/mips64el-linux-gnuabi64.toolchain.cmake
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_mips64.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
concurrency:
|
||||||
|
group: linux-mips64-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
permissions: read-all
|
||||||
|
jobs:
|
||||||
|
linux-gcc-mips64el:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: [Debug, Release]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y g++-mips64el-linux-gnuabi64 qemu-user-binfmt ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/mips64el-linux-gnuabi64.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_TESTS=ON -DTILE_BUILD_BENCHMARKS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: cmake --build build --target all -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |-
|
||||||
|
cd build
|
||||||
|
sudo ln -sf /usr/mips64el-linux-gnuabi64/lib64/ld.so.1 /lib64/ld.so.1
|
||||||
|
export LD_LIBRARY_PATH=/usr/mips64el-linux-gnuabi64/lib
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
55
.gitea/workflows/linux-riscv64-gcc.yml
Normal file
55
.gitea/workflows/linux-riscv64-gcc.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
name: linux-riscv64-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-riscv64-gcc.yml
|
||||||
|
- "cmake/**"
|
||||||
|
- toolchains/riscv64-linux-gnu.toolchain.cmake
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_riscv64.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .gitea/workflows/linux-riscv64-gcc.yml
|
||||||
|
- "cmake/**"
|
||||||
|
- toolchains/riscv64-linux-gnu.toolchain.cmake
|
||||||
|
- third_party/**
|
||||||
|
- tile/**
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_riscv64.*"
|
||||||
|
- CMakeLists.txt
|
||||||
|
- cmake/**
|
||||||
|
concurrency:
|
||||||
|
group: linux-riscv64-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
permissions: read-all
|
||||||
|
jobs:
|
||||||
|
linux-gcc-riscv64:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: [Debug, Release]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y g++-riscv64-linux-gnu qemu-user-binfmt ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/riscv64-linux-gnu.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_TESTS=ON -DTILE_BUILD_BENCHMARKS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: cmake --build build --target all -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |-
|
||||||
|
cd build
|
||||||
|
sudo ln -sf /usr/riscv64-linux-gnu/lib/ld-linux-riscv64-lp64d.so.1 /lib/ld-linux-riscv64-lp64d.so.1
|
||||||
|
export LD_LIBRARY_PATH=/usr/riscv64-linux-gnu/lib
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
52
.gitea/workflows/linux-x64-clang.yml
Normal file
52
.gitea/workflows/linux-x64-clang.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: linux-x64-clang
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-x64-clang.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-x64-clang.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: linux-x64-clang-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux-clang:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["Debug", "Release"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y ninja-build
|
||||||
|
- name: configure
|
||||||
|
env:
|
||||||
|
CC: clang
|
||||||
|
CXX: clang++
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cmake --build build -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest --output-on-failure
|
||||||
|
# - name: benchmark
|
||||||
|
# run: |
|
||||||
|
# ./build/sled_benchmark
|
53
.gitea/workflows/linux-x64-gcc.yml
Normal file
53
.gitea/workflows/linux-x64-gcc.yml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
name: linux-x64-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-x64-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_x64.*"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-x64-gcc.yml"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_x64.*"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: linux-x64-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux-gcc:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["Debug", "Release"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cmake --build build -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
||||||
|
- name: benchmark
|
||||||
|
run: |
|
||||||
|
./build/bin/tile_bm_all
|
55
.gitea/workflows/linux-x86-gcc.yml
Normal file
55
.gitea/workflows/linux-x86-gcc.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
name: linux-x86-gcc
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-x86-gcc.yml"
|
||||||
|
- "toolchains/host.gcc-m32.toolchain.cmake"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_x86.*"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".gitea/workflows/linux-x86-gcc.yml"
|
||||||
|
- "toolchains/host.gcc-m32.toolchain.cmake"
|
||||||
|
- "cmake/**"
|
||||||
|
- "third_party/**"
|
||||||
|
- "tile/**"
|
||||||
|
- "!tile/fiber/detail/asm/*"
|
||||||
|
- "tile/fiber/detail/asm/ucontext_x86.*"
|
||||||
|
- "CMakeLists.txt"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: linux-x86-gcc-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux-gcc:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["Debug", "Release"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: install-tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y gcc-multilib g++-multilib ninja-build
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=../toolchains/host.gcc-m32.toolchain.cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DTILE_BUILD_BENCHMARKS=ON -DTILE_BUILD_TESTS=ON ..
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cmake --build build -j $(nproc)
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest --output-on-failure -j $(nproc)
|
||||||
|
- name: benchmark
|
||||||
|
run: |
|
||||||
|
./build/bin/tile_bm_all
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
out/
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
compile_commands.json
|
||||||
|
.gdb_history
|
||||||
|
.cmake.conf
|
367
CMakeLists.txt
Normal file
367
CMakeLists.txt
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
set(tile_VERSION_MAJOR 0)
|
||||||
|
set(tile_VERSION_MINOR 1)
|
||||||
|
set(tile_VERSION_PATCH 1)
|
||||||
|
set(tile_VERSION_BUILD $ENV{CI_STEP_NUMBER})
|
||||||
|
if(NOT tile_VERSION_BUILD)
|
||||||
|
set(tile_VERSION_BUILD "0")
|
||||||
|
endif()
|
||||||
|
set(TILE_VERSION
|
||||||
|
"${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}.${tile_VERSION_BUILD}"
|
||||||
|
)
|
||||||
|
project(
|
||||||
|
tile
|
||||||
|
VERSION "${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}"
|
||||||
|
LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
# add static libgcc and libstdc++ for static linking set(CMAKE_C_FLAGS
|
||||||
|
# "${CMAKE_C_FLAGS} -static-libgcc -static-libstdc++") set(CMAKE_CXX_FLAGS
|
||||||
|
# "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
|
||||||
|
|
||||||
|
option(TILE_BUILD_TESTS "Build tests" OFF)
|
||||||
|
option(TILE_BUILD_BENCHMARKS "Build tests" OFF)
|
||||||
|
option(TILE_WITH_OPENSSL "Build wih openssl" OFF)
|
||||||
|
option(TILE_BUILD_SHARED "Build shared library" ON)
|
||||||
|
option(TILE_WITH_MIMALLOC "Build with mimalloc" OFF)
|
||||||
|
|
||||||
|
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
set(TILE_BUILD_TESTS ON)
|
||||||
|
set(TILE_BUILD_BENCHMARKS ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "Release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # set(CMAKE_CXX_FLAGS
|
||||||
|
# "${CMAKE_CXX_FLAGS} -gz") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") #
|
||||||
|
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS #
|
||||||
|
# "${CMAKE_CXX_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} #
|
||||||
|
# -fsanitize=address ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address #
|
||||||
|
# ")
|
||||||
|
#
|
||||||
|
# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load") # set(NO_WHOLE_ARCHIVE_PREFIX "")
|
||||||
|
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # set(CMAKE_CXX_FLAGS
|
||||||
|
# "${CMAKE_CXX_FLAGS} -gz") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") #
|
||||||
|
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS #
|
||||||
|
# "${CMAKE_CXX_FLAGS} -static")
|
||||||
|
#
|
||||||
|
# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,") # set(NO_WHOLE_ARCHIVE_PREFIX "")
|
||||||
|
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS
|
||||||
|
# "${CMAKE_CXX_FLAGS} -gz") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") #
|
||||||
|
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") #
|
||||||
|
# set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
|
||||||
|
#
|
||||||
|
# set(STATIC_BINARY_FLAGS "-static-libgcc -static-libstdc++")
|
||||||
|
# set(WHOLE_ARCHIVE_PREFIX "-Wl,--whole-archive") set(WHOLE_ARCHIVE_SUFFIX
|
||||||
|
# "-Wl,--no-whole-archive") endif()
|
||||||
|
|
||||||
|
# extern int getifaddrs(struct ifaddrs **ifap); extern void freeifaddrs(struct
|
||||||
|
# ifaddrs *ifa);
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
include(cmake/BuildInfo.cmake)
|
||||||
|
|
||||||
|
check_symbol_exists("getifaddrs" "ifaddrs.h" TILE_HAVE_GETIFADDRS)
|
||||||
|
check_symbol_exists("freeifaddrs" "ifaddrs.h" TILE_HAVE_FREEIFADDRS)
|
||||||
|
|
||||||
|
# find_package(Git REQUIRED)
|
||||||
|
#
|
||||||
|
# get_git_commit_hash(GIT_COMMIT_HASH) get_git_commit_date(GIT_COMMIT_DATE)
|
||||||
|
# get_git_commit_subject(GIT_COMMIT_SUBJECT)
|
||||||
|
|
||||||
|
set(THIRD_PARTY_INCLUDE_DIRS # "third_party/json" "third_party/inja"
|
||||||
|
"third_party/sigslot"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
|
||||||
|
include_directories(${THIRD_PARTY_INCLUDE_DIRS})
|
||||||
|
add_subdirectory("third_party/zlib")
|
||||||
|
if(TILE_WITH_MIMALLOC)
|
||||||
|
add_subdirectory("third_party/mimalloc")
|
||||||
|
endif()
|
||||||
|
add_subdirectory("third_party/jsoncpp")
|
||||||
|
add_subdirectory("third_party/fmt")
|
||||||
|
add_subdirectory("third_party/googletest")
|
||||||
|
add_subdirectory("third_party/gflags")
|
||||||
|
set(GFLAGS_USE_TARGET_NAMESPACE ON)
|
||||||
|
set(gflags_DIR "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags")
|
||||||
|
# add_subdirectory("third_party/context")
|
||||||
|
|
||||||
|
set(CURL_DISABLE_TESTS ON)
|
||||||
|
set(CURL_CA_PATH
|
||||||
|
"none"
|
||||||
|
CACHE STRING "" FORCE)
|
||||||
|
set(CURL_ENABLE_SSL
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(USE_LIBIDN2
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_USE_LIBPSL
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_USE_LIBSSH
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_USE_LIBSSH2
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_USE_GSSAPI
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_USE_RTMP
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(USE_OPENSSL_QUIC
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(USE_MSH3
|
||||||
|
OFF
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_DISABLE_LDAP
|
||||||
|
ON
|
||||||
|
CACHE BOOL "" FORCE)
|
||||||
|
set(ZLIB_FOUND ON)
|
||||||
|
set(ZLIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib")
|
||||||
|
set(ZLIB_LIBRARIES zlib)
|
||||||
|
add_subdirectory("third_party/curl")
|
||||||
|
# add_subdirectory("third_party/date")
|
||||||
|
if(TILE_WITH_OPENSSL)
|
||||||
|
add_subdirectory("third_party/openssl-cmake")
|
||||||
|
list(APPEND TILE_LINK_LIBS ssl crypto)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file("tile/base/config.h.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/tile/base/config.h" @ONLY)
|
||||||
|
# file(GLOB zlib_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib/zlib/*.c")
|
||||||
|
set(TILE_SRCS
|
||||||
|
"tile/base/buffer/builtin_buffer_block.cc"
|
||||||
|
"tile/base/buffer/compression_output_stream.cc"
|
||||||
|
"tile/base/buffer/polymorphic_buffer.cc"
|
||||||
|
"tile/base/buffer.cc"
|
||||||
|
"tile/base/chrono.cc"
|
||||||
|
"tile/base/compression.cc"
|
||||||
|
"tile/base/compression/compression.cc"
|
||||||
|
"tile/base/compression/gzip.cc"
|
||||||
|
"tile/base/compression/util.cc"
|
||||||
|
"tile/base/demangle.cc"
|
||||||
|
"tile/base/encoding/base64.cc"
|
||||||
|
"tile/base/encoding/detail/hex_chars.cc"
|
||||||
|
"tile/base/encoding/hex.cc"
|
||||||
|
"tile/base/encoding/percent.cc"
|
||||||
|
"tile/base/exposed_var.cc"
|
||||||
|
"tile/base/internal/background_task_host.cc"
|
||||||
|
"tile/base/internal/background_task_host.h"
|
||||||
|
"tile/base/internal/case_insensitive_hash_map.h"
|
||||||
|
"tile/base/internal/curl.cc"
|
||||||
|
"tile/base/internal/curl.h"
|
||||||
|
"tile/base/internal/early_init.h"
|
||||||
|
"tile/base/internal/index_alloc.cc"
|
||||||
|
"tile/base/internal/logging.cc"
|
||||||
|
"tile/base/internal/logging.h"
|
||||||
|
"tile/base/internal/thread_pool.cc"
|
||||||
|
"tile/base/internal/time_keeper.cc"
|
||||||
|
"tile/base/internal/time_keeper.h"
|
||||||
|
"tile/base/logging/basic_file_sink.cc"
|
||||||
|
"tile/base/logging/splitter_sink.cc"
|
||||||
|
"tile/base/logging/console_sink.cc"
|
||||||
|
"tile/base/net/endpoint.cc"
|
||||||
|
"tile/base/object_pool/disabled.cc"
|
||||||
|
"tile/base/object_pool/global.cc"
|
||||||
|
"tile/base/object_pool/thread_local.cc"
|
||||||
|
"tile/base/option.cc"
|
||||||
|
"tile/base/option/gflags_provider.cc"
|
||||||
|
"tile/base/option/json_parser.cc"
|
||||||
|
"tile/base/option/key.cc"
|
||||||
|
"tile/base/option/option_provider.cc"
|
||||||
|
"tile/base/option/option_service.cc"
|
||||||
|
"tile/base/slice.cc"
|
||||||
|
"tile/base/status.cc"
|
||||||
|
"tile/base/string.cc"
|
||||||
|
"tile/base/thread/cond_var.cc"
|
||||||
|
"tile/base/thread/latch.cc"
|
||||||
|
"tile/base/thread/mutex.cc"
|
||||||
|
"tile/base/thread/rw_mutex.cc"
|
||||||
|
"tile/base/thread/scoped_lock.cc"
|
||||||
|
"tile/base/thread/spinlock.cc"
|
||||||
|
"tile/base/memory_barrier.cc"
|
||||||
|
# "tile/fiber/detail/fiber.cc"
|
||||||
|
"tile/io/detail/eintr_safe.cc"
|
||||||
|
"tile/io/native/acceptor.cc"
|
||||||
|
"tile/io/descriptor.cc"
|
||||||
|
"tile/io/util/rate_limiter.cc"
|
||||||
|
"tile/io/event_loop.cc"
|
||||||
|
"tile/init.cc"
|
||||||
|
"tile/init/on_init.cc"
|
||||||
|
"tile/init/override_flag.cc"
|
||||||
|
"tile/net/http/http_headers.cc"
|
||||||
|
"tile/net/http/http_message.cc"
|
||||||
|
"tile/net/http/http_request.cc"
|
||||||
|
"tile/net/http/http_response.cc"
|
||||||
|
"tile/net/http/http_client.cc"
|
||||||
|
"tile/net/http/types.cc"
|
||||||
|
"tile/net/internal/http_task.cc"
|
||||||
|
"tile/net/internal/http_engine.cc"
|
||||||
|
"tile/testing/internal/random_string.cc"
|
||||||
|
"tile/rpc/protocol/http/buffer_io.cc"
|
||||||
|
"tile/rpc/protocol/message.cc"
|
||||||
|
# "tile/rpc/server.cc"
|
||||||
|
"tile/base/config/configuration.cc"
|
||||||
|
"tile/base/config/ini_file_configuration.cc"
|
||||||
|
"tile/base/config/json_configuration.cc"
|
||||||
|
"tile/base/config/layered_configuration.cc"
|
||||||
|
"tile/base/config/toml_configuration.cc"
|
||||||
|
"tile/system/os.cc")
|
||||||
|
|
||||||
|
if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
|
||||||
|
list(APPEND TILE_SRCS "tile/base/net/detail/android/ifaddrs.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TILE_BUILD_SHARED)
|
||||||
|
add_library(tile SHARED ${TILE_SRCS})
|
||||||
|
else()
|
||||||
|
add_library(tile STATIC ${TILE_SRCS})
|
||||||
|
endif()
|
||||||
|
set_target_properties(tile PROPERTIES VERSION ${PROJECT_VERSION}
|
||||||
|
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||||
|
|
||||||
|
target_precompile_headers(
|
||||||
|
tile
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/third_party/header_only/toml.hpp
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/third_party/json/nlohann/json.hpp
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/third_party/inja/inja/string_view.h
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/third_party/inja/inja/inja.h
|
||||||
|
)
|
||||||
|
# target_sources(tile PRIVATE ${TILE_SRCS})
|
||||||
|
target_include_directories(
|
||||||
|
tile
|
||||||
|
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/third_party/mustache.hpp"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/fmt/include"
|
||||||
|
# "${CMAKE_CURRENT_BINARY_DIR}/third_party/glog"
|
||||||
|
# "${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
${THIRD_PARTY_INCLUDE_DIRS}
|
||||||
|
RPIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/header_only/"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
|
||||||
|
if(TILE_WITH_MIMALLOC)
|
||||||
|
target_link_libraries(tile PUBLIC mimalloc-obj)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
tile
|
||||||
|
PUBLIC # -Wl,--start-group nova_context
|
||||||
|
zlib
|
||||||
|
gflags::gflags # glog::glog
|
||||||
|
jsoncpp_static
|
||||||
|
# -Wl,--end-group
|
||||||
|
libcurl
|
||||||
|
fmt)
|
||||||
|
if((CMAKE_SYSTEM_PROCESSOR MATCHES "riscv64") OR (CMAKE_SYSTEM_PROCESSOR MATCHES
|
||||||
|
"mips*"))
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
target_link_libraries(tile PUBLIC atomic)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# set(LIB_NAMES tile)
|
||||||
|
#
|
||||||
|
# if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) OR
|
||||||
|
# ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}"
|
||||||
|
# STREQUAL "GNU") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD") OR
|
||||||
|
# ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") OR ("${CMAKE_SYSTEM_NAME}"
|
||||||
|
# STREQUAL "DragonFly") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME:
|
||||||
|
# It should be "GNU ld # for elf" set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES}
|
||||||
|
# -Wl,--no-whole-archive) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
|
||||||
|
# set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) endif()
|
||||||
|
|
||||||
|
add_library(tile::tile ALIAS tile)
|
||||||
|
# add_library(tile SHARED $<TARGET_OBJECTS:tile>) target_include_directories(
|
||||||
|
# tile PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/third_party/fmt/include"
|
||||||
|
# "${CMAKE_CURRENT_BINARY_DIR}/third_party/glog"
|
||||||
|
# "${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src"
|
||||||
|
# "${CMAKE_CURRENT_SOURCE_DIR}" RPIVATE
|
||||||
|
# "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
|
||||||
|
# target_link_libraries( tile PRIVATE ${WHOLE_ARCHIVE_PREFIX} gflags::gflags
|
||||||
|
# glog::glog libcurl fmt::fmt ${NO_WHOLE_ARCHIVE_PREFIX})
|
||||||
|
|
||||||
|
if(TILE_BUILD_TESTS)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_executable(tile_test_all "tile/testing/main.cc")
|
||||||
|
target_link_libraries(tile_test_all PRIVATE gtest gmock tile::tile)
|
||||||
|
|
||||||
|
macro(tile_add_custom_test test_name test_file)
|
||||||
|
add_executable(${test_name} ${test_file} "tile/testing/main.cc")
|
||||||
|
target_link_libraries(
|
||||||
|
${test_name} PUBLIC gtest gmock ${WHOLE_ARCHIVE_PREFIX} tile::tile
|
||||||
|
${WHOLE_ARCHIVE_SUFFIX})
|
||||||
|
|
||||||
|
target_sources(${PROJECT_NAME}_test_all PRIVATE ${test_file})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(tile_add_test test_name test_file)
|
||||||
|
tile_add_custom_test(${test_name} ${test_file})
|
||||||
|
add_test(NAME ${test_name} COMMAND ${test_name})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
function(add_test_group prefix group_name)
|
||||||
|
file(GLOB_RECURSE TEST_FILES "${prefix}/**/*_test.cc" "${prefix}/*_test.cc")
|
||||||
|
foreach(SRC_FILE ${TEST_FILES})
|
||||||
|
if(IS_DIRECTORY ${SRC_FILE})
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
# convert to relative path message(STATUS "${prefix} -> ${TEST_FILE}")
|
||||||
|
file(RELATIVE_PATH TEST_NAME "${prefix}" "${SRC_FILE}")
|
||||||
|
string(REPLACE "/" "_" TEST_NAME "${TEST_NAME}")
|
||||||
|
string(REPLACE "_test.cc" "_test" TEST_NAME "${TEST_NAME}")
|
||||||
|
# if group_name is not empty, add suffix _
|
||||||
|
if(NOT group_name STREQUAL "")
|
||||||
|
set(TEST_NAME "${group_name}_${TEST_NAME}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
tile_add_test(${TEST_NAME} ${SRC_FILE})
|
||||||
|
endforeach()
|
||||||
|
endfunction(add_test_group)
|
||||||
|
|
||||||
|
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/system system)
|
||||||
|
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/base base)
|
||||||
|
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/init init)
|
||||||
|
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/io io)
|
||||||
|
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/net net)
|
||||||
|
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/rpc rpc)
|
||||||
|
tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc")
|
||||||
|
endif(TILE_BUILD_TESTS)
|
||||||
|
|
||||||
|
if(TILE_BUILD_BENCHMARKS)
|
||||||
|
add_subdirectory("third_party/benchmark")
|
||||||
|
|
||||||
|
add_executable(tile_bm_all "tile/testing/bm_main.cc")
|
||||||
|
target_link_libraries(tile_bm_all PRIVATE benchmark::benchmark tile::tile)
|
||||||
|
|
||||||
|
macro(tile_add_bm benchmark_name benchmark_file)
|
||||||
|
add_executable(${benchmark_name} ${benchmark_file}
|
||||||
|
"tile/testing/bm_main.cc")
|
||||||
|
target_link_libraries(${benchmark_name} PRIVATE benchmark::benchmark
|
||||||
|
tile::tile)
|
||||||
|
|
||||||
|
target_sources(tile_bm_all PRIVATE ${benchmark_file})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# tile_add_bm(fiber_detail_fiber_benchmark
|
||||||
|
# "tile/fiber/detail/fiber_benchmark.cc")
|
||||||
|
tile_add_bm(base_buffer_benchmark "tile/base/buffer_benchmark.cc")
|
||||||
|
tile_add_bm(base_casting_benchmark "tile/base/casting_benchmark.cc")
|
||||||
|
tile_add_bm(base_thread_mutex_benchmark "tile/base/thread/mutex_benchmark.cc")
|
||||||
|
tile_add_bm(base_encoding_benchmark "tile/base/encoding_benchmark.cc")
|
||||||
|
tile_add_bm(base_internal_time_keeper_benchmark
|
||||||
|
"tile/base/internal/time_keeper_benchmark.cc")
|
||||||
|
tile_add_bm(base_chrono_benchmark "tile/base/chrono_benchmark.cc")
|
||||||
|
endif(TILE_BUILD_BENCHMARKS)
|
26
cmake/BuildInfo.cmake
Normal file
26
cmake/BuildInfo.cmake
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
macro(get_git_commit_hash output)
|
||||||
|
# full commit hash
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} log -1 --format="%H"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE ${output}
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(get_git_commit_date output)
|
||||||
|
# 2024-06-02T20:17:41+00:00
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} log -1 --format="%cI"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE ${output}
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(get_git_commit_subject output)
|
||||||
|
# message from `git commit -m "message"`
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} log -1 --format="%s"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_SUBJECT
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
endmacro()
|
13
dir.bloaty
Normal file
13
dir.bloaty
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
custom_data_source: {
|
||||||
|
name: "dir"
|
||||||
|
base_data_source: "compileunits"
|
||||||
|
|
||||||
|
rewrite: {
|
||||||
|
pattern: "^(.*/)(third_party/\\w+)"
|
||||||
|
replacement: "\\2"
|
||||||
|
}
|
||||||
|
rewrite: {
|
||||||
|
pattern: "^(.*/)(3party/\\w+)"
|
||||||
|
replacement: "\\2"
|
||||||
|
}
|
||||||
|
}
|
85
tests/http_client_test.cc
Normal file
85
tests/http_client_test.cc
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "tile/net/http/http_client.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "tile/base/thread/latch.h"
|
||||||
|
|
||||||
|
const std::string url = "http://127.0.0.1:8000/";
|
||||||
|
|
||||||
|
TEST(HttpClient, Request) {
|
||||||
|
tile::HttpClient client;
|
||||||
|
std::vector<tile::Future<
|
||||||
|
std::expected<tile::HttpResponse, tile::HttpClient::ErrorCode>>>
|
||||||
|
v;
|
||||||
|
tile::HttpClient::RequestOptions options;
|
||||||
|
options.timeout = std::chrono::seconds(20);
|
||||||
|
|
||||||
|
std::atomic<int> count{0};
|
||||||
|
|
||||||
|
for (int i = 0; i != 1000; ++i) {
|
||||||
|
for (auto code : tile::AllHttpStatus) {
|
||||||
|
if (static_cast<int>(code) < 200) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
v.emplace_back(
|
||||||
|
client.AsyncGet(url + std::to_string(static_cast<int>(code)), options)
|
||||||
|
.Then([&](std::expected<tile::HttpResponse,
|
||||||
|
tile::HttpClient::ErrorCode> &&res) {
|
||||||
|
int cnt = count.fetch_add(1) + 1;
|
||||||
|
TILE_LOG_INFO_EVERY_SECOND("complete {}", cnt);
|
||||||
|
return res;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i != 1000; ++i) {
|
||||||
|
for (auto &code : tile::AllHttpStatus) {
|
||||||
|
if (static_cast<int>(code) < 200) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resp = tile::future::BlockingGet(std::move(v[j++]));
|
||||||
|
|
||||||
|
EXPECT_TRUE(resp) << tile::HttpClient::ErrorCodeToString(resp.error());
|
||||||
|
EXPECT_EQ(resp->status(), code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HttpClient, RequestNoWait) {
|
||||||
|
tile::HttpClient client;
|
||||||
|
tile::HttpClient::RequestOptions options;
|
||||||
|
options.timeout = std::chrono::seconds(20);
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i != 1000; ++i) {
|
||||||
|
for (auto code : tile::AllHttpStatus) {
|
||||||
|
if (static_cast<int>(code) < 200) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tile::Latch latch(cnt);
|
||||||
|
std::atomic<int> count{0};
|
||||||
|
|
||||||
|
for (int i = 0; i != 1000; ++i) {
|
||||||
|
for (auto code : tile::AllHttpStatus) {
|
||||||
|
if (static_cast<int>(code) < 200) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.AsyncGet(url + std::to_string(static_cast<int>(code)), options)
|
||||||
|
.Then([&](std::expected<tile::HttpResponse,
|
||||||
|
tile::HttpClient::ErrorCode> &&) {
|
||||||
|
int cnt = count.fetch_add(1) + 1;
|
||||||
|
TILE_LOG_INFO_EVERY_SECOND("complete {}", cnt);
|
||||||
|
latch.CountDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
latch.Wait();
|
||||||
|
}
|
24
tests/http_server.py
Normal file
24
tests/http_server.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import http.server
|
||||||
|
import socket
|
||||||
|
import socketserver
|
||||||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
|
|
||||||
|
PORT = 8000
|
||||||
|
|
||||||
|
class Request(BaseHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
code = int(self.path.split("/")[1])
|
||||||
|
|
||||||
|
self.send_response(code)
|
||||||
|
self.send_header('Content-type', 'text/plain')
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
class HTTPServer(socketserver.TCPServer):
|
||||||
|
def server_bind(self):
|
||||||
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self.socket.bind(self.server_address)
|
||||||
|
|
||||||
|
Handler = http.server.SimpleHTTPRequestHandler
|
||||||
|
with HTTPServer(("", PORT), Request) as httpd:
|
||||||
|
httpd.serve_forever()
|
3
third_party/README.md
vendored
Normal file
3
third_party/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Library
|
||||||
|
|
||||||
|
- [toml11](https://github.com/ToruNiina/toml11/archive/refs/tags/v4.2.0.tar.gz)
|
5
third_party/benchmark/.clang-format
vendored
Normal file
5
third_party/benchmark/.clang-format
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: Google
|
||||||
|
PointerAlignment: Left
|
||||||
|
...
|
6
third_party/benchmark/.clang-tidy
vendored
Normal file
6
third_party/benchmark/.clang-tidy
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
Checks: 'clang-analyzer-*,readability-redundant-*,performance-*'
|
||||||
|
WarningsAsErrors: 'clang-analyzer-*,readability-redundant-*,performance-*'
|
||||||
|
HeaderFilterRegex: '.*'
|
||||||
|
FormatStyle: none
|
||||||
|
User: user
|
68
third_party/benchmark/.gitignore
vendored
Normal file
68
third_party/benchmark/.gitignore
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.so.?*
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.dylib
|
||||||
|
*.cmake
|
||||||
|
!/cmake/*.cmake
|
||||||
|
!/test/AssemblyTests.cmake
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
*.pyc
|
||||||
|
__pycache__
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# bazel output symlinks.
|
||||||
|
bazel-*
|
||||||
|
MODULE.bazel.lock
|
||||||
|
|
||||||
|
# out-of-source build top-level folders.
|
||||||
|
build/
|
||||||
|
_build/
|
||||||
|
build*/
|
||||||
|
|
||||||
|
# in-source dependencies
|
||||||
|
/googletest/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
CMakeSettings.json
|
||||||
|
|
||||||
|
# Visual Studio Code cache/options directory
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Python build stuff
|
||||||
|
dist/
|
||||||
|
*.egg-info*
|
18
third_party/benchmark/.pre-commit-config.yaml
vendored
Normal file
18
third_party/benchmark/.pre-commit-config.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/keith/pre-commit-buildifier
|
||||||
|
rev: 6.4.0
|
||||||
|
hooks:
|
||||||
|
- id: buildifier
|
||||||
|
- id: buildifier-lint
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v1.8.0
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
types_or: [ python, pyi ]
|
||||||
|
args: [ "--ignore-missing-imports", "--scripts-are-modules" ]
|
||||||
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
|
rev: v0.3.1
|
||||||
|
hooks:
|
||||||
|
- id: ruff
|
||||||
|
args: [ --fix, --exit-non-zero-on-fix ]
|
||||||
|
- id: ruff-format
|
208
third_party/benchmark/.travis.yml
vendored
Normal file
208
third_party/benchmark/.travis.yml
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
language: cpp
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- lcov
|
||||||
|
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Coverage
|
||||||
|
- compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- g++-multilib
|
||||||
|
- libc6:i386
|
||||||
|
env:
|
||||||
|
- COMPILER=g++
|
||||||
|
- C_COMPILER=gcc
|
||||||
|
- BUILD_TYPE=Debug
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-m32"
|
||||||
|
- compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- g++-multilib
|
||||||
|
- libc6:i386
|
||||||
|
env:
|
||||||
|
- COMPILER=g++
|
||||||
|
- C_COMPILER=gcc
|
||||||
|
- BUILD_TYPE=Release
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-m32"
|
||||||
|
- compiler: gcc
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=g++-6 C_COMPILER=gcc-6 BUILD_TYPE=Debug
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-fno-omit-frame-pointer -g -O2 -fsanitize=undefined,address -fuse-ld=gold"
|
||||||
|
# Clang w/ libc++
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Release
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
# Clang w/ 32bit libc++
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-3.8
|
||||||
|
- g++-multilib
|
||||||
|
- libc6:i386
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-m32"
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
# Clang w/ 32bit libc++
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-3.8
|
||||||
|
- g++-multilib
|
||||||
|
- libc6:i386
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Release
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-m32"
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
# Clang w/ libc++, ASAN, UBSAN
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1 LIBCXX_SANITIZER="Undefined;Address"
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=undefined,address -fno-sanitize-recover=all"
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
- UBSAN_OPTIONS=print_stacktrace=1
|
||||||
|
# Clang w/ libc++ and MSAN
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=MemoryWithOrigins
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins"
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
# Clang w/ libc++ and MSAN
|
||||||
|
- compiler: clang
|
||||||
|
dist: xenial
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=RelWithDebInfo
|
||||||
|
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=Thread
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all"
|
||||||
|
- EXTRA_CXX_FLAGS="-stdlib=libc++"
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++
|
||||||
|
- BUILD_TYPE=Release
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-m32"
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- if [ -n "${LIBCXX_BUILD}" ]; then
|
||||||
|
source .libcxx-setup.sh;
|
||||||
|
fi
|
||||||
|
- if [ -n "${ENABLE_SANITIZER}" ]; then
|
||||||
|
export EXTRA_OPTIONS="-DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF";
|
||||||
|
else
|
||||||
|
export EXTRA_OPTIONS="";
|
||||||
|
fi
|
||||||
|
- mkdir -p build && cd build
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [ -z "$BUILD_32_BITS" ]; then
|
||||||
|
export BUILD_32_BITS=OFF && echo disabling 32 bit build;
|
||||||
|
fi
|
||||||
|
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
|
||||||
|
sudo add-apt-repository -y "ppa:ubuntu-toolchain-r/test";
|
||||||
|
sudo apt-get update --option Acquire::Retries=100 --option Acquire::http::Timeout="60";
|
||||||
|
fi
|
||||||
|
|
||||||
|
install:
|
||||||
|
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
|
||||||
|
travis_wait sudo -E apt-get -yq --no-install-suggests --no-install-recommends install g++-6;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" == "linux" -a "${BUILD_32_BITS}" == "OFF" ]; then
|
||||||
|
travis_wait sudo -E apt-get -y --no-install-suggests --no-install-recommends install llvm-3.9-tools;
|
||||||
|
sudo cp /usr/lib/llvm-3.9/bin/FileCheck /usr/local/bin/;
|
||||||
|
fi
|
||||||
|
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||||
|
PATH=~/.local/bin:${PATH};
|
||||||
|
pip install --user --upgrade pip;
|
||||||
|
travis_wait pip install --user cpp-coveralls;
|
||||||
|
fi
|
||||||
|
- if [ "${C_COMPILER}" == "gcc-7" -a "${TRAVIS_OS_NAME}" == "osx" ]; then
|
||||||
|
rm -f /usr/local/include/c++;
|
||||||
|
brew update;
|
||||||
|
travis_wait brew install gcc@7;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||||
|
sudo apt-get update -qq;
|
||||||
|
sudo apt-get install -qq unzip cmake3;
|
||||||
|
wget https://github.com/bazelbuild/bazel/releases/download/3.2.0/bazel-3.2.0-installer-linux-x86_64.sh --output-document bazel-installer.sh;
|
||||||
|
travis_wait sudo bash bazel-installer.sh;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
|
||||||
|
curl -L -o bazel-installer.sh https://github.com/bazelbuild/bazel/releases/download/3.2.0/bazel-3.2.0-installer-darwin-x86_64.sh;
|
||||||
|
travis_wait sudo bash bazel-installer.sh;
|
||||||
|
fi
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_C_FLAGS="${EXTRA_FLAGS}" -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} ${EXTRA_CXX_FLAGS}" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_BUILD_32_BITS=${BUILD_32_BITS} ${EXTRA_OPTIONS} ..
|
||||||
|
- make
|
||||||
|
- ctest -C ${BUILD_TYPE} --output-on-failure
|
||||||
|
- bazel test -c dbg --define google_benchmark.have_regex=posix --announce_rc --verbose_failures --test_output=errors --keep_going //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
|
120
third_party/benchmark/.ycm_extra_conf.py
vendored
Normal file
120
third_party/benchmark/.ycm_extra_conf.py
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
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",
|
||||||
|
"-pedantic-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}
|
72
third_party/benchmark/AUTHORS
vendored
Normal file
72
third_party/benchmark/AUTHORS
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# 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>
|
||||||
|
Alex Steele <steeleal123@gmail.com>
|
||||||
|
Andriy Berestovskyy <berestovskyy@gmail.com>
|
||||||
|
Arne Beer <arne@twobeer.de>
|
||||||
|
Carto
|
||||||
|
Cezary Skrzyński <czars1988@gmail.com>
|
||||||
|
Christian Wassermann <christian_wassermann@web.de>
|
||||||
|
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||||
|
Colin Braley <braley.colin@gmail.com>
|
||||||
|
Daniel Harvey <danielharvey458@gmail.com>
|
||||||
|
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||||
|
Deniz Evrenci <denizevrenci@gmail.com>
|
||||||
|
Dirac Research
|
||||||
|
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
||||||
|
Dominik Korman <kormandominik@gmail.com>
|
||||||
|
Donald Aingworth <donalds_junk_mail@yahoo.com>
|
||||||
|
Eric Backus <eric_backus@alum.mit.edu>
|
||||||
|
Eric Fiselier <eric@efcs.ca>
|
||||||
|
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||||
|
Evgeny Safronov <division494@gmail.com>
|
||||||
|
Fabien Pichot <pichot.fabien@gmail.com>
|
||||||
|
Federico Ficarelli <federico.ficarelli@gmail.com>
|
||||||
|
Felix Homann <linuxaudio@showlabor.de>
|
||||||
|
Gergely Meszaros <maetveis@gmail.com>
|
||||||
|
Gergő Szitár <szitar.gergo@gmail.com>
|
||||||
|
Google Inc.
|
||||||
|
Henrique Bucher <hbucher@gmail.com>
|
||||||
|
International Business Machines Corporation
|
||||||
|
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
||||||
|
Jern-Kuan Leong <jernkuan@gmail.com>
|
||||||
|
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
||||||
|
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
|
||||||
|
Jordan Williams <jwillikers@protonmail.com>
|
||||||
|
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
||||||
|
Kaito Udagawa <umireon@gmail.com>
|
||||||
|
Kishan Kumar <kumar.kishan@outlook.com>
|
||||||
|
Lei Xu <eddyxu@gmail.com>
|
||||||
|
Marcel Jacobse <mjacobse@uni-bremen.de>
|
||||||
|
Matt Clarkson <mattyclarkson@gmail.com>
|
||||||
|
Maxim Vafin <maxvafin@gmail.com>
|
||||||
|
Mike Apodaca <gatorfax@gmail.com>
|
||||||
|
Min-Yih Hsu <yihshyng223@gmail.com>
|
||||||
|
MongoDB Inc.
|
||||||
|
Nick Hutchinson <nshutchinson@gmail.com>
|
||||||
|
Norman Heino <norman.heino@gmail.com>
|
||||||
|
Oleksandr Sochka <sasha.sochka@gmail.com>
|
||||||
|
Ori Livneh <ori.livneh@gmail.com>
|
||||||
|
Paul Redmond <paul.redmond@gmail.com>
|
||||||
|
Radoslav Yovchev <radoslav.tm@gmail.com>
|
||||||
|
Raghu Raja <raghu@enfabrica.net>
|
||||||
|
Rainer Orth <ro@cebitec.uni-bielefeld.de>
|
||||||
|
Roman Lebedev <lebedev.ri@gmail.com>
|
||||||
|
Sayan Bhattacharjee <aero.sayan@gmail.com>
|
||||||
|
Shapr3D <google-contributors@shapr3d.com>
|
||||||
|
Shuo Chen <chenshuo@chenshuo.com>
|
||||||
|
Staffan Tjernstrom <staffantj@gmail.com>
|
||||||
|
Steinar H. Gunderson <sgunderson@bigfoot.com>
|
||||||
|
Stripe, Inc.
|
||||||
|
Tobias Schmidt <tobias.schmidt@in.tum.de>
|
||||||
|
Yixuan Qiu <yixuanq@gmail.com>
|
||||||
|
Yusuke Suzuki <utatane.tea@gmail.com>
|
||||||
|
Zbigniew Skowron <zbychs@gmail.com>
|
114
third_party/benchmark/BUILD.bazel
vendored
Normal file
114
third_party/benchmark/BUILD.bazel
vendored
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
COPTS = [
|
||||||
|
"-pedantic",
|
||||||
|
"-pedantic-errors",
|
||||||
|
"-std=c++11",
|
||||||
|
"-Wall",
|
||||||
|
"-Wconversion",
|
||||||
|
"-Wextra",
|
||||||
|
"-Wshadow",
|
||||||
|
# "-Wshorten-64-to-32",
|
||||||
|
"-Wfloat-equal",
|
||||||
|
"-fstrict-aliasing",
|
||||||
|
## assert() are used a lot in tests upstream, which may be optimised out leading to
|
||||||
|
## unused-variable warning.
|
||||||
|
"-Wno-unused-variable",
|
||||||
|
"-Werror=old-style-cast",
|
||||||
|
]
|
||||||
|
|
||||||
|
config_setting(
|
||||||
|
name = "qnx",
|
||||||
|
constraint_values = ["@platforms//os:qnx"],
|
||||||
|
values = {
|
||||||
|
"cpu": "x64_qnx",
|
||||||
|
},
|
||||||
|
visibility = [":__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
|
config_setting(
|
||||||
|
name = "windows",
|
||||||
|
constraint_values = ["@platforms//os:windows"],
|
||||||
|
values = {
|
||||||
|
"cpu": "x64_windows",
|
||||||
|
},
|
||||||
|
visibility = [":__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
|
config_setting(
|
||||||
|
name = "macos",
|
||||||
|
constraint_values = ["@platforms//os:macos"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
config_setting(
|
||||||
|
name = "perfcounters",
|
||||||
|
define_values = {
|
||||||
|
"pfm": "1",
|
||||||
|
},
|
||||||
|
visibility = [":__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "benchmark",
|
||||||
|
srcs = glob(
|
||||||
|
[
|
||||||
|
"src/*.cc",
|
||||||
|
"src/*.h",
|
||||||
|
],
|
||||||
|
exclude = ["src/benchmark_main.cc"],
|
||||||
|
),
|
||||||
|
hdrs = [
|
||||||
|
"include/benchmark/benchmark.h",
|
||||||
|
"include/benchmark/export.h",
|
||||||
|
],
|
||||||
|
copts = select({
|
||||||
|
":windows": [],
|
||||||
|
"//conditions:default": COPTS,
|
||||||
|
}),
|
||||||
|
defines = [
|
||||||
|
"BENCHMARK_STATIC_DEFINE",
|
||||||
|
"BENCHMARK_VERSION=\\\"" + (module_version() if module_version() != None else "") + "\\\"",
|
||||||
|
] + select({
|
||||||
|
":perfcounters": ["HAVE_LIBPFM"],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
linkopts = select({
|
||||||
|
":windows": ["-DEFAULTLIB:shlwapi.lib"],
|
||||||
|
"//conditions:default": ["-pthread"],
|
||||||
|
}),
|
||||||
|
# Only static linking is allowed; no .so will be produced.
|
||||||
|
# Using `defines` (i.e. not `local_defines`) means that no
|
||||||
|
# dependent rules need to bother about defining the macro.
|
||||||
|
linkstatic = True,
|
||||||
|
local_defines = [
|
||||||
|
# Turn on Large-file Support
|
||||||
|
"_FILE_OFFSET_BITS=64",
|
||||||
|
"_LARGEFILE64_SOURCE",
|
||||||
|
"_LARGEFILE_SOURCE",
|
||||||
|
],
|
||||||
|
strip_include_prefix = "include",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = select({
|
||||||
|
":perfcounters": ["@libpfm"],
|
||||||
|
"//conditions:default": [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "benchmark_main",
|
||||||
|
srcs = ["src/benchmark_main.cc"],
|
||||||
|
hdrs = [
|
||||||
|
"include/benchmark/benchmark.h",
|
||||||
|
"include/benchmark/export.h",
|
||||||
|
],
|
||||||
|
strip_include_prefix = "include",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [":benchmark"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "benchmark_internal_headers",
|
||||||
|
hdrs = glob(["src/*.h"]),
|
||||||
|
visibility = ["//test:__pkg__"],
|
||||||
|
)
|
355
third_party/benchmark/CMakeLists.txt
vendored
Normal file
355
third_party/benchmark/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
# Require CMake 3.10. If available, use the policies up to CMake 3.22.
|
||||||
|
cmake_minimum_required (VERSION 3.10...3.22)
|
||||||
|
|
||||||
|
project (benchmark VERSION 1.8.4 LANGUAGES CXX)
|
||||||
|
|
||||||
|
option(BENCHMARK_ENABLE_TESTING "Enable testing of the benchmark library." OFF)
|
||||||
|
option(BENCHMARK_ENABLE_EXCEPTIONS "Enable the use of exceptions in the benchmark library." ON)
|
||||||
|
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)
|
||||||
|
option(BENCHMARK_ENABLE_WERROR "Build Release candidates with -Werror." ON)
|
||||||
|
option(BENCHMARK_FORCE_WERROR "Build Release candidates with -Werror regardless of compiler issues." OFF)
|
||||||
|
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "PGI")
|
||||||
|
# PGC++ maybe reporting false positives.
|
||||||
|
set(BENCHMARK_ENABLE_WERROR OFF)
|
||||||
|
endif()
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "NVHPC")
|
||||||
|
set(BENCHMARK_ENABLE_WERROR OFF)
|
||||||
|
endif()
|
||||||
|
if(BENCHMARK_FORCE_WERROR)
|
||||||
|
set(BENCHMARK_ENABLE_WERROR ON)
|
||||||
|
endif(BENCHMARK_FORCE_WERROR)
|
||||||
|
|
||||||
|
if(NOT (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))
|
||||||
|
option(BENCHMARK_BUILD_32_BITS "Build a 32 bit version of the library." OFF)
|
||||||
|
else()
|
||||||
|
set(BENCHMARK_BUILD_32_BITS OFF CACHE BOOL "Build a 32 bit version of the library - unsupported when using MSVC)" FORCE)
|
||||||
|
endif()
|
||||||
|
option(BENCHMARK_ENABLE_INSTALL "Enable installation of benchmark. (Projects embedding benchmark may want to turn this OFF.)" ON)
|
||||||
|
option(BENCHMARK_ENABLE_DOXYGEN "Build documentation with Doxygen." OFF)
|
||||||
|
option(BENCHMARK_INSTALL_DOCS "Enable installation of documentation." OFF)
|
||||||
|
|
||||||
|
# Allow unmet dependencies to be met using CMake's ExternalProject mechanics, which
|
||||||
|
# may require downloading the source code.
|
||||||
|
option(BENCHMARK_DOWNLOAD_DEPENDENCIES "Allow the downloading and in-tree building of unmet dependencies" OFF)
|
||||||
|
|
||||||
|
# This option can be used to disable building and running unit tests which depend on gtest
|
||||||
|
# in cases where it is not possible to build or find a valid version of gtest.
|
||||||
|
option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" OFF)
|
||||||
|
option(BENCHMARK_USE_BUNDLED_GTEST "Use bundled GoogleTest. If disabled, the find_package(GTest) will be used." ON)
|
||||||
|
|
||||||
|
option(BENCHMARK_ENABLE_LIBPFM "Enable performance counters provided by libpfm" OFF)
|
||||||
|
|
||||||
|
# Export only public symbols
|
||||||
|
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||||
|
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
|
# As of CMake 3.18, CMAKE_SYSTEM_PROCESSOR is not set properly for MSVC and
|
||||||
|
# cross-compilation (e.g. Host=x86_64, target=aarch64) requires using the
|
||||||
|
# undocumented, but working variable.
|
||||||
|
# See https://gitlab.kitware.com/cmake/cmake/-/issues/15170
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR ${MSVC_CXX_ARCHITECTURE_ID})
|
||||||
|
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM")
|
||||||
|
set(CMAKE_CROSSCOMPILING TRUE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ENABLE_ASSEMBLY_TESTS_DEFAULT OFF)
|
||||||
|
function(should_enable_assembly_tests)
|
||||||
|
if(CMAKE_BUILD_TYPE)
|
||||||
|
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
|
||||||
|
if (${CMAKE_BUILD_TYPE_LOWER} MATCHES "coverage")
|
||||||
|
# FIXME: The --coverage flag needs to be removed when building assembly
|
||||||
|
# tests for this to work.
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
||||||
|
return()
|
||||||
|
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||||
|
return()
|
||||||
|
elseif(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
# FIXME: Make these work on 32 bit builds
|
||||||
|
return()
|
||||||
|
elseif(BENCHMARK_BUILD_32_BITS)
|
||||||
|
# FIXME: Make these work on 32 bit builds
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
find_program(LLVM_FILECHECK_EXE FileCheck)
|
||||||
|
if (LLVM_FILECHECK_EXE)
|
||||||
|
set(LLVM_FILECHECK_EXE "${LLVM_FILECHECK_EXE}" CACHE PATH "llvm filecheck" FORCE)
|
||||||
|
message(STATUS "LLVM FileCheck Found: ${LLVM_FILECHECK_EXE}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Failed to find LLVM FileCheck")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(ENABLE_ASSEMBLY_TESTS_DEFAULT ON PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
should_enable_assembly_tests()
|
||||||
|
|
||||||
|
# This option disables the building and running of the assembly verification tests
|
||||||
|
option(BENCHMARK_ENABLE_ASSEMBLY_TESTS "Enable building and running the assembly tests"
|
||||||
|
${ENABLE_ASSEMBLY_TESTS_DEFAULT})
|
||||||
|
|
||||||
|
# Make sure we can import out CMake functions
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
|
||||||
|
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)
|
||||||
|
|
||||||
|
# If no git version can be determined, use the version
|
||||||
|
# from the project() command
|
||||||
|
if ("${GIT_VERSION}" STREQUAL "0.0.0")
|
||||||
|
set(VERSION "v${benchmark_VERSION}")
|
||||||
|
else()
|
||||||
|
set(VERSION "${GIT_VERSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Normalize version: drop "v" prefix, replace first "-" with ".",
|
||||||
|
# drop everything after second "-" (including said "-").
|
||||||
|
string(STRIP ${VERSION} VERSION)
|
||||||
|
if(VERSION MATCHES v[^-]*-)
|
||||||
|
string(REGEX REPLACE "v([^-]*)-([0-9]+)-.*" "\\1.\\2" NORMALIZED_VERSION ${VERSION})
|
||||||
|
else()
|
||||||
|
string(REGEX REPLACE "v(.*)" "\\1" NORMALIZED_VERSION ${VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Tell the user what versions we are using
|
||||||
|
message(STATUS "Google Benchmark version: ${VERSION}, normalized to ${NORMALIZED_VERSION}")
|
||||||
|
|
||||||
|
# The version of the libraries
|
||||||
|
set(GENERIC_LIB_VERSION ${NORMALIZED_VERSION})
|
||||||
|
string(SUBSTRING ${NORMALIZED_VERSION} 0 1 GENERIC_LIB_SOVERSION)
|
||||||
|
|
||||||
|
# Import our CMake modules
|
||||||
|
include(AddCXXCompilerFlag)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckLibraryExists)
|
||||||
|
include(CXXFeatureCheck)
|
||||||
|
|
||||||
|
check_library_exists(rt shm_open "" HAVE_LIB_RT)
|
||||||
|
|
||||||
|
if (BENCHMARK_BUILD_32_BITS)
|
||||||
|
add_required_cxx_compiler_flag(-m32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
||||||
|
set(BENCHMARK_CXX_STANDARD 14)
|
||||||
|
else()
|
||||||
|
set(BENCHMARK_CXX_STANDARD 11)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD ${BENCHMARK_CXX_STANDARD})
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
if (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)
|
||||||
|
|
||||||
|
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
||||||
|
add_cxx_compiler_flag(-EHs-)
|
||||||
|
add_cxx_compiler_flag(-EHa-)
|
||||||
|
add_definitions(-D_HAS_EXCEPTIONS=0)
|
||||||
|
endif()
|
||||||
|
# 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()
|
||||||
|
# Turn on Large-file Support
|
||||||
|
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||||
|
add_definitions(-D_LARGEFILE64_SOURCE)
|
||||||
|
add_definitions(-D_LARGEFILE_SOURCE)
|
||||||
|
# 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(-Wfloat-equal)
|
||||||
|
add_cxx_compiler_flag(-Wold-style-cast)
|
||||||
|
add_cxx_compiler_flag(-Wconversion)
|
||||||
|
if(BENCHMARK_ENABLE_WERROR)
|
||||||
|
add_cxx_compiler_flag(-Werror)
|
||||||
|
endif()
|
||||||
|
if (NOT BENCHMARK_ENABLE_TESTING)
|
||||||
|
# Disable warning when compiling tests as gtest does not use 'override'.
|
||||||
|
add_cxx_compiler_flag(-Wsuggest-override)
|
||||||
|
endif()
|
||||||
|
add_cxx_compiler_flag(-pedantic)
|
||||||
|
add_cxx_compiler_flag(-pedantic-errors)
|
||||||
|
add_cxx_compiler_flag(-Wshorten-64-to-32)
|
||||||
|
add_cxx_compiler_flag(-fstrict-aliasing)
|
||||||
|
# Disable warnings regarding deprecated parts of the library while building
|
||||||
|
# and testing those parts of the library.
|
||||||
|
add_cxx_compiler_flag(-Wno-deprecated-declarations)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
||||||
|
# Intel silently ignores '-Wno-deprecated-declarations',
|
||||||
|
# warning no. 1786 must be explicitly disabled.
|
||||||
|
# See #631 for rationale.
|
||||||
|
add_cxx_compiler_flag(-wd1786)
|
||||||
|
add_cxx_compiler_flag(-fno-finite-math-only)
|
||||||
|
endif()
|
||||||
|
# Disable deprecation warnings for release builds (when -Werror is enabled).
|
||||||
|
if(BENCHMARK_ENABLE_WERROR)
|
||||||
|
add_cxx_compiler_flag(-Wno-deprecated)
|
||||||
|
endif()
|
||||||
|
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
||||||
|
add_cxx_compiler_flag(-fno-exceptions)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (HAVE_CXX_FLAG_FSTRICT_ALIASING)
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel" AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") #ICC17u2: Many false positives for Wstrict-aliasing
|
||||||
|
add_cxx_compiler_flag(-Wstrict-aliasing)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
# ICC17u2: overloaded virtual function "benchmark::Fixture::SetUp" is only partially overridden
|
||||||
|
# (because of deprecated overload)
|
||||||
|
add_cxx_compiler_flag(-wd654)
|
||||||
|
add_cxx_compiler_flag(-Wthread-safety)
|
||||||
|
if (HAVE_CXX_FLAG_WTHREAD_SAFETY)
|
||||||
|
cxx_feature_check(THREAD_SAFETY_ATTRIBUTES "-DINCLUDE_DIRECTORIES=${PROJECT_SOURCE_DIR}/include")
|
||||||
|
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 ourselves
|
||||||
|
# since we depend on GNU/POSIX/BSD extensions.
|
||||||
|
if (CYGWIN)
|
||||||
|
add_definitions(-D_GNU_SOURCE=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (QNXNTO)
|
||||||
|
add_definitions(-D_QNX_SOURCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Link time optimisation
|
||||||
|
if (BENCHMARK_ENABLE_LTO)
|
||||||
|
add_cxx_compiler_flag(-flto)
|
||||||
|
add_cxx_compiler_flag(-Wno-lto-type-mismatch)
|
||||||
|
if ("${CMAKE_CXX_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()
|
||||||
|
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
include(llvm-toolchain)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Coverage build type
|
||||||
|
set(BENCHMARK_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG}"
|
||||||
|
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||||
|
FORCE)
|
||||||
|
set(BENCHMARK_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_DEBUG}"
|
||||||
|
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||||
|
FORCE)
|
||||||
|
set(BENCHMARK_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(
|
||||||
|
BENCHMARK_CXX_FLAGS_COVERAGE
|
||||||
|
BENCHMARK_EXE_LINKER_FLAGS_COVERAGE
|
||||||
|
BENCHMARK_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.")
|
||||||
|
add_cxx_compiler_flag(--coverage COVERAGE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (BENCHMARK_USE_LIBCXX)
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
add_cxx_compiler_flag(-stdlib=libc++)
|
||||||
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
||||||
|
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" OR
|
||||||
|
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "IntelLLVM")
|
||||||
|
add_cxx_compiler_flag(-nostdinc++)
|
||||||
|
message(WARNING "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_ERROR "-DBENCHMARK_USE_LIBCXX:BOOL=ON is not supported for compiler")
|
||||||
|
endif()
|
||||||
|
endif(BENCHMARK_USE_LIBCXX)
|
||||||
|
|
||||||
|
set(EXTRA_CXX_FLAGS "")
|
||||||
|
if (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
# Clang on Windows fails to compile the regex feature check under C++11
|
||||||
|
set(EXTRA_CXX_FLAGS "-DCMAKE_CXX_STANDARD=14")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# C++ feature checks
|
||||||
|
# Determine the correct regular expression engine to use
|
||||||
|
cxx_feature_check(STD_REGEX ${EXTRA_CXX_FLAGS})
|
||||||
|
cxx_feature_check(GNU_POSIX_REGEX ${EXTRA_CXX_FLAGS})
|
||||||
|
cxx_feature_check(POSIX_REGEX ${EXTRA_CXX_FLAGS})
|
||||||
|
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()
|
||||||
|
if (NOT BENCHMARK_ENABLE_EXCEPTIONS AND HAVE_STD_REGEX
|
||||||
|
AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX)
|
||||||
|
message(WARNING "Using std::regex with exceptions disabled is not fully supported")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
cxx_feature_check(STEADY_CLOCK)
|
||||||
|
# Ensure we have pthreads
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
cxx_feature_check(PTHREAD_AFFINITY)
|
||||||
|
|
||||||
|
if (BENCHMARK_ENABLE_LIBPFM)
|
||||||
|
find_package(PFM REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set up directories
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
# Build the targets
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
if (BENCHMARK_ENABLE_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
if (BENCHMARK_ENABLE_GTEST_TESTS AND
|
||||||
|
NOT (TARGET gtest AND TARGET gtest_main AND
|
||||||
|
TARGET gmock AND TARGET gmock_main))
|
||||||
|
if (BENCHMARK_USE_BUNDLED_GTEST)
|
||||||
|
include(GoogleTest)
|
||||||
|
else()
|
||||||
|
find_package(GTest CONFIG REQUIRED)
|
||||||
|
add_library(gtest ALIAS GTest::gtest)
|
||||||
|
add_library(gtest_main ALIAS GTest::gtest_main)
|
||||||
|
add_library(gmock ALIAS GTest::gmock)
|
||||||
|
add_library(gmock_main ALIAS GTest::gmock_main)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
58
third_party/benchmark/CONTRIBUTING.md
vendored
Normal file
58
third_party/benchmark/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# 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
|
97
third_party/benchmark/CONTRIBUTORS
vendored
Normal file
97
third_party/benchmark/CONTRIBUTORS
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
Abhina Sreeskantharajan <abhina.sreeskantharajan@ibm.com>
|
||||||
|
Albert Pretorius <pretoalb@gmail.com>
|
||||||
|
Alex Steele <steelal123@gmail.com>
|
||||||
|
Andriy Berestovskyy <berestovskyy@gmail.com>
|
||||||
|
Arne Beer <arne@twobeer.de>
|
||||||
|
Bátor Tallér <bator.taller@shapr3d.com>
|
||||||
|
Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
|
||||||
|
Cezary Skrzyński <czars1988@gmail.com>
|
||||||
|
Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
|
||||||
|
Christian Wassermann <christian_wassermann@web.de>
|
||||||
|
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||||
|
Colin Braley <braley.colin@gmail.com>
|
||||||
|
Cyrille Faucheux <cyrille.faucheux@gmail.com>
|
||||||
|
Daniel Harvey <danielharvey458@gmail.com>
|
||||||
|
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||||
|
Deniz Evrenci <denizevrenci@gmail.com>
|
||||||
|
Dominic Hamon <dma@stripysock.com> <dominic@google.com>
|
||||||
|
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
||||||
|
Dominik Korman <kormandominik@gmail.com>
|
||||||
|
Donald Aingworth <donalds_junk_mail@yahoo.com>
|
||||||
|
Eric Backus <eric_backus@alum.mit.edu>
|
||||||
|
Eric Fiselier <eric@efcs.ca>
|
||||||
|
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||||
|
Evgeny Safronov <division494@gmail.com>
|
||||||
|
Fabien Pichot <pichot.fabien@gmail.com>
|
||||||
|
Fanbo Meng <fanbo.meng@ibm.com>
|
||||||
|
Federico Ficarelli <federico.ficarelli@gmail.com>
|
||||||
|
Felix Homann <linuxaudio@showlabor.de>
|
||||||
|
Geoffrey Martin-Noble <gcmn@google.com> <gmngeoffrey@gmail.com>
|
||||||
|
Gergely Meszaros <maetveis@gmail.com>
|
||||||
|
Gergő Szitár <szitar.gergo@gmail.com>
|
||||||
|
Hannes Hauswedell <h2@fsfe.org>
|
||||||
|
Henrique Bucher <hbucher@gmail.com>
|
||||||
|
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
||||||
|
Iakov Sergeev <yahontu@gmail.com>
|
||||||
|
Jern-Kuan Leong <jernkuan@gmail.com>
|
||||||
|
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
||||||
|
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
|
||||||
|
John Millikin <jmillikin@stripe.com>
|
||||||
|
Jordan Williams <jwillikers@protonmail.com>
|
||||||
|
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
||||||
|
Kaito Udagawa <umireon@gmail.com>
|
||||||
|
Kai Wolf <kai.wolf@gmail.com>
|
||||||
|
Kishan Kumar <kumar.kishan@outlook.com>
|
||||||
|
Lei Xu <eddyxu@gmail.com>
|
||||||
|
Marcel Jacobse <mjacobse@uni-bremen.de>
|
||||||
|
Matt Clarkson <mattyclarkson@gmail.com>
|
||||||
|
Maxim Vafin <maxvafin@gmail.com>
|
||||||
|
Mike Apodaca <gatorfax@gmail.com>
|
||||||
|
Min-Yih Hsu <yihshyng223@gmail.com>
|
||||||
|
Nick Hutchinson <nshutchinson@gmail.com>
|
||||||
|
Norman Heino <norman.heino@gmail.com>
|
||||||
|
Oleksandr Sochka <sasha.sochka@gmail.com>
|
||||||
|
Ori Livneh <ori.livneh@gmail.com>
|
||||||
|
Pascal Leroy <phl@google.com>
|
||||||
|
Paul Redmond <paul.redmond@gmail.com>
|
||||||
|
Pierre Phaneuf <pphaneuf@google.com>
|
||||||
|
Radoslav Yovchev <radoslav.tm@gmail.com>
|
||||||
|
Raghu Raja <raghu@enfabrica.net>
|
||||||
|
Rainer Orth <ro@cebitec.uni-bielefeld.de>
|
||||||
|
Raul Marin <rmrodriguez@cartodb.com>
|
||||||
|
Ray Glover <ray.glover@uk.ibm.com>
|
||||||
|
Robert Guo <robert.guo@mongodb.com>
|
||||||
|
Roman Lebedev <lebedev.ri@gmail.com>
|
||||||
|
Sayan Bhattacharjee <aero.sayan@gmail.com>
|
||||||
|
Shuo Chen <chenshuo@chenshuo.com>
|
||||||
|
Steven Wan <wan.yu@ibm.com>
|
||||||
|
Tobias Schmidt <tobias.schmidt@in.tum.de>
|
||||||
|
Tobias Ulvgård <tobias.ulvgard@dirac.se>
|
||||||
|
Tom Madams <tom.ej.madams@gmail.com> <tmadams@google.com>
|
||||||
|
Yixuan Qiu <yixuanq@gmail.com>
|
||||||
|
Yusuke Suzuki <utatane.tea@gmail.com>
|
||||||
|
Zbigniew Skowron <zbychs@gmail.com>
|
202
third_party/benchmark/LICENSE
vendored
Normal file
202
third_party/benchmark/LICENSE
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
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.
|
41
third_party/benchmark/MODULE.bazel
vendored
Normal file
41
third_party/benchmark/MODULE.bazel
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
module(
|
||||||
|
name = "google_benchmark",
|
||||||
|
version = "1.8.4",
|
||||||
|
)
|
||||||
|
|
||||||
|
bazel_dep(name = "bazel_skylib", version = "1.5.0")
|
||||||
|
bazel_dep(name = "platforms", version = "0.0.8")
|
||||||
|
bazel_dep(name = "rules_foreign_cc", version = "0.10.1")
|
||||||
|
bazel_dep(name = "rules_cc", version = "0.0.9")
|
||||||
|
|
||||||
|
bazel_dep(name = "rules_python", version = "0.31.0", dev_dependency = True)
|
||||||
|
bazel_dep(name = "googletest", version = "1.12.1", dev_dependency = True, repo_name = "com_google_googletest")
|
||||||
|
|
||||||
|
bazel_dep(name = "libpfm", version = "4.11.0")
|
||||||
|
|
||||||
|
# Register a toolchain for Python 3.9 to be able to build numpy. Python
|
||||||
|
# versions >=3.10 are problematic.
|
||||||
|
# A second reason for this is to be able to build Python hermetically instead
|
||||||
|
# of relying on the changing default version from rules_python.
|
||||||
|
|
||||||
|
python = use_extension("@rules_python//python/extensions:python.bzl", "python", dev_dependency = True)
|
||||||
|
python.toolchain(python_version = "3.8")
|
||||||
|
python.toolchain(python_version = "3.9")
|
||||||
|
python.toolchain(python_version = "3.10")
|
||||||
|
python.toolchain(python_version = "3.11")
|
||||||
|
python.toolchain(
|
||||||
|
is_default = True,
|
||||||
|
python_version = "3.12",
|
||||||
|
)
|
||||||
|
|
||||||
|
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip", dev_dependency = True)
|
||||||
|
pip.parse(
|
||||||
|
hub_name = "tools_pip_deps",
|
||||||
|
python_version = "3.9",
|
||||||
|
requirements_lock = "//tools:requirements.txt",
|
||||||
|
)
|
||||||
|
use_repo(pip, "tools_pip_deps")
|
||||||
|
|
||||||
|
# -- bazel_dep definitions -- #
|
||||||
|
|
||||||
|
bazel_dep(name = "nanobind_bazel", version = "1.0.0", dev_dependency = True)
|
223
third_party/benchmark/README.md
vendored
Normal file
223
third_party/benchmark/README.md
vendored
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
# Benchmark
|
||||||
|
|
||||||
|
[![build-and-test](https://github.com/google/benchmark/workflows/build-and-test/badge.svg)](https://github.com/google/benchmark/actions?query=workflow%3Abuild-and-test)
|
||||||
|
[![bazel](https://github.com/google/benchmark/actions/workflows/bazel.yml/badge.svg)](https://github.com/google/benchmark/actions/workflows/bazel.yml)
|
||||||
|
[![pylint](https://github.com/google/benchmark/workflows/pylint/badge.svg)](https://github.com/google/benchmark/actions?query=workflow%3Apylint)
|
||||||
|
[![test-bindings](https://github.com/google/benchmark/workflows/test-bindings/badge.svg)](https://github.com/google/benchmark/actions?query=workflow%3Atest-bindings)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/google/benchmark/badge.svg)](https://coveralls.io/r/google/benchmark)
|
||||||
|
|
||||||
|
[![Discord](https://discordapp.com/api/guilds/1125694995928719494/widget.png?style=shield)](https://discord.gg/cz7UX7wKC2)
|
||||||
|
|
||||||
|
A library to benchmark code snippets, similar to unit tests. Example:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
static void BM_SomeFunction(benchmark::State& state) {
|
||||||
|
// Perform setup here
|
||||||
|
for (auto _ : state) {
|
||||||
|
// This code gets timed
|
||||||
|
SomeFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Register the function as a benchmark
|
||||||
|
BENCHMARK(BM_SomeFunction);
|
||||||
|
// Run the benchmark
|
||||||
|
BENCHMARK_MAIN();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
To get started, see [Requirements](#requirements) and
|
||||||
|
[Installation](#installation). See [Usage](#usage) for a full example and the
|
||||||
|
[User Guide](docs/user_guide.md) for a more comprehensive feature overview.
|
||||||
|
|
||||||
|
It may also help to read the [Google Test documentation](https://github.com/google/googletest/blob/main/docs/primer.md)
|
||||||
|
as some of the structural aspects of the APIs are similar.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
[Discussion group](https://groups.google.com/d/forum/benchmark-discuss)
|
||||||
|
|
||||||
|
IRC channels:
|
||||||
|
* [libera](https://libera.chat) #benchmark
|
||||||
|
|
||||||
|
[Additional Tooling Documentation](docs/tools.md)
|
||||||
|
|
||||||
|
[Assembly Testing Documentation](docs/AssemblyTests.md)
|
||||||
|
|
||||||
|
[Building and installing Python bindings](docs/python_bindings.md)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
The library can be used with C++03. However, it requires C++11 to build,
|
||||||
|
including compiler and standard library support.
|
||||||
|
|
||||||
|
The following minimum versions are required to build the library:
|
||||||
|
|
||||||
|
* GCC 4.8
|
||||||
|
* Clang 3.4
|
||||||
|
* Visual Studio 14 2015
|
||||||
|
* Intel 2015 Update 1
|
||||||
|
|
||||||
|
See [Platform-Specific Build Instructions](docs/platform_specific_build_instructions.md).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This describes the installation process using cmake. As pre-requisites, you'll
|
||||||
|
need git and cmake installed.
|
||||||
|
|
||||||
|
_See [dependencies.md](docs/dependencies.md) for more details regarding supported
|
||||||
|
versions of build tools._
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check out the library.
|
||||||
|
$ git clone https://github.com/google/benchmark.git
|
||||||
|
# Go to the library root directory
|
||||||
|
$ cd benchmark
|
||||||
|
# Make a build directory to place the build output.
|
||||||
|
$ cmake -E make_directory "build"
|
||||||
|
# Generate build system files with cmake, and download any dependencies.
|
||||||
|
$ cmake -E chdir "build" cmake -DBENCHMARK_DOWNLOAD_DEPENDENCIES=on -DCMAKE_BUILD_TYPE=Release ../
|
||||||
|
# or, starting with CMake 3.13, use a simpler form:
|
||||||
|
# cmake -DCMAKE_BUILD_TYPE=Release -S . -B "build"
|
||||||
|
# Build the library.
|
||||||
|
$ cmake --build "build" --config Release
|
||||||
|
```
|
||||||
|
This builds the `benchmark` and `benchmark_main` libraries and tests.
|
||||||
|
On a unix system, the build directory should now look something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
/benchmark
|
||||||
|
/build
|
||||||
|
/src
|
||||||
|
/libbenchmark.a
|
||||||
|
/libbenchmark_main.a
|
||||||
|
/test
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, you can run the tests to check the build.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cmake -E chdir "build" ctest --build-config Release
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to install the library globally, also run:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo cmake --build "build" --config Release --target install
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that Google Benchmark requires Google Test to build and run the tests. This
|
||||||
|
dependency can be provided two ways:
|
||||||
|
|
||||||
|
* Checkout the Google Test sources into `benchmark/googletest`.
|
||||||
|
* Otherwise, if `-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON` is specified during
|
||||||
|
configuration as above, the library will automatically download and build
|
||||||
|
any required dependencies.
|
||||||
|
|
||||||
|
If you do not wish to build and run the tests, add `-DBENCHMARK_ENABLE_GTEST_TESTS=OFF`
|
||||||
|
to `CMAKE_ARGS`.
|
||||||
|
|
||||||
|
### Debug vs Release
|
||||||
|
|
||||||
|
By default, benchmark builds as a debug library. You will see a warning in the
|
||||||
|
output when this is the case. To build it as a release library instead, add
|
||||||
|
`-DCMAKE_BUILD_TYPE=Release` when generating the build system files, as shown
|
||||||
|
above. The use of `--config Release` in build commands is needed to properly
|
||||||
|
support multi-configuration tools (like Visual Studio for example) and can be
|
||||||
|
skipped for other build systems (like Makefile).
|
||||||
|
|
||||||
|
To enable link-time optimisation, also add `-DBENCHMARK_ENABLE_LTO=true` when
|
||||||
|
generating the build system files.
|
||||||
|
|
||||||
|
If you are using gcc, you might need to set `GCC_AR` and `GCC_RANLIB` cmake
|
||||||
|
cache variables, if autodetection fails.
|
||||||
|
|
||||||
|
If you are using clang, you may need to set `LLVMAR_EXECUTABLE`,
|
||||||
|
`LLVMNM_EXECUTABLE` and `LLVMRANLIB_EXECUTABLE` cmake cache variables.
|
||||||
|
|
||||||
|
To enable sanitizer checks (eg., `asan` and `tsan`), add:
|
||||||
|
```
|
||||||
|
-DCMAKE_C_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=address -fsanitize=thread -fno-sanitize-recover=all"
|
||||||
|
-DCMAKE_CXX_FLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=address -fsanitize=thread -fno-sanitize-recover=all "
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stable and Experimental Library Versions
|
||||||
|
|
||||||
|
The main branch contains the latest stable version of the benchmarking library;
|
||||||
|
the API of which can be considered largely stable, with source breaking changes
|
||||||
|
being made only upon the release of a new major version.
|
||||||
|
|
||||||
|
Newer, experimental, features are implemented and tested on the
|
||||||
|
[`v2` branch](https://github.com/google/benchmark/tree/v2). Users who wish
|
||||||
|
to use, test, and provide feedback on the new features are encouraged to try
|
||||||
|
this branch. However, this branch provides no stability guarantees and reserves
|
||||||
|
the right to change and break the API at any time.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic usage
|
||||||
|
|
||||||
|
Define a function that executes the code to measure, register it as a benchmark
|
||||||
|
function using the `BENCHMARK` macro, and ensure an appropriate `main` function
|
||||||
|
is available:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
static void BM_StringCreation(benchmark::State& state) {
|
||||||
|
for (auto _ : state)
|
||||||
|
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";
|
||||||
|
for (auto _ : state)
|
||||||
|
std::string copy(x);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_StringCopy);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the benchmark, compile and link against the `benchmark` library
|
||||||
|
(libbenchmark.a/.so). If you followed the build steps above, this library will
|
||||||
|
be under the build directory you created.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example on linux after running the build steps above. Assumes the
|
||||||
|
# `benchmark` and `build` directories are under the current directory.
|
||||||
|
$ g++ mybenchmark.cc -std=c++11 -isystem benchmark/include \
|
||||||
|
-Lbenchmark/build/src -lbenchmark -lpthread -o mybenchmark
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, link against the `benchmark_main` library and remove
|
||||||
|
`BENCHMARK_MAIN();` above to get the same behavior.
|
||||||
|
|
||||||
|
The compiled executable will run all benchmarks by default. Pass the `--help`
|
||||||
|
flag for option information or see the [User Guide](docs/user_guide.md).
|
||||||
|
|
||||||
|
### Usage with CMake
|
||||||
|
|
||||||
|
If using CMake, it is recommended to link against the project-provided
|
||||||
|
`benchmark::benchmark` and `benchmark::benchmark_main` targets using
|
||||||
|
`target_link_libraries`.
|
||||||
|
It is possible to use ```find_package``` to import an installed version of the
|
||||||
|
library.
|
||||||
|
```cmake
|
||||||
|
find_package(benchmark REQUIRED)
|
||||||
|
```
|
||||||
|
Alternatively, ```add_subdirectory``` will incorporate the library directly in
|
||||||
|
to one's CMake project.
|
||||||
|
```cmake
|
||||||
|
add_subdirectory(benchmark)
|
||||||
|
```
|
||||||
|
Either way, link to the library as follows.
|
||||||
|
```cmake
|
||||||
|
target_link_libraries(MyTarget benchmark::benchmark)
|
||||||
|
```
|
24
third_party/benchmark/WORKSPACE
vendored
Normal file
24
third_party/benchmark/WORKSPACE
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
workspace(name = "com_github_google_benchmark")
|
||||||
|
|
||||||
|
load("//:bazel/benchmark_deps.bzl", "benchmark_deps")
|
||||||
|
|
||||||
|
benchmark_deps()
|
||||||
|
|
||||||
|
load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies")
|
||||||
|
|
||||||
|
rules_foreign_cc_dependencies()
|
||||||
|
|
||||||
|
load("@rules_python//python:repositories.bzl", "py_repositories")
|
||||||
|
|
||||||
|
py_repositories()
|
||||||
|
|
||||||
|
load("@rules_python//python:pip.bzl", "pip_parse")
|
||||||
|
|
||||||
|
pip_parse(
|
||||||
|
name = "tools_pip_deps",
|
||||||
|
requirements_lock = "//tools:requirements.txt",
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@tools_pip_deps//:requirements.bzl", "install_deps")
|
||||||
|
|
||||||
|
install_deps()
|
2
third_party/benchmark/WORKSPACE.bzlmod
vendored
Normal file
2
third_party/benchmark/WORKSPACE.bzlmod
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# This file marks the root of the Bazel workspace.
|
||||||
|
# See MODULE.bazel for dependencies and setup.
|
2
third_party/benchmark/_config.yml
vendored
Normal file
2
third_party/benchmark/_config.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
theme: jekyll-theme-midnight
|
||||||
|
markdown: GFM
|
50
third_party/benchmark/appveyor.yml
vendored
Normal file
50
third_party/benchmark/appveyor.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
version: '{build}'
|
||||||
|
|
||||||
|
image: Visual Studio 2017
|
||||||
|
|
||||||
|
configuration:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- compiler: msvc-15-seh
|
||||||
|
generator: "Visual Studio 15 2017"
|
||||||
|
|
||||||
|
- compiler: msvc-15-seh
|
||||||
|
generator: "Visual Studio 15 2017 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'
|
||||||
|
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||||
|
|
||||||
|
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%")
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- md _build -Force
|
||||||
|
- cd _build
|
||||||
|
- echo %configuration%
|
||||||
|
- cmake -G "%generator%" "-DCMAKE_BUILD_TYPE=%configuration%" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON ..
|
||||||
|
- cmake --build . --config %configuration%
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- ctest --build-config %configuration% --timeout 300 --output-on-failure
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: '_build/CMakeFiles/*.log'
|
||||||
|
name: logs
|
||||||
|
- path: '_build/Testing/**/*.xml'
|
||||||
|
name: test_results
|
78
third_party/benchmark/cmake/AddCXXCompilerFlag.cmake
vendored
Normal file
78
third_party/benchmark/cmake/AddCXXCompilerFlag.cmake
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# - 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(mangle_compiler_flag FLAG OUTPUT)
|
||||||
|
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(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE)
|
||||||
|
endfunction(mangle_compiler_flag)
|
||||||
|
|
||||||
|
function(add_cxx_compiler_flag FLAG)
|
||||||
|
mangle_compiler_flag("${FLAG}" MANGLED_FLAG)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
|
||||||
|
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
|
if(${MANGLED_FLAG})
|
||||||
|
if(ARGC GREATER 1)
|
||||||
|
set(VARIANT ${ARGV1})
|
||||||
|
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||||
|
else()
|
||||||
|
set(VARIANT "")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${BENCHMARK_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_required_cxx_compiler_flag FLAG)
|
||||||
|
mangle_compiler_flag("${FLAG}" MANGLED_FLAG)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
|
||||||
|
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
|
if(${MANGLED_FLAG})
|
||||||
|
if(ARGC GREATER 1)
|
||||||
|
set(VARIANT ${ARGV1})
|
||||||
|
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||||
|
else()
|
||||||
|
set(VARIANT "")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(check_cxx_warning_flag FLAG)
|
||||||
|
mangle_compiler_flag("${FLAG}" MANGLED_FLAG)
|
||||||
|
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||||
|
# Add -Werror to ensure the compiler generates an error if the warning flag
|
||||||
|
# doesn't exist.
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror ${FLAG}")
|
||||||
|
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
|
endfunction()
|
82
third_party/benchmark/cmake/CXXFeatureCheck.cmake
vendored
Normal file
82
third_party/benchmark/cmake/CXXFeatureCheck.cmake
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# - 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.8.12+
|
||||||
|
|
||||||
|
if(__cxx_feature_check)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__cxx_feature_check INCLUDED)
|
||||||
|
|
||||||
|
option(CXXFEATURECHECK_DEBUG OFF)
|
||||||
|
|
||||||
|
function(cxx_feature_check FILE)
|
||||||
|
string(TOLOWER ${FILE} FILE)
|
||||||
|
string(TOUPPER ${FILE} VAR)
|
||||||
|
string(TOUPPER "HAVE_${VAR}" FEATURE)
|
||||||
|
if (DEFINED HAVE_${VAR})
|
||||||
|
set(HAVE_${VAR} 1 PARENT_SCOPE)
|
||||||
|
add_definitions(-DHAVE_${VAR})
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(FEATURE_CHECK_CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
|
||||||
|
if (ARGC GREATER 1)
|
||||||
|
message(STATUS "Enabling additional flags: ${ARGV1}")
|
||||||
|
list(APPEND FEATURE_CHECK_CMAKE_FLAGS ${ARGV1})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT DEFINED COMPILE_${FEATURE})
|
||||||
|
if(CMAKE_CROSSCOMPILING)
|
||||||
|
message(STATUS "Cross-compiling to test ${FEATURE}")
|
||||||
|
try_compile(COMPILE_${FEATURE}
|
||||||
|
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
||||||
|
CXX_STANDARD 11
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CMAKE_FLAGS ${FEATURE_CHECK_CMAKE_FLAGS}
|
||||||
|
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}
|
||||||
|
OUTPUT_VARIABLE COMPILE_OUTPUT_VAR)
|
||||||
|
if(COMPILE_${FEATURE})
|
||||||
|
message(WARNING
|
||||||
|
"If you see build failures due to cross compilation, try setting HAVE_${VAR} to 0")
|
||||||
|
set(RUN_${FEATURE} 0 CACHE INTERNAL "")
|
||||||
|
else()
|
||||||
|
set(RUN_${FEATURE} 1 CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "Compiling and running to test ${FEATURE}")
|
||||||
|
try_run(RUN_${FEATURE} COMPILE_${FEATURE}
|
||||||
|
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
||||||
|
CXX_STANDARD 11
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CMAKE_FLAGS ${FEATURE_CHECK_CMAKE_FLAGS}
|
||||||
|
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}
|
||||||
|
COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_VAR)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(RUN_${FEATURE} EQUAL 0)
|
||||||
|
message(STATUS "Performing Test ${FEATURE} -- success")
|
||||||
|
set(HAVE_${VAR} 1 PARENT_SCOPE)
|
||||||
|
add_definitions(-DHAVE_${VAR})
|
||||||
|
else()
|
||||||
|
if(NOT COMPILE_${FEATURE})
|
||||||
|
if(CXXFEATURECHECK_DEBUG)
|
||||||
|
message(STATUS "Performing Test ${FEATURE} -- failed to compile: ${COMPILE_OUTPUT_VAR}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Performing Test ${FEATURE} -- failed to compile")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "Performing Test ${FEATURE} -- compiled but failed to run")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
7
third_party/benchmark/cmake/Config.cmake.in
vendored
Normal file
7
third_party/benchmark/cmake/Config.cmake.in
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include (CMakeFindDependencyMacro)
|
||||||
|
|
||||||
|
find_dependency (Threads)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
36
third_party/benchmark/cmake/GetGitVersion.cmake
vendored
Normal file
36
third_party/benchmark/cmake/GetGitVersion.cmake
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# - 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 --tags --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8 --dirty
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
RESULT_VARIABLE status
|
||||||
|
OUTPUT_VARIABLE GIT_VERSION
|
||||||
|
ERROR_QUIET)
|
||||||
|
if(status)
|
||||||
|
set(GIT_VERSION "v0.0.0")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(GIT_VERSION "v0.0.0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${var} ${GIT_VERSION} PARENT_SCOPE)
|
||||||
|
endfunction()
|
58
third_party/benchmark/cmake/GoogleTest.cmake
vendored
Normal file
58
third_party/benchmark/cmake/GoogleTest.cmake
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Download and unpack googletest at configure time
|
||||||
|
set(GOOGLETEST_PREFIX "${benchmark_BINARY_DIR}/third_party/googletest")
|
||||||
|
configure_file(${benchmark_SOURCE_DIR}/cmake/GoogleTest.cmake.in ${GOOGLETEST_PREFIX}/CMakeLists.txt @ONLY)
|
||||||
|
|
||||||
|
set(GOOGLETEST_PATH "${CMAKE_CURRENT_SOURCE_DIR}/googletest" CACHE PATH "") # Mind the quotes
|
||||||
|
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
|
||||||
|
-DALLOW_DOWNLOADING_GOOGLETEST=${BENCHMARK_DOWNLOAD_DEPENDENCIES} -DGOOGLETEST_PATH:PATH=${GOOGLETEST_PATH} .
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
WORKING_DIRECTORY ${GOOGLETEST_PREFIX}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(result)
|
||||||
|
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} --build .
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
WORKING_DIRECTORY ${GOOGLETEST_PREFIX}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(result)
|
||||||
|
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Prevent overriding the parent project's compiler/linker
|
||||||
|
# settings on Windows
|
||||||
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
include(${GOOGLETEST_PREFIX}/googletest-paths.cmake)
|
||||||
|
|
||||||
|
# Add googletest directly to our build. This defines
|
||||||
|
# the gtest and gtest_main targets.
|
||||||
|
add_subdirectory(${GOOGLETEST_SOURCE_DIR}
|
||||||
|
${GOOGLETEST_BINARY_DIR}
|
||||||
|
EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
# googletest doesn't seem to want to stay build warning clean so let's not hurt ourselves.
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(gtest PRIVATE "/wd4244" "/wd4722")
|
||||||
|
target_compile_options(gtest_main PRIVATE "/wd4244" "/wd4722")
|
||||||
|
target_compile_options(gmock PRIVATE "/wd4244" "/wd4722")
|
||||||
|
target_compile_options(gmock_main PRIVATE "/wd4244" "/wd4722")
|
||||||
|
else()
|
||||||
|
target_compile_options(gtest PRIVATE "-w")
|
||||||
|
target_compile_options(gtest_main PRIVATE "-w")
|
||||||
|
target_compile_options(gmock PRIVATE "-w")
|
||||||
|
target_compile_options(gmock_main PRIVATE "-w")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED GTEST_COMPILE_COMMANDS)
|
||||||
|
set(GTEST_COMPILE_COMMANDS ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(gtest PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gtest,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
||||||
|
set_target_properties(gtest_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gtest_main,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
||||||
|
set_target_properties(gmock PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gmock,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
||||||
|
set_target_properties(gmock_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gmock_main,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
59
third_party/benchmark/cmake/GoogleTest.cmake.in
vendored
Normal file
59
third_party/benchmark/cmake/GoogleTest.cmake.in
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8.12)
|
||||||
|
|
||||||
|
project(googletest-download NONE)
|
||||||
|
|
||||||
|
# Enable ExternalProject CMake module
|
||||||
|
include(ExternalProject)
|
||||||
|
|
||||||
|
option(ALLOW_DOWNLOADING_GOOGLETEST "If googletest src tree is not found in location specified by GOOGLETEST_PATH, do fetch the archive from internet" OFF)
|
||||||
|
set(GOOGLETEST_PATH "/usr/src/googletest" CACHE PATH
|
||||||
|
"Path to the googletest root tree. Should contain googletest and googlemock subdirs. And CMakeLists.txt in root, and in both of these subdirs")
|
||||||
|
|
||||||
|
# Download and install GoogleTest
|
||||||
|
|
||||||
|
message(STATUS "Looking for Google Test sources")
|
||||||
|
message(STATUS "Looking for Google Test sources in ${GOOGLETEST_PATH}")
|
||||||
|
if(EXISTS "${GOOGLETEST_PATH}" AND IS_DIRECTORY "${GOOGLETEST_PATH}" AND EXISTS "${GOOGLETEST_PATH}/CMakeLists.txt" AND
|
||||||
|
EXISTS "${GOOGLETEST_PATH}/googletest" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googletest" AND EXISTS "${GOOGLETEST_PATH}/googletest/CMakeLists.txt" AND
|
||||||
|
EXISTS "${GOOGLETEST_PATH}/googlemock" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googlemock" AND EXISTS "${GOOGLETEST_PATH}/googlemock/CMakeLists.txt")
|
||||||
|
message(STATUS "Found Google Test in ${GOOGLETEST_PATH}")
|
||||||
|
|
||||||
|
ExternalProject_Add(
|
||||||
|
googletest
|
||||||
|
PREFIX "${CMAKE_BINARY_DIR}"
|
||||||
|
DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download"
|
||||||
|
SOURCE_DIR "${GOOGLETEST_PATH}" # use existing src dir.
|
||||||
|
BINARY_DIR "${CMAKE_BINARY_DIR}/build"
|
||||||
|
CONFIGURE_COMMAND ""
|
||||||
|
BUILD_COMMAND ""
|
||||||
|
INSTALL_COMMAND ""
|
||||||
|
TEST_COMMAND ""
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
if(NOT ALLOW_DOWNLOADING_GOOGLETEST)
|
||||||
|
message(SEND_ERROR "Did not find Google Test sources! Either pass correct path in GOOGLETEST_PATH, or enable BENCHMARK_DOWNLOAD_DEPENDENCIES, or disable BENCHMARK_USE_BUNDLED_GTEST, or disable BENCHMARK_ENABLE_GTEST_TESTS / BENCHMARK_ENABLE_TESTING.")
|
||||||
|
return()
|
||||||
|
else()
|
||||||
|
message(WARNING "Did not find Google Test sources! Fetching from web...")
|
||||||
|
ExternalProject_Add(
|
||||||
|
googletest
|
||||||
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||||
|
GIT_TAG "release-1.11.0"
|
||||||
|
PREFIX "${CMAKE_BINARY_DIR}"
|
||||||
|
STAMP_DIR "${CMAKE_BINARY_DIR}/stamp"
|
||||||
|
DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download"
|
||||||
|
SOURCE_DIR "${CMAKE_BINARY_DIR}/src"
|
||||||
|
BINARY_DIR "${CMAKE_BINARY_DIR}/build"
|
||||||
|
CONFIGURE_COMMAND ""
|
||||||
|
BUILD_COMMAND ""
|
||||||
|
INSTALL_COMMAND ""
|
||||||
|
TEST_COMMAND ""
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
ExternalProject_Get_Property(googletest SOURCE_DIR BINARY_DIR)
|
||||||
|
file(WRITE googletest-paths.cmake
|
||||||
|
"set(GOOGLETEST_SOURCE_DIR \"${SOURCE_DIR}\")
|
||||||
|
set(GOOGLETEST_BINARY_DIR \"${BINARY_DIR}\")
|
||||||
|
")
|
12
third_party/benchmark/cmake/benchmark.pc.in
vendored
Normal file
12
third_party/benchmark/cmake/benchmark.pc.in
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
exec_prefix=${prefix}
|
||||||
|
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||||
|
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||||
|
|
||||||
|
Name: @PROJECT_NAME@
|
||||||
|
Description: Google microbenchmark framework
|
||||||
|
Version: @VERSION@
|
||||||
|
|
||||||
|
Libs: -L${libdir} -lbenchmark
|
||||||
|
Libs.private: -lpthread
|
||||||
|
Cflags: -I${includedir}
|
7
third_party/benchmark/cmake/benchmark_main.pc.in
vendored
Normal file
7
third_party/benchmark/cmake/benchmark_main.pc.in
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||||
|
|
||||||
|
Name: @PROJECT_NAME@
|
||||||
|
Description: Google microbenchmark framework (with main() function)
|
||||||
|
Version: @VERSION@
|
||||||
|
Requires: benchmark
|
||||||
|
Libs: -L${libdir} -lbenchmark_main
|
12
third_party/benchmark/cmake/gnu_posix_regex.cpp
vendored
Normal file
12
third_party/benchmark/cmake/gnu_posix_regex.cpp
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
8
third_party/benchmark/cmake/llvm-toolchain.cmake
vendored
Normal file
8
third_party/benchmark/cmake/llvm-toolchain.cmake
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
find_package(LLVMAr REQUIRED)
|
||||||
|
set(CMAKE_AR "${LLVMAR_EXECUTABLE}" CACHE FILEPATH "" FORCE)
|
||||||
|
|
||||||
|
find_package(LLVMNm REQUIRED)
|
||||||
|
set(CMAKE_NM "${LLVMNM_EXECUTABLE}" CACHE FILEPATH "" FORCE)
|
||||||
|
|
||||||
|
find_package(LLVMRanLib REQUIRED)
|
||||||
|
set(CMAKE_RANLIB "${LLVMRANLIB_EXECUTABLE}" CACHE FILEPATH "" FORCE)
|
14
third_party/benchmark/cmake/posix_regex.cpp
vendored
Normal file
14
third_party/benchmark/cmake/posix_regex.cpp
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
16
third_party/benchmark/cmake/pthread_affinity.cpp
vendored
Normal file
16
third_party/benchmark/cmake/pthread_affinity.cpp
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
int main() {
|
||||||
|
cpu_set_t set;
|
||||||
|
CPU_ZERO(&set);
|
||||||
|
for (int i = 0; i < CPU_SETSIZE; ++i) {
|
||||||
|
CPU_SET(i, &set);
|
||||||
|
CPU_CLR(i, &set);
|
||||||
|
}
|
||||||
|
pthread_t self = pthread_self();
|
||||||
|
int ret;
|
||||||
|
ret = pthread_getaffinity_np(self, sizeof(set), &set);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
ret = pthread_setaffinity_np(self, sizeof(set), &set);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
3
third_party/benchmark/cmake/split_list.cmake
vendored
Normal file
3
third_party/benchmark/cmake/split_list.cmake
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
macro(split_list listname)
|
||||||
|
string(REPLACE ";" " " ${listname} "${${listname}}")
|
||||||
|
endmacro()
|
10
third_party/benchmark/cmake/std_regex.cpp
vendored
Normal file
10
third_party/benchmark/cmake/std_regex.cpp
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
7
third_party/benchmark/cmake/steady_clock.cpp
vendored
Normal file
7
third_party/benchmark/cmake/steady_clock.cpp
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
typedef std::chrono::steady_clock Clock;
|
||||||
|
Clock::time_point tp = Clock::now();
|
||||||
|
((void)tp);
|
||||||
|
}
|
4
third_party/benchmark/cmake/thread_safety_attributes.cpp
vendored
Normal file
4
third_party/benchmark/cmake/thread_safety_attributes.cpp
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#define HAVE_THREAD_SAFETY_ATTRIBUTES
|
||||||
|
#include "../src/mutex.h"
|
||||||
|
|
||||||
|
int main() {}
|
2040
third_party/benchmark/include/benchmark/benchmark.h
vendored
Normal file
2040
third_party/benchmark/include/benchmark/benchmark.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
47
third_party/benchmark/include/benchmark/export.h
vendored
Normal file
47
third_party/benchmark/include/benchmark/export.h
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef BENCHMARK_EXPORT_H
|
||||||
|
#define BENCHMARK_EXPORT_H
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define EXPORT_ATTR __declspec(dllexport)
|
||||||
|
#define IMPORT_ATTR __declspec(dllimport)
|
||||||
|
#define NO_EXPORT_ATTR
|
||||||
|
#define DEPRECATED_ATTR __declspec(deprecated)
|
||||||
|
#else // _WIN32
|
||||||
|
#define EXPORT_ATTR __attribute__((visibility("default")))
|
||||||
|
#define IMPORT_ATTR __attribute__((visibility("default")))
|
||||||
|
#define NO_EXPORT_ATTR __attribute__((visibility("hidden")))
|
||||||
|
#define DEPRECATE_ATTR __attribute__((__deprecated__))
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_STATIC_DEFINE
|
||||||
|
#define BENCHMARK_EXPORT
|
||||||
|
#define BENCHMARK_NO_EXPORT
|
||||||
|
#else // BENCHMARK_STATIC_DEFINE
|
||||||
|
#ifndef BENCHMARK_EXPORT
|
||||||
|
#ifdef benchmark_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
#define BENCHMARK_EXPORT EXPORT_ATTR
|
||||||
|
#else // benchmark_EXPORTS
|
||||||
|
/* We are using this library */
|
||||||
|
#define BENCHMARK_EXPORT IMPORT_ATTR
|
||||||
|
#endif // benchmark_EXPORTS
|
||||||
|
#endif // !BENCHMARK_EXPORT
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_NO_EXPORT
|
||||||
|
#define BENCHMARK_NO_EXPORT NO_EXPORT_ATTR
|
||||||
|
#endif // !BENCHMARK_NO_EXPORT
|
||||||
|
#endif // BENCHMARK_STATIC_DEFINE
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_DEPRECATED
|
||||||
|
#define BENCHMARK_DEPRECATED DEPRECATE_ATTR
|
||||||
|
#endif // BENCHMARK_DEPRECATED
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_DEPRECATED_EXPORT
|
||||||
|
#define BENCHMARK_DEPRECATED_EXPORT BENCHMARK_EXPORT BENCHMARK_DEPRECATED
|
||||||
|
#endif // BENCHMARK_DEPRECATED_EXPORT
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_DEPRECATED_NO_EXPORT
|
||||||
|
#define BENCHMARK_DEPRECATED_NO_EXPORT BENCHMARK_NO_EXPORT BENCHMARK_DEPRECATED
|
||||||
|
#endif // BENCHMARK_DEPRECATED_EXPORT
|
||||||
|
|
||||||
|
#endif /* BENCHMARK_EXPORT_H */
|
86
third_party/benchmark/pyproject.toml
vendored
Normal file
86
third_party/benchmark/pyproject.toml
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools", "setuptools-scm[toml]", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "google_benchmark"
|
||||||
|
description = "A library to benchmark code snippets."
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
license = {file = "LICENSE"}
|
||||||
|
keywords = ["benchmark"]
|
||||||
|
|
||||||
|
authors = [
|
||||||
|
{name = "Google", email = "benchmark-discuss@googlegroups.com"},
|
||||||
|
]
|
||||||
|
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 4 - Beta",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Intended Audience :: Science/Research",
|
||||||
|
"License :: OSI Approved :: Apache Software License",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Topic :: Software Development :: Testing",
|
||||||
|
"Topic :: System :: Benchmark",
|
||||||
|
]
|
||||||
|
|
||||||
|
dynamic = ["readme", "version"]
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
"absl-py>=0.7.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pre-commit>=3.3.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://github.com/google/benchmark"
|
||||||
|
Documentation = "https://github.com/google/benchmark/tree/main/docs"
|
||||||
|
Repository = "https://github.com/google/benchmark.git"
|
||||||
|
Discord = "https://discord.gg/cz7UX7wKC2"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
package-dir = {"" = "bindings/python"}
|
||||||
|
zip-safe = false
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["bindings/python"]
|
||||||
|
|
||||||
|
[tool.setuptools.dynamic]
|
||||||
|
readme = { file = "README.md", content-type = "text/markdown" }
|
||||||
|
|
||||||
|
[tool.setuptools_scm]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
check_untyped_defs = true
|
||||||
|
disallow_incomplete_defs = true
|
||||||
|
pretty = true
|
||||||
|
python_version = "3.11"
|
||||||
|
strict_optional = false
|
||||||
|
warn_unreachable = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = ["yaml"]
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
# explicitly tell ruff the source directory to correctly identify first-party package.
|
||||||
|
src = ["bindings/python"]
|
||||||
|
|
||||||
|
line-length = 80
|
||||||
|
target-version = "py311"
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
# Enable pycodestyle (`E`, `W`), Pyflakes (`F`), and isort (`I`) codes by default.
|
||||||
|
select = ["E", "F", "I", "W"]
|
||||||
|
ignore = [
|
||||||
|
"E501", # line too long
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint.isort]
|
||||||
|
combine-as-imports = true
|
148
third_party/benchmark/setup.py
vendored
Normal file
148
third_party/benchmark/setup.py
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import contextlib
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Generator
|
||||||
|
|
||||||
|
import setuptools
|
||||||
|
from setuptools.command import build_ext
|
||||||
|
|
||||||
|
IS_WINDOWS = platform.system() == "Windows"
|
||||||
|
IS_MAC = platform.system() == "Darwin"
|
||||||
|
IS_LINUX = platform.system() == "Linux"
|
||||||
|
|
||||||
|
# hardcoded SABI-related options. Requires that each Python interpreter
|
||||||
|
# (hermetic or not) participating is of the same major-minor version.
|
||||||
|
version_tuple = tuple(int(i) for i in platform.python_version_tuple())
|
||||||
|
py_limited_api = version_tuple >= (3, 12)
|
||||||
|
options = {"bdist_wheel": {"py_limited_api": "cp312"}} if py_limited_api else {}
|
||||||
|
|
||||||
|
|
||||||
|
def is_cibuildwheel() -> bool:
|
||||||
|
return os.getenv("CIBUILDWHEEL") is not None
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def _maybe_patch_toolchains() -> Generator[None, None, None]:
|
||||||
|
"""
|
||||||
|
Patch rules_python toolchains to ignore root user error
|
||||||
|
when run in a Docker container on Linux in cibuildwheel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def fmt_toolchain_args(matchobj):
|
||||||
|
suffix = "ignore_root_user_error = True"
|
||||||
|
callargs = matchobj.group(1)
|
||||||
|
# toolchain def is broken over multiple lines
|
||||||
|
if callargs.endswith("\n"):
|
||||||
|
callargs = callargs + " " + suffix + ",\n"
|
||||||
|
# toolchain def is on one line.
|
||||||
|
else:
|
||||||
|
callargs = callargs + ", " + suffix
|
||||||
|
return "python.toolchain(" + callargs + ")"
|
||||||
|
|
||||||
|
CIBW_LINUX = is_cibuildwheel() and IS_LINUX
|
||||||
|
try:
|
||||||
|
if CIBW_LINUX:
|
||||||
|
module_bazel = Path("MODULE.bazel")
|
||||||
|
content: str = module_bazel.read_text()
|
||||||
|
module_bazel.write_text(
|
||||||
|
re.sub(
|
||||||
|
r"python.toolchain\(([\w\"\s,.=]*)\)",
|
||||||
|
fmt_toolchain_args,
|
||||||
|
content,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
if CIBW_LINUX:
|
||||||
|
module_bazel.write_text(content)
|
||||||
|
|
||||||
|
|
||||||
|
class BazelExtension(setuptools.Extension):
|
||||||
|
"""A C/C++ extension that is defined as a Bazel BUILD target."""
|
||||||
|
|
||||||
|
def __init__(self, name: str, bazel_target: str, **kwargs: Any):
|
||||||
|
super().__init__(name=name, sources=[], **kwargs)
|
||||||
|
|
||||||
|
self.bazel_target = bazel_target
|
||||||
|
stripped_target = bazel_target.split("//")[-1]
|
||||||
|
self.relpath, self.target_name = stripped_target.split(":")
|
||||||
|
|
||||||
|
|
||||||
|
class BuildBazelExtension(build_ext.build_ext):
|
||||||
|
"""A command that runs Bazel to build a C/C++ extension."""
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for ext in self.extensions:
|
||||||
|
self.bazel_build(ext)
|
||||||
|
super().run()
|
||||||
|
# explicitly call `bazel shutdown` for graceful exit
|
||||||
|
self.spawn(["bazel", "shutdown"])
|
||||||
|
|
||||||
|
def copy_extensions_to_source(self):
|
||||||
|
"""
|
||||||
|
Copy generated extensions into the source tree.
|
||||||
|
This is done in the ``bazel_build`` method, so it's not necessary to
|
||||||
|
do again in the `build_ext` base class.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bazel_build(self, ext: BazelExtension) -> None:
|
||||||
|
"""Runs the bazel build to create the package."""
|
||||||
|
temp_path = Path(self.build_temp)
|
||||||
|
# omit the patch version to avoid build errors if the toolchain is not
|
||||||
|
# yet registered in the current @rules_python version.
|
||||||
|
# patch version differences should be fine.
|
||||||
|
python_version = ".".join(platform.python_version_tuple()[:2])
|
||||||
|
|
||||||
|
bazel_argv = [
|
||||||
|
"bazel",
|
||||||
|
"build",
|
||||||
|
ext.bazel_target,
|
||||||
|
f"--symlink_prefix={temp_path / 'bazel-'}",
|
||||||
|
f"--compilation_mode={'dbg' if self.debug else 'opt'}",
|
||||||
|
# C++17 is required by nanobind
|
||||||
|
f"--cxxopt={'/std:c++17' if IS_WINDOWS else '-std=c++17'}",
|
||||||
|
f"--@rules_python//python/config_settings:python_version={python_version}",
|
||||||
|
]
|
||||||
|
|
||||||
|
if ext.py_limited_api:
|
||||||
|
bazel_argv += ["--@nanobind_bazel//:py-limited-api=cp312"]
|
||||||
|
|
||||||
|
if IS_WINDOWS:
|
||||||
|
# Link with python*.lib.
|
||||||
|
for library_dir in self.library_dirs:
|
||||||
|
bazel_argv.append("--linkopt=/LIBPATH:" + library_dir)
|
||||||
|
elif IS_MAC:
|
||||||
|
# C++17 needs macOS 10.14 at minimum
|
||||||
|
bazel_argv.append("--macos_minimum_os=10.14")
|
||||||
|
|
||||||
|
with _maybe_patch_toolchains():
|
||||||
|
self.spawn(bazel_argv)
|
||||||
|
|
||||||
|
if IS_WINDOWS:
|
||||||
|
suffix = ".pyd"
|
||||||
|
else:
|
||||||
|
suffix = ".abi3.so" if ext.py_limited_api else ".so"
|
||||||
|
|
||||||
|
ext_name = ext.target_name + suffix
|
||||||
|
ext_bazel_bin_path = temp_path / "bazel-bin" / ext.relpath / ext_name
|
||||||
|
ext_dest_path = Path(self.get_ext_fullpath(ext.name)).with_name(
|
||||||
|
ext_name
|
||||||
|
)
|
||||||
|
shutil.copyfile(ext_bazel_bin_path, ext_dest_path)
|
||||||
|
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
cmdclass=dict(build_ext=BuildBazelExtension),
|
||||||
|
ext_modules=[
|
||||||
|
BazelExtension(
|
||||||
|
name="google_benchmark._benchmark",
|
||||||
|
bazel_target="//bindings/python/google_benchmark:_benchmark",
|
||||||
|
py_limited_api=py_limited_api,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
options=options,
|
||||||
|
)
|
179
third_party/benchmark/src/CMakeLists.txt
vendored
Normal file
179
third_party/benchmark/src/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# Allow the source files to find headers in src/
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
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)
|
||||||
|
file(GLOB BENCHMARK_MAIN "benchmark_main.cc")
|
||||||
|
foreach(item ${BENCHMARK_MAIN})
|
||||||
|
list(REMOVE_ITEM SOURCE_FILES "${item}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_library(benchmark ${SOURCE_FILES})
|
||||||
|
add_library(benchmark::benchmark ALIAS benchmark)
|
||||||
|
set_target_properties(benchmark PROPERTIES
|
||||||
|
OUTPUT_NAME "benchmark"
|
||||||
|
VERSION ${GENERIC_LIB_VERSION}
|
||||||
|
SOVERSION ${GENERIC_LIB_SOVERSION}
|
||||||
|
)
|
||||||
|
target_include_directories(benchmark PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(
|
||||||
|
SOURCE benchmark.cc
|
||||||
|
APPEND
|
||||||
|
PROPERTY COMPILE_DEFINITIONS
|
||||||
|
BENCHMARK_VERSION="${VERSION}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# libpfm, if available
|
||||||
|
if (PFM_FOUND)
|
||||||
|
target_link_libraries(benchmark PRIVATE PFM::libpfm)
|
||||||
|
target_compile_definitions(benchmark PRIVATE -DHAVE_LIBPFM)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# pthread affinity, if available
|
||||||
|
if(HAVE_PTHREAD_AFFINITY)
|
||||||
|
target_compile_definitions(benchmark PRIVATE -DBENCHMARK_HAS_PTHREAD_AFFINITY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Link threads.
|
||||||
|
target_link_libraries(benchmark PRIVATE Threads::Threads)
|
||||||
|
|
||||||
|
target_link_libraries(benchmark PRIVATE ${BENCHMARK_CXX_LIBRARIES})
|
||||||
|
|
||||||
|
if(HAVE_LIB_RT)
|
||||||
|
target_link_libraries(benchmark PRIVATE rt)
|
||||||
|
endif(HAVE_LIB_RT)
|
||||||
|
|
||||||
|
|
||||||
|
# We need extra libraries on Windows
|
||||||
|
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||||
|
target_link_libraries(benchmark PRIVATE shlwapi)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# We need extra libraries on Solaris
|
||||||
|
if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
|
||||||
|
target_link_libraries(benchmark PRIVATE kstat)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT BUILD_SHARED_LIBS)
|
||||||
|
target_compile_definitions(benchmark PUBLIC -DBENCHMARK_STATIC_DEFINE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Benchmark main library
|
||||||
|
add_library(benchmark_main "benchmark_main.cc")
|
||||||
|
add_library(benchmark::benchmark_main ALIAS benchmark_main)
|
||||||
|
set_target_properties(benchmark_main PROPERTIES
|
||||||
|
OUTPUT_NAME "benchmark_main"
|
||||||
|
VERSION ${GENERIC_LIB_VERSION}
|
||||||
|
SOVERSION ${GENERIC_LIB_SOVERSION}
|
||||||
|
DEFINE_SYMBOL benchmark_EXPORTS
|
||||||
|
)
|
||||||
|
target_link_libraries(benchmark_main PUBLIC benchmark::benchmark)
|
||||||
|
|
||||||
|
set(generated_dir "${PROJECT_BINARY_DIR}")
|
||||||
|
|
||||||
|
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||||
|
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
|
||||||
|
set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
|
||||||
|
set(pkg_config_main "${generated_dir}/${PROJECT_NAME}_main.pc")
|
||||||
|
set(targets_to_export benchmark benchmark_main)
|
||||||
|
set(targets_export_name "${PROJECT_NAME}Targets")
|
||||||
|
|
||||||
|
set(namespace "${PROJECT_NAME}::")
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
configure_package_config_file (
|
||||||
|
${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in
|
||||||
|
${project_config}
|
||||||
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
||||||
|
NO_SET_AND_CHECK_MACRO
|
||||||
|
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||||
|
)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${version_config}" VERSION ${GENERIC_LIB_VERSION} COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/benchmark.pc.in" "${pkg_config}" @ONLY)
|
||||||
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/benchmark_main.pc.in" "${pkg_config_main}" @ONLY)
|
||||||
|
|
||||||
|
export (
|
||||||
|
TARGETS ${targets_to_export}
|
||||||
|
NAMESPACE "${namespace}"
|
||||||
|
FILE ${generated_dir}/${targets_export_name}.cmake
|
||||||
|
)
|
||||||
|
|
||||||
|
if (BENCHMARK_ENABLE_INSTALL)
|
||||||
|
# Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable)
|
||||||
|
install(
|
||||||
|
TARGETS ${targets_to_export}
|
||||||
|
EXPORT ${targets_export_name}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
|
|
||||||
|
install(
|
||||||
|
DIRECTORY "${PROJECT_SOURCE_DIR}/include/benchmark"
|
||||||
|
"${PROJECT_BINARY_DIR}/include/benchmark"
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
FILES_MATCHING PATTERN "*.*h")
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES "${project_config}" "${version_config}"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES "${pkg_config}" "${pkg_config_main}"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
|
|
||||||
|
install(
|
||||||
|
EXPORT "${targets_export_name}"
|
||||||
|
NAMESPACE "${namespace}"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (BENCHMARK_ENABLE_DOXYGEN)
|
||||||
|
find_package(Doxygen REQUIRED)
|
||||||
|
set(DOXYGEN_QUIET YES)
|
||||||
|
set(DOXYGEN_RECURSIVE YES)
|
||||||
|
set(DOXYGEN_GENERATE_HTML YES)
|
||||||
|
set(DOXYGEN_GENERATE_MAN NO)
|
||||||
|
set(DOXYGEN_MARKDOWN_SUPPORT YES)
|
||||||
|
set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
|
||||||
|
set(DOXYGEN_EXTRACT_PACKAGE YES)
|
||||||
|
set(DOXYGEN_EXTRACT_STATIC YES)
|
||||||
|
set(DOXYGEN_SHOW_INCLUDE_FILES YES)
|
||||||
|
set(DOXYGEN_BINARY_TOC YES)
|
||||||
|
set(DOXYGEN_TOC_EXPAND YES)
|
||||||
|
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "index.md")
|
||||||
|
doxygen_add_docs(benchmark_doxygen
|
||||||
|
docs
|
||||||
|
include
|
||||||
|
src
|
||||||
|
ALL
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMENT "Building documentation with Doxygen.")
|
||||||
|
if (BENCHMARK_ENABLE_INSTALL AND BENCHMARK_INSTALL_DOCS)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html/"
|
||||||
|
DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if (BENCHMARK_ENABLE_INSTALL AND BENCHMARK_INSTALL_DOCS)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${PROJECT_SOURCE_DIR}/docs/"
|
||||||
|
DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||||
|
endif()
|
||||||
|
endif()
|
33
third_party/benchmark/src/arraysize.h
vendored
Normal file
33
third_party/benchmark/src/arraysize.h
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#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_
|
808
third_party/benchmark/src/benchmark.cc
vendored
Normal file
808
third_party/benchmark/src/benchmark.cc
vendored
Normal file
@ -0,0 +1,808 @@
|
|||||||
|
// 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 "benchmark_runner.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_OS_WINDOWS
|
||||||
|
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <random>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "colorprint.h"
|
||||||
|
#include "commandlineflags.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#include "counter.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "perf_counters.h"
|
||||||
|
#include "re.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "thread_manager.h"
|
||||||
|
#include "thread_timer.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
// Print a list of benchmarks. This option overrides all other options.
|
||||||
|
BM_DEFINE_bool(benchmark_list_tests, false);
|
||||||
|
|
||||||
|
// A regular expression that specifies the set of benchmarks to execute. If
|
||||||
|
// this flag is empty, or if this flag is the string \"all\", all benchmarks
|
||||||
|
// linked into the binary are run.
|
||||||
|
BM_DEFINE_string(benchmark_filter, "");
|
||||||
|
|
||||||
|
// Specification of how long to run the benchmark.
|
||||||
|
//
|
||||||
|
// It can be either an exact number of iterations (specified as `<integer>x`),
|
||||||
|
// or a minimum number of seconds (specified as `<float>s`). If the latter
|
||||||
|
// format (ie., min seconds) is used, the system may run the benchmark longer
|
||||||
|
// until the results are considered significant.
|
||||||
|
//
|
||||||
|
// For backward compatibility, the `s` suffix may be omitted, in which case,
|
||||||
|
// the specified number is interpreted as the number of seconds.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
BM_DEFINE_string(benchmark_min_time, kDefaultMinTimeStr);
|
||||||
|
|
||||||
|
// Minimum number of seconds a benchmark should be run before results should be
|
||||||
|
// taken into account. This e.g can be necessary for benchmarks of code which
|
||||||
|
// needs to fill some form of cache before performance is of interest.
|
||||||
|
// Note: results gathered within this period are discarded and not used for
|
||||||
|
// reported result.
|
||||||
|
BM_DEFINE_double(benchmark_min_warmup_time, 0.0);
|
||||||
|
|
||||||
|
// The number of runs of each benchmark. If greater than 1, the mean and
|
||||||
|
// standard deviation of the runs will be reported.
|
||||||
|
BM_DEFINE_int32(benchmark_repetitions, 1);
|
||||||
|
|
||||||
|
// If set, enable random interleaving of repetitions of all benchmarks.
|
||||||
|
// See http://github.com/google/benchmark/issues/1051 for details.
|
||||||
|
BM_DEFINE_bool(benchmark_enable_random_interleaving, 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. Affects all reporters.
|
||||||
|
BM_DEFINE_bool(benchmark_report_aggregates_only, false);
|
||||||
|
|
||||||
|
// Display the result of each benchmark repetitions. When 'true' is specified
|
||||||
|
// only the mean, standard deviation, and other statistics are displayed for
|
||||||
|
// repeated benchmarks. Unlike benchmark_report_aggregates_only, only affects
|
||||||
|
// the display reporter, but *NOT* file reporter, which will still contain
|
||||||
|
// all the output.
|
||||||
|
BM_DEFINE_bool(benchmark_display_aggregates_only, false);
|
||||||
|
|
||||||
|
// The format to use for console output.
|
||||||
|
// Valid values are 'console', 'json', or 'csv'.
|
||||||
|
BM_DEFINE_string(benchmark_format, "console");
|
||||||
|
|
||||||
|
// The format to use for file output.
|
||||||
|
// Valid values are 'console', 'json', or 'csv'.
|
||||||
|
BM_DEFINE_string(benchmark_out_format, "json");
|
||||||
|
|
||||||
|
// The file to write additional output to.
|
||||||
|
BM_DEFINE_string(benchmark_out, "");
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
BM_DEFINE_string(benchmark_color, "auto");
|
||||||
|
|
||||||
|
// Whether to use tabular format when printing user counters to the console.
|
||||||
|
// Valid values: 'true'/'yes'/1, 'false'/'no'/0. Defaults to false.
|
||||||
|
BM_DEFINE_bool(benchmark_counters_tabular, false);
|
||||||
|
|
||||||
|
// List of additional perf counters to collect, in libpfm format. For more
|
||||||
|
// information about libpfm: https://man7.org/linux/man-pages/man3/libpfm.3.html
|
||||||
|
BM_DEFINE_string(benchmark_perf_counters, "");
|
||||||
|
|
||||||
|
// Extra context to include in the output formatted as comma-separated key-value
|
||||||
|
// pairs. Kept internal as it's only used for parsing from env/command line.
|
||||||
|
BM_DEFINE_kvpairs(benchmark_context, {});
|
||||||
|
|
||||||
|
// Set the default time unit to use for reports
|
||||||
|
// Valid values are 'ns', 'us', 'ms' or 's'
|
||||||
|
BM_DEFINE_string(benchmark_time_unit, "");
|
||||||
|
|
||||||
|
// The level of verbose logging to output
|
||||||
|
BM_DEFINE_int32(v, 0);
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
std::map<std::string, std::string>* global_context = nullptr;
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT std::map<std::string, std::string>*& GetGlobalContext() {
|
||||||
|
return global_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void const volatile* volatile global_force_escape_pointer;
|
||||||
|
|
||||||
|
// FIXME: Verify if LTO still messes this up?
|
||||||
|
void UseCharPointer(char const volatile* const v) {
|
||||||
|
// We want to escape the pointer `v` so that the compiler can not eliminate
|
||||||
|
// computations that produced it. To do that, we escape the pointer by storing
|
||||||
|
// it into a volatile variable, since generally, volatile store, is not
|
||||||
|
// something the compiler is allowed to elide.
|
||||||
|
global_force_escape_pointer = reinterpret_cast<void const volatile*>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
State::State(std::string name, IterationCount max_iters,
|
||||||
|
const std::vector<int64_t>& ranges, int thread_i, int n_threads,
|
||||||
|
internal::ThreadTimer* timer, internal::ThreadManager* manager,
|
||||||
|
internal::PerfCountersMeasurement* perf_counters_measurement)
|
||||||
|
: total_iterations_(0),
|
||||||
|
batch_leftover_(0),
|
||||||
|
max_iterations(max_iters),
|
||||||
|
started_(false),
|
||||||
|
finished_(false),
|
||||||
|
skipped_(internal::NotSkipped),
|
||||||
|
range_(ranges),
|
||||||
|
complexity_n_(0),
|
||||||
|
name_(std::move(name)),
|
||||||
|
thread_index_(thread_i),
|
||||||
|
threads_(n_threads),
|
||||||
|
timer_(timer),
|
||||||
|
manager_(manager),
|
||||||
|
perf_counters_measurement_(perf_counters_measurement) {
|
||||||
|
BM_CHECK(max_iterations != 0) << "At least one iteration must be run";
|
||||||
|
BM_CHECK_LT(thread_index_, threads_)
|
||||||
|
<< "thread_index must be less than threads";
|
||||||
|
|
||||||
|
// Add counters with correct flag now. If added with `counters[name]` in
|
||||||
|
// `PauseTiming`, a new `Counter` will be inserted the first time, which
|
||||||
|
// won't have the flag. Inserting them now also reduces the allocations
|
||||||
|
// during the benchmark.
|
||||||
|
if (perf_counters_measurement_) {
|
||||||
|
for (const std::string& counter_name :
|
||||||
|
perf_counters_measurement_->names()) {
|
||||||
|
counters[counter_name] = Counter(0.0, Counter::kAvgIterations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: The use of offsetof below is technically undefined until C++17
|
||||||
|
// because State is not a standard layout type. However, all compilers
|
||||||
|
// currently provide well-defined behavior as an extension (which is
|
||||||
|
// demonstrated since constexpr evaluation must diagnose all undefined
|
||||||
|
// behavior). However, GCC and Clang also warn about this use of offsetof,
|
||||||
|
// which must be suppressed.
|
||||||
|
#if defined(__INTEL_COMPILER)
|
||||||
|
#pragma warning push
|
||||||
|
#pragma warning(disable : 1875)
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
#endif
|
||||||
|
#if defined(__NVCC__)
|
||||||
|
#pragma nv_diagnostic push
|
||||||
|
#pragma nv_diag_suppress 1427
|
||||||
|
#endif
|
||||||
|
#if defined(__NVCOMPILER)
|
||||||
|
#pragma diagnostic push
|
||||||
|
#pragma diag_suppress offset_in_non_POD_nonstandard
|
||||||
|
#endif
|
||||||
|
// Offset tests to ensure commonly accessed data is on the first cache line.
|
||||||
|
const int cache_line_size = 64;
|
||||||
|
static_assert(
|
||||||
|
offsetof(State, skipped_) <= (cache_line_size - sizeof(skipped_)), "");
|
||||||
|
#if defined(__INTEL_COMPILER)
|
||||||
|
#pragma warning pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
#if defined(__NVCC__)
|
||||||
|
#pragma nv_diagnostic pop
|
||||||
|
#endif
|
||||||
|
#if defined(__NVCOMPILER)
|
||||||
|
#pragma diagnostic pop
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::PauseTiming() {
|
||||||
|
// Add in time accumulated so far
|
||||||
|
BM_CHECK(started_ && !finished_ && !skipped());
|
||||||
|
timer_->StopTimer();
|
||||||
|
if (perf_counters_measurement_) {
|
||||||
|
std::vector<std::pair<std::string, double>> measurements;
|
||||||
|
if (!perf_counters_measurement_->Stop(measurements)) {
|
||||||
|
BM_CHECK(false) << "Perf counters read the value failed.";
|
||||||
|
}
|
||||||
|
for (const auto& name_and_measurement : measurements) {
|
||||||
|
const std::string& name = name_and_measurement.first;
|
||||||
|
const double measurement = name_and_measurement.second;
|
||||||
|
// Counter was inserted with `kAvgIterations` flag by the constructor.
|
||||||
|
assert(counters.find(name) != counters.end());
|
||||||
|
counters[name].value += measurement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::ResumeTiming() {
|
||||||
|
BM_CHECK(started_ && !finished_ && !skipped());
|
||||||
|
timer_->StartTimer();
|
||||||
|
if (perf_counters_measurement_) {
|
||||||
|
perf_counters_measurement_->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::SkipWithMessage(const std::string& msg) {
|
||||||
|
skipped_ = internal::SkippedWithMessage;
|
||||||
|
{
|
||||||
|
MutexLock l(manager_->GetBenchmarkMutex());
|
||||||
|
if (internal::NotSkipped == manager_->results.skipped_) {
|
||||||
|
manager_->results.skip_message_ = msg;
|
||||||
|
manager_->results.skipped_ = skipped_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_iterations_ = 0;
|
||||||
|
if (timer_->running()) timer_->StopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::SkipWithError(const std::string& msg) {
|
||||||
|
skipped_ = internal::SkippedWithError;
|
||||||
|
{
|
||||||
|
MutexLock l(manager_->GetBenchmarkMutex());
|
||||||
|
if (internal::NotSkipped == manager_->results.skipped_) {
|
||||||
|
manager_->results.skip_message_ = msg;
|
||||||
|
manager_->results.skipped_ = skipped_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_iterations_ = 0;
|
||||||
|
if (timer_->running()) timer_->StopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::SetIterationTime(double seconds) {
|
||||||
|
timer_->SetIterationTime(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::SetLabel(const std::string& label) {
|
||||||
|
MutexLock l(manager_->GetBenchmarkMutex());
|
||||||
|
manager_->results.report_label_ = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::StartKeepRunning() {
|
||||||
|
BM_CHECK(!started_ && !finished_);
|
||||||
|
started_ = true;
|
||||||
|
total_iterations_ = skipped() ? 0 : max_iterations;
|
||||||
|
manager_->StartStopBarrier();
|
||||||
|
if (!skipped()) ResumeTiming();
|
||||||
|
}
|
||||||
|
|
||||||
|
void State::FinishKeepRunning() {
|
||||||
|
BM_CHECK(started_ && (!finished_ || skipped()));
|
||||||
|
if (!skipped()) {
|
||||||
|
PauseTiming();
|
||||||
|
}
|
||||||
|
// Total iterations has now wrapped around past 0. Fix this.
|
||||||
|
total_iterations_ = 0;
|
||||||
|
finished_ = true;
|
||||||
|
manager_->StartStopBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Flushes streams after invoking reporter methods that write to them. This
|
||||||
|
// ensures users get timely updates even when streams are not line-buffered.
|
||||||
|
void FlushStreams(BenchmarkReporter* reporter) {
|
||||||
|
if (!reporter) return;
|
||||||
|
std::flush(reporter->GetOutputStream());
|
||||||
|
std::flush(reporter->GetErrorStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reports in both display and file reporters.
|
||||||
|
void Report(BenchmarkReporter* display_reporter,
|
||||||
|
BenchmarkReporter* file_reporter, const RunResults& run_results) {
|
||||||
|
auto report_one = [](BenchmarkReporter* reporter, bool aggregates_only,
|
||||||
|
const RunResults& results) {
|
||||||
|
assert(reporter);
|
||||||
|
// If there are no aggregates, do output non-aggregates.
|
||||||
|
aggregates_only &= !results.aggregates_only.empty();
|
||||||
|
if (!aggregates_only) reporter->ReportRuns(results.non_aggregates);
|
||||||
|
if (!results.aggregates_only.empty())
|
||||||
|
reporter->ReportRuns(results.aggregates_only);
|
||||||
|
};
|
||||||
|
|
||||||
|
report_one(display_reporter, run_results.display_report_aggregates_only,
|
||||||
|
run_results);
|
||||||
|
if (file_reporter)
|
||||||
|
report_one(file_reporter, run_results.file_report_aggregates_only,
|
||||||
|
run_results);
|
||||||
|
|
||||||
|
FlushStreams(display_reporter);
|
||||||
|
FlushStreams(file_reporter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunBenchmarks(const std::vector<BenchmarkInstance>& benchmarks,
|
||||||
|
BenchmarkReporter* display_reporter,
|
||||||
|
BenchmarkReporter* file_reporter) {
|
||||||
|
// Note the file_reporter can be null.
|
||||||
|
BM_CHECK(display_reporter != nullptr);
|
||||||
|
|
||||||
|
// Determine the width of the name field using a minimum width of 10.
|
||||||
|
bool might_have_aggregates = FLAGS_benchmark_repetitions > 1;
|
||||||
|
size_t name_field_width = 10;
|
||||||
|
size_t stat_field_width = 0;
|
||||||
|
for (const BenchmarkInstance& benchmark : benchmarks) {
|
||||||
|
name_field_width =
|
||||||
|
std::max<size_t>(name_field_width, benchmark.name().str().size());
|
||||||
|
might_have_aggregates |= benchmark.repetitions() > 1;
|
||||||
|
|
||||||
|
for (const auto& Stat : benchmark.statistics())
|
||||||
|
stat_field_width = std::max<size_t>(stat_field_width, Stat.name_.size());
|
||||||
|
}
|
||||||
|
if (might_have_aggregates) name_field_width += 1 + stat_field_width;
|
||||||
|
|
||||||
|
// Print header here
|
||||||
|
BenchmarkReporter::Context context;
|
||||||
|
context.name_field_width = name_field_width;
|
||||||
|
|
||||||
|
// Keep track of running times of all instances of each benchmark family.
|
||||||
|
std::map<int /*family_index*/, BenchmarkReporter::PerFamilyRunReports>
|
||||||
|
per_family_reports;
|
||||||
|
|
||||||
|
if (display_reporter->ReportContext(context) &&
|
||||||
|
(!file_reporter || file_reporter->ReportContext(context))) {
|
||||||
|
FlushStreams(display_reporter);
|
||||||
|
FlushStreams(file_reporter);
|
||||||
|
|
||||||
|
size_t num_repetitions_total = 0;
|
||||||
|
|
||||||
|
// This perfcounters object needs to be created before the runners vector
|
||||||
|
// below so it outlasts their lifetime.
|
||||||
|
PerfCountersMeasurement perfcounters(
|
||||||
|
StrSplit(FLAGS_benchmark_perf_counters, ','));
|
||||||
|
|
||||||
|
// Vector of benchmarks to run
|
||||||
|
std::vector<internal::BenchmarkRunner> runners;
|
||||||
|
runners.reserve(benchmarks.size());
|
||||||
|
|
||||||
|
// Count the number of benchmarks with threads to warn the user in case
|
||||||
|
// performance counters are used.
|
||||||
|
int benchmarks_with_threads = 0;
|
||||||
|
|
||||||
|
// Loop through all benchmarks
|
||||||
|
for (const BenchmarkInstance& benchmark : benchmarks) {
|
||||||
|
BenchmarkReporter::PerFamilyRunReports* reports_for_family = nullptr;
|
||||||
|
if (benchmark.complexity() != oNone)
|
||||||
|
reports_for_family = &per_family_reports[benchmark.family_index()];
|
||||||
|
benchmarks_with_threads += (benchmark.threads() > 1);
|
||||||
|
runners.emplace_back(benchmark, &perfcounters, reports_for_family);
|
||||||
|
int num_repeats_of_this_instance = runners.back().GetNumRepeats();
|
||||||
|
num_repetitions_total +=
|
||||||
|
static_cast<size_t>(num_repeats_of_this_instance);
|
||||||
|
if (reports_for_family)
|
||||||
|
reports_for_family->num_runs_total += num_repeats_of_this_instance;
|
||||||
|
}
|
||||||
|
assert(runners.size() == benchmarks.size() && "Unexpected runner count.");
|
||||||
|
|
||||||
|
// The use of performance counters with threads would be unintuitive for
|
||||||
|
// the average user so we need to warn them about this case
|
||||||
|
if ((benchmarks_with_threads > 0) && (perfcounters.num_counters() > 0)) {
|
||||||
|
GetErrorLogInstance()
|
||||||
|
<< "***WARNING*** There are " << benchmarks_with_threads
|
||||||
|
<< " benchmarks with threads and " << perfcounters.num_counters()
|
||||||
|
<< " performance counters were requested. Beware counters will "
|
||||||
|
"reflect the combined usage across all "
|
||||||
|
"threads.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> repetition_indices;
|
||||||
|
repetition_indices.reserve(num_repetitions_total);
|
||||||
|
for (size_t runner_index = 0, num_runners = runners.size();
|
||||||
|
runner_index != num_runners; ++runner_index) {
|
||||||
|
const internal::BenchmarkRunner& runner = runners[runner_index];
|
||||||
|
std::fill_n(std::back_inserter(repetition_indices),
|
||||||
|
runner.GetNumRepeats(), runner_index);
|
||||||
|
}
|
||||||
|
assert(repetition_indices.size() == num_repetitions_total &&
|
||||||
|
"Unexpected number of repetition indexes.");
|
||||||
|
|
||||||
|
if (FLAGS_benchmark_enable_random_interleaving) {
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 g(rd());
|
||||||
|
std::shuffle(repetition_indices.begin(), repetition_indices.end(), g);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t repetition_index : repetition_indices) {
|
||||||
|
internal::BenchmarkRunner& runner = runners[repetition_index];
|
||||||
|
runner.DoOneRepetition();
|
||||||
|
if (runner.HasRepeatsRemaining()) continue;
|
||||||
|
// FIXME: report each repetition separately, not all of them in bulk.
|
||||||
|
|
||||||
|
display_reporter->ReportRunsConfig(
|
||||||
|
runner.GetMinTime(), runner.HasExplicitIters(), runner.GetIters());
|
||||||
|
if (file_reporter)
|
||||||
|
file_reporter->ReportRunsConfig(
|
||||||
|
runner.GetMinTime(), runner.HasExplicitIters(), runner.GetIters());
|
||||||
|
|
||||||
|
RunResults run_results = runner.GetResults();
|
||||||
|
|
||||||
|
// Maybe calculate complexity report
|
||||||
|
if (const auto* reports_for_family = runner.GetReportsForFamily()) {
|
||||||
|
if (reports_for_family->num_runs_done ==
|
||||||
|
reports_for_family->num_runs_total) {
|
||||||
|
auto additional_run_stats = ComputeBigO(reports_for_family->Runs);
|
||||||
|
run_results.aggregates_only.insert(run_results.aggregates_only.end(),
|
||||||
|
additional_run_stats.begin(),
|
||||||
|
additional_run_stats.end());
|
||||||
|
per_family_reports.erase(
|
||||||
|
static_cast<int>(reports_for_family->Runs.front().family_index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Report(display_reporter, file_reporter, run_results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display_reporter->Finalize();
|
||||||
|
if (file_reporter) file_reporter->Finalize();
|
||||||
|
FlushStreams(display_reporter);
|
||||||
|
FlushStreams(file_reporter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable deprecated warnings temporarily because we need to reference
|
||||||
|
// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations
|
||||||
|
BENCHMARK_DISABLE_DEPRECATED_WARNING
|
||||||
|
|
||||||
|
std::unique_ptr<BenchmarkReporter> CreateReporter(
|
||||||
|
std::string const& name, ConsoleReporter::OutputOptions output_opts) {
|
||||||
|
typedef std::unique_ptr<BenchmarkReporter> PtrType;
|
||||||
|
if (name == "console") {
|
||||||
|
return PtrType(new ConsoleReporter(output_opts));
|
||||||
|
}
|
||||||
|
if (name == "json") {
|
||||||
|
return PtrType(new JSONReporter());
|
||||||
|
}
|
||||||
|
if (name == "csv") {
|
||||||
|
return PtrType(new CSVReporter());
|
||||||
|
}
|
||||||
|
std::cerr << "Unexpected format: '" << name << "'\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_RESTORE_DEPRECATED_WARNING
|
||||||
|
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
|
bool IsZero(double n) {
|
||||||
|
return std::abs(n) < std::numeric_limits<double>::epsilon();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color) {
|
||||||
|
int output_opts = ConsoleReporter::OO_Defaults;
|
||||||
|
auto is_benchmark_color = [force_no_color]() -> bool {
|
||||||
|
if (force_no_color) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (FLAGS_benchmark_color == "auto") {
|
||||||
|
return IsColorTerminal();
|
||||||
|
}
|
||||||
|
return IsTruthyFlagValue(FLAGS_benchmark_color);
|
||||||
|
};
|
||||||
|
if (is_benchmark_color()) {
|
||||||
|
output_opts |= ConsoleReporter::OO_Color;
|
||||||
|
} else {
|
||||||
|
output_opts &= ~ConsoleReporter::OO_Color;
|
||||||
|
}
|
||||||
|
if (FLAGS_benchmark_counters_tabular) {
|
||||||
|
output_opts |= ConsoleReporter::OO_Tabular;
|
||||||
|
} else {
|
||||||
|
output_opts &= ~ConsoleReporter::OO_Tabular;
|
||||||
|
}
|
||||||
|
return static_cast<ConsoleReporter::OutputOptions>(output_opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
|
||||||
|
BenchmarkReporter* CreateDefaultDisplayReporter() {
|
||||||
|
static auto default_display_reporter =
|
||||||
|
internal::CreateReporter(FLAGS_benchmark_format,
|
||||||
|
internal::GetOutputOptions())
|
||||||
|
.release();
|
||||||
|
return default_display_reporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks() {
|
||||||
|
return RunSpecifiedBenchmarks(nullptr, nullptr, FLAGS_benchmark_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks(std::string spec) {
|
||||||
|
return RunSpecifiedBenchmarks(nullptr, nullptr, std::move(spec));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter) {
|
||||||
|
return RunSpecifiedBenchmarks(display_reporter, nullptr,
|
||||||
|
FLAGS_benchmark_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
|
||||||
|
std::string spec) {
|
||||||
|
return RunSpecifiedBenchmarks(display_reporter, nullptr, std::move(spec));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
|
||||||
|
BenchmarkReporter* file_reporter) {
|
||||||
|
return RunSpecifiedBenchmarks(display_reporter, file_reporter,
|
||||||
|
FLAGS_benchmark_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
|
||||||
|
BenchmarkReporter* file_reporter,
|
||||||
|
std::string spec) {
|
||||||
|
if (spec.empty() || spec == "all")
|
||||||
|
spec = "."; // Regexp that matches all benchmarks
|
||||||
|
|
||||||
|
// Setup the reporters
|
||||||
|
std::ofstream output_file;
|
||||||
|
std::unique_ptr<BenchmarkReporter> default_display_reporter;
|
||||||
|
std::unique_ptr<BenchmarkReporter> default_file_reporter;
|
||||||
|
if (!display_reporter) {
|
||||||
|
default_display_reporter.reset(CreateDefaultDisplayReporter());
|
||||||
|
display_reporter = default_display_reporter.get();
|
||||||
|
}
|
||||||
|
auto& Out = display_reporter->GetOutputStream();
|
||||||
|
auto& Err = display_reporter->GetErrorStream();
|
||||||
|
|
||||||
|
std::string const& fname = FLAGS_benchmark_out;
|
||||||
|
if (fname.empty() && file_reporter) {
|
||||||
|
Err << "A custom file reporter was provided but "
|
||||||
|
"--benchmark_out=<file> was not specified."
|
||||||
|
<< std::endl;
|
||||||
|
Out.flush();
|
||||||
|
Err.flush();
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
if (!fname.empty()) {
|
||||||
|
output_file.open(fname);
|
||||||
|
if (!output_file.is_open()) {
|
||||||
|
Err << "invalid file name: '" << fname << "'" << std::endl;
|
||||||
|
Out.flush();
|
||||||
|
Err.flush();
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
if (!file_reporter) {
|
||||||
|
default_file_reporter = internal::CreateReporter(
|
||||||
|
FLAGS_benchmark_out_format, FLAGS_benchmark_counters_tabular
|
||||||
|
? ConsoleReporter::OO_Tabular
|
||||||
|
: ConsoleReporter::OO_None);
|
||||||
|
file_reporter = default_file_reporter.get();
|
||||||
|
}
|
||||||
|
file_reporter->SetOutputStream(&output_file);
|
||||||
|
file_reporter->SetErrorStream(&output_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<internal::BenchmarkInstance> benchmarks;
|
||||||
|
if (!FindBenchmarksInternal(spec, &benchmarks, &Err)) {
|
||||||
|
Out.flush();
|
||||||
|
Err.flush();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benchmarks.empty()) {
|
||||||
|
Err << "Failed to match any benchmarks against regex: " << spec << "\n";
|
||||||
|
Out.flush();
|
||||||
|
Err.flush();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_benchmark_list_tests) {
|
||||||
|
for (auto const& benchmark : benchmarks)
|
||||||
|
Out << benchmark.name().str() << "\n";
|
||||||
|
} else {
|
||||||
|
internal::RunBenchmarks(benchmarks, display_reporter, file_reporter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Out.flush();
|
||||||
|
Err.flush();
|
||||||
|
return benchmarks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// stores the time unit benchmarks use by default
|
||||||
|
TimeUnit default_time_unit = kNanosecond;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TimeUnit GetDefaultTimeUnit() { return default_time_unit; }
|
||||||
|
|
||||||
|
void SetDefaultTimeUnit(TimeUnit unit) { default_time_unit = unit; }
|
||||||
|
|
||||||
|
std::string GetBenchmarkFilter() { return FLAGS_benchmark_filter; }
|
||||||
|
|
||||||
|
void SetBenchmarkFilter(std::string value) {
|
||||||
|
FLAGS_benchmark_filter = std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t GetBenchmarkVerbosity() { return FLAGS_v; }
|
||||||
|
|
||||||
|
void RegisterMemoryManager(MemoryManager* manager) {
|
||||||
|
internal::memory_manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCustomContext(const std::string& key, const std::string& value) {
|
||||||
|
if (internal::global_context == nullptr) {
|
||||||
|
internal::global_context = new std::map<std::string, std::string>();
|
||||||
|
}
|
||||||
|
if (!internal::global_context->emplace(key, value).second) {
|
||||||
|
std::cerr << "Failed to add custom context \"" << key << "\" as it already "
|
||||||
|
<< "exists with value \"" << value << "\"\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
void (*HelperPrintf)();
|
||||||
|
|
||||||
|
void PrintUsageAndExit() {
|
||||||
|
HelperPrintf();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDefaultTimeUnitFromFlag(const std::string& time_unit_flag) {
|
||||||
|
if (time_unit_flag == "s") {
|
||||||
|
return SetDefaultTimeUnit(kSecond);
|
||||||
|
}
|
||||||
|
if (time_unit_flag == "ms") {
|
||||||
|
return SetDefaultTimeUnit(kMillisecond);
|
||||||
|
}
|
||||||
|
if (time_unit_flag == "us") {
|
||||||
|
return SetDefaultTimeUnit(kMicrosecond);
|
||||||
|
}
|
||||||
|
if (time_unit_flag == "ns") {
|
||||||
|
return SetDefaultTimeUnit(kNanosecond);
|
||||||
|
}
|
||||||
|
if (!time_unit_flag.empty()) {
|
||||||
|
PrintUsageAndExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseCommandLineFlags(int* argc, char** argv) {
|
||||||
|
using namespace benchmark;
|
||||||
|
BenchmarkReporter::Context::executable_name =
|
||||||
|
(argc && *argc > 0) ? argv[0] : "unknown";
|
||||||
|
for (int i = 1; argc && i < *argc; ++i) {
|
||||||
|
if (ParseBoolFlag(argv[i], "benchmark_list_tests",
|
||||||
|
&FLAGS_benchmark_list_tests) ||
|
||||||
|
ParseStringFlag(argv[i], "benchmark_filter", &FLAGS_benchmark_filter) ||
|
||||||
|
ParseStringFlag(argv[i], "benchmark_min_time",
|
||||||
|
&FLAGS_benchmark_min_time) ||
|
||||||
|
ParseDoubleFlag(argv[i], "benchmark_min_warmup_time",
|
||||||
|
&FLAGS_benchmark_min_warmup_time) ||
|
||||||
|
ParseInt32Flag(argv[i], "benchmark_repetitions",
|
||||||
|
&FLAGS_benchmark_repetitions) ||
|
||||||
|
ParseBoolFlag(argv[i], "benchmark_enable_random_interleaving",
|
||||||
|
&FLAGS_benchmark_enable_random_interleaving) ||
|
||||||
|
ParseBoolFlag(argv[i], "benchmark_report_aggregates_only",
|
||||||
|
&FLAGS_benchmark_report_aggregates_only) ||
|
||||||
|
ParseBoolFlag(argv[i], "benchmark_display_aggregates_only",
|
||||||
|
&FLAGS_benchmark_display_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) ||
|
||||||
|
ParseBoolFlag(argv[i], "benchmark_counters_tabular",
|
||||||
|
&FLAGS_benchmark_counters_tabular) ||
|
||||||
|
ParseStringFlag(argv[i], "benchmark_perf_counters",
|
||||||
|
&FLAGS_benchmark_perf_counters) ||
|
||||||
|
ParseKeyValueFlag(argv[i], "benchmark_context",
|
||||||
|
&FLAGS_benchmark_context) ||
|
||||||
|
ParseStringFlag(argv[i], "benchmark_time_unit",
|
||||||
|
&FLAGS_benchmark_time_unit) ||
|
||||||
|
ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
|
||||||
|
for (int j = i; j != *argc - 1; ++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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetDefaultTimeUnitFromFlag(FLAGS_benchmark_time_unit);
|
||||||
|
if (FLAGS_benchmark_color.empty()) {
|
||||||
|
PrintUsageAndExit();
|
||||||
|
}
|
||||||
|
for (const auto& kv : FLAGS_benchmark_context) {
|
||||||
|
AddCustomContext(kv.first, kv.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int InitializeStreams() {
|
||||||
|
static std::ios_base::Init init;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
|
||||||
|
std::string GetBenchmarkVersion() {
|
||||||
|
#ifdef BENCHMARK_VERSION
|
||||||
|
return {BENCHMARK_VERSION};
|
||||||
|
#else
|
||||||
|
return {""};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintDefaultHelp() {
|
||||||
|
fprintf(stdout,
|
||||||
|
"benchmark"
|
||||||
|
" [--benchmark_list_tests={true|false}]\n"
|
||||||
|
" [--benchmark_filter=<regex>]\n"
|
||||||
|
" [--benchmark_min_time=`<integer>x` OR `<float>s` ]\n"
|
||||||
|
" [--benchmark_min_warmup_time=<min_warmup_time>]\n"
|
||||||
|
" [--benchmark_repetitions=<num_repetitions>]\n"
|
||||||
|
" [--benchmark_enable_random_interleaving={true|false}]\n"
|
||||||
|
" [--benchmark_report_aggregates_only={true|false}]\n"
|
||||||
|
" [--benchmark_display_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"
|
||||||
|
" [--benchmark_counters_tabular={true|false}]\n"
|
||||||
|
#if defined HAVE_LIBPFM
|
||||||
|
" [--benchmark_perf_counters=<counter>,...]\n"
|
||||||
|
#endif
|
||||||
|
" [--benchmark_context=<key>=<value>,...]\n"
|
||||||
|
" [--benchmark_time_unit={ns|us|ms|s}]\n"
|
||||||
|
" [--v=<verbosity>]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(int* argc, char** argv, void (*HelperPrintf)()) {
|
||||||
|
internal::HelperPrintf = HelperPrintf;
|
||||||
|
internal::ParseCommandLineFlags(argc, argv);
|
||||||
|
internal::LogLevel() = FLAGS_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown() { delete internal::global_context; }
|
||||||
|
|
||||||
|
bool ReportUnrecognizedArguments(int argc, char** argv) {
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
fprintf(stderr, "%s: error: unrecognized command-line flag: %s\n", argv[0],
|
||||||
|
argv[i]);
|
||||||
|
}
|
||||||
|
return argc > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
118
third_party/benchmark/src/benchmark_api_internal.cc
vendored
Normal file
118
third_party/benchmark/src/benchmark_api_internal.cc
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include "benchmark_api_internal.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include "string_util.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
BenchmarkInstance::BenchmarkInstance(Benchmark* benchmark, int family_idx,
|
||||||
|
int per_family_instance_idx,
|
||||||
|
const std::vector<int64_t>& args,
|
||||||
|
int thread_count)
|
||||||
|
: benchmark_(*benchmark),
|
||||||
|
family_index_(family_idx),
|
||||||
|
per_family_instance_index_(per_family_instance_idx),
|
||||||
|
aggregation_report_mode_(benchmark_.aggregation_report_mode_),
|
||||||
|
args_(args),
|
||||||
|
time_unit_(benchmark_.GetTimeUnit()),
|
||||||
|
measure_process_cpu_time_(benchmark_.measure_process_cpu_time_),
|
||||||
|
use_real_time_(benchmark_.use_real_time_),
|
||||||
|
use_manual_time_(benchmark_.use_manual_time_),
|
||||||
|
complexity_(benchmark_.complexity_),
|
||||||
|
complexity_lambda_(benchmark_.complexity_lambda_),
|
||||||
|
statistics_(benchmark_.statistics_),
|
||||||
|
repetitions_(benchmark_.repetitions_),
|
||||||
|
min_time_(benchmark_.min_time_),
|
||||||
|
min_warmup_time_(benchmark_.min_warmup_time_),
|
||||||
|
iterations_(benchmark_.iterations_),
|
||||||
|
threads_(thread_count) {
|
||||||
|
name_.function_name = benchmark_.name_;
|
||||||
|
|
||||||
|
size_t arg_i = 0;
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
if (!name_.args.empty()) {
|
||||||
|
name_.args += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_i < benchmark->arg_names_.size()) {
|
||||||
|
const auto& arg_name = benchmark_.arg_names_[arg_i];
|
||||||
|
if (!arg_name.empty()) {
|
||||||
|
name_.args += StrFormat("%s:", arg_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name_.args += StrFormat("%" PRId64, arg);
|
||||||
|
++arg_i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsZero(benchmark->min_time_)) {
|
||||||
|
name_.min_time = StrFormat("min_time:%0.3f", benchmark_.min_time_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsZero(benchmark->min_warmup_time_)) {
|
||||||
|
name_.min_warmup_time =
|
||||||
|
StrFormat("min_warmup_time:%0.3f", benchmark_.min_warmup_time_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benchmark_.iterations_ != 0) {
|
||||||
|
name_.iterations = StrFormat(
|
||||||
|
"iterations:%lu", static_cast<unsigned long>(benchmark_.iterations_));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benchmark_.repetitions_ != 0) {
|
||||||
|
name_.repetitions = StrFormat("repeats:%d", benchmark_.repetitions_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benchmark_.measure_process_cpu_time_) {
|
||||||
|
name_.time_type = "process_time";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benchmark_.use_manual_time_) {
|
||||||
|
if (!name_.time_type.empty()) {
|
||||||
|
name_.time_type += '/';
|
||||||
|
}
|
||||||
|
name_.time_type += "manual_time";
|
||||||
|
} else if (benchmark_.use_real_time_) {
|
||||||
|
if (!name_.time_type.empty()) {
|
||||||
|
name_.time_type += '/';
|
||||||
|
}
|
||||||
|
name_.time_type += "real_time";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!benchmark_.thread_counts_.empty()) {
|
||||||
|
name_.threads = StrFormat("threads:%d", threads_);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_ = benchmark_.setup_;
|
||||||
|
teardown_ = benchmark_.teardown_;
|
||||||
|
}
|
||||||
|
|
||||||
|
State BenchmarkInstance::Run(
|
||||||
|
IterationCount iters, int thread_id, internal::ThreadTimer* timer,
|
||||||
|
internal::ThreadManager* manager,
|
||||||
|
internal::PerfCountersMeasurement* perf_counters_measurement) const {
|
||||||
|
State st(name_.function_name, iters, args_, thread_id, threads_, timer,
|
||||||
|
manager, perf_counters_measurement);
|
||||||
|
benchmark_.Run(st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BenchmarkInstance::Setup() const {
|
||||||
|
if (setup_) {
|
||||||
|
State st(name_.function_name, /*iters*/ 1, args_, /*thread_id*/ 0, threads_,
|
||||||
|
nullptr, nullptr, nullptr);
|
||||||
|
setup_(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BenchmarkInstance::Teardown() const {
|
||||||
|
if (teardown_) {
|
||||||
|
State st(name_.function_name, /*iters*/ 1, args_, /*thread_id*/ 0, threads_,
|
||||||
|
nullptr, nullptr, nullptr);
|
||||||
|
teardown_(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace benchmark
|
87
third_party/benchmark/src/benchmark_api_internal.h
vendored
Normal file
87
third_party/benchmark/src/benchmark_api_internal.h
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#ifndef BENCHMARK_API_INTERNAL_H
|
||||||
|
#define BENCHMARK_API_INTERNAL_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "commandlineflags.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Information kept per benchmark we may want to run
|
||||||
|
class BenchmarkInstance {
|
||||||
|
public:
|
||||||
|
BenchmarkInstance(Benchmark* benchmark, int family_index,
|
||||||
|
int per_family_instance_index,
|
||||||
|
const std::vector<int64_t>& args, int threads);
|
||||||
|
|
||||||
|
const BenchmarkName& name() const { return name_; }
|
||||||
|
int family_index() const { return family_index_; }
|
||||||
|
int per_family_instance_index() const { return per_family_instance_index_; }
|
||||||
|
AggregationReportMode aggregation_report_mode() const {
|
||||||
|
return aggregation_report_mode_;
|
||||||
|
}
|
||||||
|
TimeUnit time_unit() const { return time_unit_; }
|
||||||
|
bool measure_process_cpu_time() const { return measure_process_cpu_time_; }
|
||||||
|
bool use_real_time() const { return use_real_time_; }
|
||||||
|
bool use_manual_time() const { return use_manual_time_; }
|
||||||
|
BigO complexity() const { return complexity_; }
|
||||||
|
BigOFunc* complexity_lambda() const { return complexity_lambda_; }
|
||||||
|
const std::vector<Statistics>& statistics() const { return statistics_; }
|
||||||
|
int repetitions() const { return repetitions_; }
|
||||||
|
double min_time() const { return min_time_; }
|
||||||
|
double min_warmup_time() const { return min_warmup_time_; }
|
||||||
|
IterationCount iterations() const { return iterations_; }
|
||||||
|
int threads() const { return threads_; }
|
||||||
|
void Setup() const;
|
||||||
|
void Teardown() const;
|
||||||
|
|
||||||
|
State Run(IterationCount iters, int thread_id, internal::ThreadTimer* timer,
|
||||||
|
internal::ThreadManager* manager,
|
||||||
|
internal::PerfCountersMeasurement* perf_counters_measurement) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BenchmarkName name_;
|
||||||
|
Benchmark& benchmark_;
|
||||||
|
const int family_index_;
|
||||||
|
const int per_family_instance_index_;
|
||||||
|
AggregationReportMode aggregation_report_mode_;
|
||||||
|
const std::vector<int64_t>& args_;
|
||||||
|
TimeUnit time_unit_;
|
||||||
|
bool measure_process_cpu_time_;
|
||||||
|
bool use_real_time_;
|
||||||
|
bool use_manual_time_;
|
||||||
|
BigO complexity_;
|
||||||
|
BigOFunc* complexity_lambda_;
|
||||||
|
UserCounters counters_;
|
||||||
|
const std::vector<Statistics>& statistics_;
|
||||||
|
int repetitions_;
|
||||||
|
double min_time_;
|
||||||
|
double min_warmup_time_;
|
||||||
|
IterationCount iterations_;
|
||||||
|
int threads_; // Number of concurrent threads to us
|
||||||
|
|
||||||
|
typedef void (*callback_function)(const benchmark::State&);
|
||||||
|
callback_function setup_ = nullptr;
|
||||||
|
callback_function teardown_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool FindBenchmarksInternal(const std::string& re,
|
||||||
|
std::vector<BenchmarkInstance>* benchmarks,
|
||||||
|
std::ostream* Err);
|
||||||
|
|
||||||
|
bool IsZero(double n);
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color = false);
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_API_INTERNAL_H
|
18
third_party/benchmark/src/benchmark_main.cc
vendored
Normal file
18
third_party/benchmark/src/benchmark_main.cc
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2018 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"
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT int main(int, char**);
|
||||||
|
BENCHMARK_MAIN();
|
59
third_party/benchmark/src/benchmark_name.cc
vendored
Normal file
59
third_party/benchmark/src/benchmark_name.cc
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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>
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Compute the total size of a pack of std::strings
|
||||||
|
size_t size_impl() { return 0; }
|
||||||
|
|
||||||
|
template <typename Head, typename... Tail>
|
||||||
|
size_t size_impl(const Head& head, const Tail&... tail) {
|
||||||
|
return head.size() + size_impl(tail...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join a pack of std::strings using a delimiter
|
||||||
|
// TODO: use absl::StrJoin
|
||||||
|
void join_impl(std::string&, char) {}
|
||||||
|
|
||||||
|
template <typename Head, typename... Tail>
|
||||||
|
void join_impl(std::string& s, const char delimiter, const Head& head,
|
||||||
|
const Tail&... tail) {
|
||||||
|
if (!s.empty() && !head.empty()) {
|
||||||
|
s += delimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
s += head;
|
||||||
|
|
||||||
|
join_impl(s, delimiter, tail...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
std::string join(char delimiter, const Ts&... ts) {
|
||||||
|
std::string s;
|
||||||
|
s.reserve(sizeof...(Ts) + size_impl(ts...));
|
||||||
|
join_impl(s, delimiter, ts...);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
std::string BenchmarkName::str() const {
|
||||||
|
return join('/', function_name, args, min_time, min_warmup_time, iterations,
|
||||||
|
repetitions, time_type, threads);
|
||||||
|
}
|
||||||
|
} // namespace benchmark
|
521
third_party/benchmark/src/benchmark_register.cc
vendored
Normal file
521
third_party/benchmark/src/benchmark_register.cc
vendored
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
// 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_register.h"
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_OS_WINDOWS
|
||||||
|
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <numeric>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "benchmark_api_internal.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "commandlineflags.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "re.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// For non-dense Range, intermediate values are powers of kRangeMultiplier.
|
||||||
|
static constexpr 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 constexpr size_t kMaxFamilySize = 100;
|
||||||
|
|
||||||
|
static constexpr char kDisabledPrefix[] = "DISABLED_";
|
||||||
|
} // 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);
|
||||||
|
|
||||||
|
// Clear all registered benchmark families.
|
||||||
|
void ClearBenchmarks();
|
||||||
|
|
||||||
|
// Extract the list of benchmark instances that match the specified
|
||||||
|
// regular expression.
|
||||||
|
bool FindBenchmarks(std::string re,
|
||||||
|
std::vector<BenchmarkInstance>* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BenchmarkFamilies::ClearBenchmarks() {
|
||||||
|
MutexLock l(mutex_);
|
||||||
|
families_.clear();
|
||||||
|
families_.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BenchmarkFamilies::FindBenchmarks(
|
||||||
|
std::string spec, std::vector<BenchmarkInstance>* benchmarks,
|
||||||
|
std::ostream* ErrStream) {
|
||||||
|
BM_CHECK(ErrStream);
|
||||||
|
auto& Err = *ErrStream;
|
||||||
|
// Make regular expression out of command-line flag
|
||||||
|
std::string error_msg;
|
||||||
|
Regex re;
|
||||||
|
bool is_negative_filter = false;
|
||||||
|
if (spec[0] == '-') {
|
||||||
|
spec.replace(0, 1, "");
|
||||||
|
is_negative_filter = true;
|
||||||
|
}
|
||||||
|
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};
|
||||||
|
|
||||||
|
int next_family_index = 0;
|
||||||
|
|
||||||
|
MutexLock l(mutex_);
|
||||||
|
for (std::unique_ptr<Benchmark>& family : families_) {
|
||||||
|
int family_index = next_family_index;
|
||||||
|
int per_family_instance_index = 0;
|
||||||
|
|
||||||
|
// 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. this doesn't take into account any disabled benchmarks
|
||||||
|
// so worst case we reserve more than we need.
|
||||||
|
if (spec == ".") benchmarks->reserve(benchmarks->size() + family_size);
|
||||||
|
|
||||||
|
for (auto const& args : family->args_) {
|
||||||
|
for (int num_threads : *thread_counts) {
|
||||||
|
BenchmarkInstance instance(family.get(), family_index,
|
||||||
|
per_family_instance_index, args,
|
||||||
|
num_threads);
|
||||||
|
|
||||||
|
const auto full_name = instance.name().str();
|
||||||
|
if (full_name.rfind(kDisabledPrefix, 0) != 0 &&
|
||||||
|
((re.Match(full_name) && !is_negative_filter) ||
|
||||||
|
(!re.Match(full_name) && is_negative_filter))) {
|
||||||
|
benchmarks->push_back(std::move(instance));
|
||||||
|
|
||||||
|
++per_family_instance_index;
|
||||||
|
|
||||||
|
// Only bump the next family index once we've estabilished that
|
||||||
|
// at least one instance of this family will be run.
|
||||||
|
if (next_family_index == family_index) ++next_family_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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<BenchmarkInstance>* benchmarks,
|
||||||
|
std::ostream* Err) {
|
||||||
|
return BenchmarkFamilies::GetInstance()->FindBenchmarks(re, benchmarks, Err);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================//
|
||||||
|
// Benchmark
|
||||||
|
//=============================================================================//
|
||||||
|
|
||||||
|
Benchmark::Benchmark(const std::string& name)
|
||||||
|
: name_(name),
|
||||||
|
aggregation_report_mode_(ARM_Unspecified),
|
||||||
|
time_unit_(GetDefaultTimeUnit()),
|
||||||
|
use_default_time_unit_(true),
|
||||||
|
range_multiplier_(kRangeMultiplier),
|
||||||
|
min_time_(0),
|
||||||
|
min_warmup_time_(0),
|
||||||
|
iterations_(0),
|
||||||
|
repetitions_(0),
|
||||||
|
measure_process_cpu_time_(false),
|
||||||
|
use_real_time_(false),
|
||||||
|
use_manual_time_(false),
|
||||||
|
complexity_(oNone),
|
||||||
|
complexity_lambda_(nullptr),
|
||||||
|
setup_(nullptr),
|
||||||
|
teardown_(nullptr) {
|
||||||
|
ComputeStatistics("mean", StatisticsMean);
|
||||||
|
ComputeStatistics("median", StatisticsMedian);
|
||||||
|
ComputeStatistics("stddev", StatisticsStdDev);
|
||||||
|
ComputeStatistics("cv", StatisticsCV, kPercentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark::~Benchmark() {}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Name(const std::string& name) {
|
||||||
|
SetName(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Arg(int64_t x) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
|
args_.push_back({x});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Unit(TimeUnit unit) {
|
||||||
|
time_unit_ = unit;
|
||||||
|
use_default_time_unit_ = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Range(int64_t start, int64_t limit) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
|
std::vector<int64_t> arglist;
|
||||||
|
AddRange(&arglist, start, limit, range_multiplier_);
|
||||||
|
|
||||||
|
for (int64_t i : arglist) {
|
||||||
|
args_.push_back({i});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Ranges(
|
||||||
|
const std::vector<std::pair<int64_t, int64_t>>& ranges) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(ranges.size()));
|
||||||
|
std::vector<std::vector<int64_t>> arglists(ranges.size());
|
||||||
|
for (std::size_t i = 0; i < ranges.size(); i++) {
|
||||||
|
AddRange(&arglists[i], ranges[i].first, ranges[i].second,
|
||||||
|
range_multiplier_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArgsProduct(arglists);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::ArgsProduct(
|
||||||
|
const std::vector<std::vector<int64_t>>& arglists) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(arglists.size()));
|
||||||
|
|
||||||
|
std::vector<std::size_t> indices(arglists.size());
|
||||||
|
const std::size_t total = std::accumulate(
|
||||||
|
std::begin(arglists), std::end(arglists), std::size_t{1},
|
||||||
|
[](const std::size_t res, const std::vector<int64_t>& arglist) {
|
||||||
|
return res * arglist.size();
|
||||||
|
});
|
||||||
|
std::vector<int64_t> args;
|
||||||
|
args.reserve(arglists.size());
|
||||||
|
for (std::size_t i = 0; i < total; i++) {
|
||||||
|
for (std::size_t arg = 0; arg < arglists.size(); arg++) {
|
||||||
|
args.push_back(arglists[arg][indices[arg]]);
|
||||||
|
}
|
||||||
|
args_.push_back(args);
|
||||||
|
args.clear();
|
||||||
|
|
||||||
|
std::size_t arg = 0;
|
||||||
|
do {
|
||||||
|
indices[arg] = (indices[arg] + 1) % arglists[arg].size();
|
||||||
|
} while (indices[arg++] == 0 && arg < arglists.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::ArgName(const std::string& name) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
|
arg_names_ = {name};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::ArgNames(const std::vector<std::string>& names) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(names.size()));
|
||||||
|
arg_names_ = names;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::DenseRange(int64_t start, int64_t limit, int step) {
|
||||||
|
BM_CHECK(ArgsCnt() == -1 || ArgsCnt() == 1);
|
||||||
|
BM_CHECK_LE(start, limit);
|
||||||
|
for (int64_t arg = start; arg <= limit; arg += step) {
|
||||||
|
args_.push_back({arg});
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Args(const std::vector<int64_t>& args) {
|
||||||
|
BM_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::Setup(void (*setup)(const benchmark::State&)) {
|
||||||
|
BM_CHECK(setup != nullptr);
|
||||||
|
setup_ = setup;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Teardown(void (*teardown)(const benchmark::State&)) {
|
||||||
|
BM_CHECK(teardown != nullptr);
|
||||||
|
teardown_ = teardown;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::RangeMultiplier(int multiplier) {
|
||||||
|
BM_CHECK(multiplier > 1);
|
||||||
|
range_multiplier_ = multiplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::MinTime(double t) {
|
||||||
|
BM_CHECK(t > 0.0);
|
||||||
|
BM_CHECK(iterations_ == 0);
|
||||||
|
min_time_ = t;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::MinWarmUpTime(double t) {
|
||||||
|
BM_CHECK(t >= 0.0);
|
||||||
|
BM_CHECK(iterations_ == 0);
|
||||||
|
min_warmup_time_ = t;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Iterations(IterationCount n) {
|
||||||
|
BM_CHECK(n > 0);
|
||||||
|
BM_CHECK(IsZero(min_time_));
|
||||||
|
BM_CHECK(IsZero(min_warmup_time_));
|
||||||
|
iterations_ = n;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Repetitions(int n) {
|
||||||
|
BM_CHECK(n > 0);
|
||||||
|
repetitions_ = n;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::ReportAggregatesOnly(bool value) {
|
||||||
|
aggregation_report_mode_ = value ? ARM_ReportAggregatesOnly : ARM_Default;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::DisplayAggregatesOnly(bool value) {
|
||||||
|
// If we were called, the report mode is no longer 'unspecified', in any case.
|
||||||
|
aggregation_report_mode_ = static_cast<AggregationReportMode>(
|
||||||
|
aggregation_report_mode_ | ARM_Default);
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
aggregation_report_mode_ = static_cast<AggregationReportMode>(
|
||||||
|
aggregation_report_mode_ | ARM_DisplayReportAggregatesOnly);
|
||||||
|
} else {
|
||||||
|
aggregation_report_mode_ = static_cast<AggregationReportMode>(
|
||||||
|
aggregation_report_mode_ & ~ARM_DisplayReportAggregatesOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::MeasureProcessCPUTime() {
|
||||||
|
// Can be used together with UseRealTime() / UseManualTime().
|
||||||
|
measure_process_cpu_time_ = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::UseRealTime() {
|
||||||
|
BM_CHECK(!use_manual_time_)
|
||||||
|
<< "Cannot set UseRealTime and UseManualTime simultaneously.";
|
||||||
|
use_real_time_ = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::UseManualTime() {
|
||||||
|
BM_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::ComputeStatistics(const std::string& name,
|
||||||
|
StatisticsFunc* statistics,
|
||||||
|
StatisticUnit unit) {
|
||||||
|
statistics_.emplace_back(name, statistics, unit);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::Threads(int t) {
|
||||||
|
BM_CHECK_GT(t, 0);
|
||||||
|
thread_counts_.push_back(t);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark* Benchmark::ThreadRange(int min_threads, int max_threads) {
|
||||||
|
BM_CHECK_GT(min_threads, 0);
|
||||||
|
BM_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) {
|
||||||
|
BM_CHECK_GT(min_threads, 0);
|
||||||
|
BM_CHECK_GE(max_threads, min_threads);
|
||||||
|
BM_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() {
|
||||||
|
thread_counts_.push_back(CPUInfo::Get().num_cpus);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Benchmark::SetName(const std::string& name) { name_ = name; }
|
||||||
|
|
||||||
|
const char* Benchmark::GetName() const { return name_.c_str(); }
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Benchmark::GetArgName(int arg) const {
|
||||||
|
BM_CHECK_GE(arg, 0);
|
||||||
|
size_t uarg = static_cast<size_t>(arg);
|
||||||
|
BM_CHECK_LT(uarg, arg_names_.size());
|
||||||
|
return arg_names_[uarg].c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeUnit Benchmark::GetTimeUnit() const {
|
||||||
|
return use_default_time_unit_ ? GetDefaultTimeUnit() : time_unit_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================//
|
||||||
|
// FunctionBenchmark
|
||||||
|
//=============================================================================//
|
||||||
|
|
||||||
|
void FunctionBenchmark::Run(State& st) { func_(st); }
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
|
||||||
|
void ClearRegisteredBenchmarks() {
|
||||||
|
internal::BenchmarkFamilies::GetInstance()->ClearBenchmarks();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int64_t> CreateRange(int64_t lo, int64_t hi, int multi) {
|
||||||
|
std::vector<int64_t> args;
|
||||||
|
internal::AddRange(&args, lo, hi, multi);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int64_t> CreateDenseRange(int64_t start, int64_t limit, int step) {
|
||||||
|
BM_CHECK_LE(start, limit);
|
||||||
|
std::vector<int64_t> args;
|
||||||
|
for (int64_t arg = start; arg <= limit; arg += step) {
|
||||||
|
args.push_back(arg);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
109
third_party/benchmark/src/benchmark_register.h
vendored
Normal file
109
third_party/benchmark/src/benchmark_register.h
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#ifndef BENCHMARK_REGISTER_H
|
||||||
|
#define BENCHMARK_REGISTER_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Append the powers of 'mult' in the closed interval [lo, hi].
|
||||||
|
// Returns iterator to the start of the inserted range.
|
||||||
|
template <typename T>
|
||||||
|
typename std::vector<T>::iterator AddPowers(std::vector<T>* dst, T lo, T hi,
|
||||||
|
int mult) {
|
||||||
|
BM_CHECK_GE(lo, 0);
|
||||||
|
BM_CHECK_GE(hi, lo);
|
||||||
|
BM_CHECK_GE(mult, 2);
|
||||||
|
|
||||||
|
const size_t start_offset = dst->size();
|
||||||
|
|
||||||
|
static const T kmax = std::numeric_limits<T>::max();
|
||||||
|
|
||||||
|
// Space out the values in multiples of "mult"
|
||||||
|
for (T i = static_cast<T>(1); i <= hi; i = static_cast<T>(i * mult)) {
|
||||||
|
if (i >= lo) {
|
||||||
|
dst->push_back(i);
|
||||||
|
}
|
||||||
|
// Break the loop here since multiplying by
|
||||||
|
// 'mult' would move outside of the range of T
|
||||||
|
if (i > kmax / mult) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst->begin() + static_cast<int>(start_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void AddNegatedPowers(std::vector<T>* dst, T lo, T hi, int mult) {
|
||||||
|
// We negate lo and hi so we require that they cannot be equal to 'min'.
|
||||||
|
BM_CHECK_GT(lo, std::numeric_limits<T>::min());
|
||||||
|
BM_CHECK_GT(hi, std::numeric_limits<T>::min());
|
||||||
|
BM_CHECK_GE(hi, lo);
|
||||||
|
BM_CHECK_LE(hi, 0);
|
||||||
|
|
||||||
|
// Add positive powers, then negate and reverse.
|
||||||
|
// Casts necessary since small integers get promoted
|
||||||
|
// to 'int' when negating.
|
||||||
|
const auto lo_complement = static_cast<T>(-lo);
|
||||||
|
const auto hi_complement = static_cast<T>(-hi);
|
||||||
|
|
||||||
|
const auto it = AddPowers(dst, hi_complement, lo_complement, mult);
|
||||||
|
|
||||||
|
std::for_each(it, dst->end(), [](T& t) { t = static_cast<T>(t * -1); });
|
||||||
|
std::reverse(it, dst->end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void AddRange(std::vector<T>* dst, T lo, T hi, int mult) {
|
||||||
|
static_assert(std::is_integral<T>::value && std::is_signed<T>::value,
|
||||||
|
"Args type must be a signed integer");
|
||||||
|
|
||||||
|
BM_CHECK_GE(hi, lo);
|
||||||
|
BM_CHECK_GE(mult, 2);
|
||||||
|
|
||||||
|
// Add "lo"
|
||||||
|
dst->push_back(lo);
|
||||||
|
|
||||||
|
// Handle lo == hi as a special case, so we then know
|
||||||
|
// lo < hi and so it is safe to add 1 to lo and subtract 1
|
||||||
|
// from hi without falling outside of the range of T.
|
||||||
|
if (lo == hi) return;
|
||||||
|
|
||||||
|
// Ensure that lo_inner <= hi_inner below.
|
||||||
|
if (lo + 1 == hi) {
|
||||||
|
dst->push_back(hi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all powers of 'mult' in the range [lo+1, hi-1] (inclusive).
|
||||||
|
const auto lo_inner = static_cast<T>(lo + 1);
|
||||||
|
const auto hi_inner = static_cast<T>(hi - 1);
|
||||||
|
|
||||||
|
// Insert negative values
|
||||||
|
if (lo_inner < 0) {
|
||||||
|
AddNegatedPowers(dst, lo_inner, std::min(hi_inner, T{-1}), mult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat 0 as a special case (see discussion on #762).
|
||||||
|
if (lo < 0 && hi >= 0) {
|
||||||
|
dst->push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert positive values
|
||||||
|
if (hi_inner > 0) {
|
||||||
|
AddPowers(dst, std::max(lo_inner, T{1}), hi_inner, mult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add "hi" (if different from last value).
|
||||||
|
if (hi != dst->back()) {
|
||||||
|
dst->push_back(hi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_REGISTER_H
|
498
third_party/benchmark/src/benchmark_runner.cc
vendored
Normal file
498
third_party/benchmark/src/benchmark_runner.cc
vendored
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
// 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_runner.h"
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "benchmark_api_internal.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_OS_WINDOWS
|
||||||
|
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "colorprint.h"
|
||||||
|
#include "commandlineflags.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#include "counter.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "perf_counters.h"
|
||||||
|
#include "re.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "thread_manager.h"
|
||||||
|
#include "thread_timer.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
MemoryManager* memory_manager = nullptr;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static constexpr IterationCount kMaxIterations = 1000000000000;
|
||||||
|
const double kDefaultMinTime =
|
||||||
|
std::strtod(::benchmark::kDefaultMinTimeStr, /*p_end*/ nullptr);
|
||||||
|
|
||||||
|
BenchmarkReporter::Run CreateRunReport(
|
||||||
|
const benchmark::internal::BenchmarkInstance& b,
|
||||||
|
const internal::ThreadManager::Result& results,
|
||||||
|
IterationCount memory_iterations,
|
||||||
|
const MemoryManager::Result* memory_result, double seconds,
|
||||||
|
int64_t repetition_index, int64_t repeats) {
|
||||||
|
// Create report about this benchmark run.
|
||||||
|
BenchmarkReporter::Run report;
|
||||||
|
|
||||||
|
report.run_name = b.name();
|
||||||
|
report.family_index = b.family_index();
|
||||||
|
report.per_family_instance_index = b.per_family_instance_index();
|
||||||
|
report.skipped = results.skipped_;
|
||||||
|
report.skip_message = results.skip_message_;
|
||||||
|
report.report_label = results.report_label_;
|
||||||
|
// This is the total iterations across all threads.
|
||||||
|
report.iterations = results.iterations;
|
||||||
|
report.time_unit = b.time_unit();
|
||||||
|
report.threads = b.threads();
|
||||||
|
report.repetition_index = repetition_index;
|
||||||
|
report.repetitions = repeats;
|
||||||
|
|
||||||
|
if (!report.skipped) {
|
||||||
|
if (b.use_manual_time()) {
|
||||||
|
report.real_accumulated_time = results.manual_time_used;
|
||||||
|
} else {
|
||||||
|
report.real_accumulated_time = results.real_time_used;
|
||||||
|
}
|
||||||
|
report.use_real_time_for_initial_big_o = b.use_manual_time();
|
||||||
|
report.cpu_accumulated_time = results.cpu_time_used;
|
||||||
|
report.complexity_n = results.complexity_n;
|
||||||
|
report.complexity = b.complexity();
|
||||||
|
report.complexity_lambda = b.complexity_lambda();
|
||||||
|
report.statistics = &b.statistics();
|
||||||
|
report.counters = results.counters;
|
||||||
|
|
||||||
|
if (memory_iterations > 0) {
|
||||||
|
assert(memory_result != nullptr);
|
||||||
|
report.memory_result = memory_result;
|
||||||
|
report.allocs_per_iter =
|
||||||
|
memory_iterations ? static_cast<double>(memory_result->num_allocs) /
|
||||||
|
static_cast<double>(memory_iterations)
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal::Finish(&report.counters, results.iterations, seconds,
|
||||||
|
b.threads());
|
||||||
|
}
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute one thread of benchmark b for the specified number of iterations.
|
||||||
|
// Adds the stats collected for the thread into manager->results.
|
||||||
|
void RunInThread(const BenchmarkInstance* b, IterationCount iters,
|
||||||
|
int thread_id, ThreadManager* manager,
|
||||||
|
PerfCountersMeasurement* perf_counters_measurement) {
|
||||||
|
internal::ThreadTimer timer(
|
||||||
|
b->measure_process_cpu_time()
|
||||||
|
? internal::ThreadTimer::CreateProcessCpuTime()
|
||||||
|
: internal::ThreadTimer::Create());
|
||||||
|
|
||||||
|
State st =
|
||||||
|
b->Run(iters, thread_id, &timer, manager, perf_counters_measurement);
|
||||||
|
BM_CHECK(st.skipped() || st.iterations() >= st.max_iterations)
|
||||||
|
<< "Benchmark returned before State::KeepRunning() returned false!";
|
||||||
|
{
|
||||||
|
MutexLock l(manager->GetBenchmarkMutex());
|
||||||
|
internal::ThreadManager::Result& results = manager->results;
|
||||||
|
results.iterations += st.iterations();
|
||||||
|
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.complexity_n += st.complexity_length_n();
|
||||||
|
internal::Increment(&results.counters, st.counters);
|
||||||
|
}
|
||||||
|
manager->NotifyThreadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ComputeMinTime(const benchmark::internal::BenchmarkInstance& b,
|
||||||
|
const BenchTimeType& iters_or_time) {
|
||||||
|
if (!IsZero(b.min_time())) return b.min_time();
|
||||||
|
// If the flag was used to specify number of iters, then return the default
|
||||||
|
// min_time.
|
||||||
|
if (iters_or_time.tag == BenchTimeType::ITERS) return kDefaultMinTime;
|
||||||
|
|
||||||
|
return iters_or_time.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
IterationCount ComputeIters(const benchmark::internal::BenchmarkInstance& b,
|
||||||
|
const BenchTimeType& iters_or_time) {
|
||||||
|
if (b.iterations() != 0) return b.iterations();
|
||||||
|
|
||||||
|
// We've already concluded that this flag is currently used to pass
|
||||||
|
// iters but do a check here again anyway.
|
||||||
|
BM_CHECK(iters_or_time.tag == BenchTimeType::ITERS);
|
||||||
|
return iters_or_time.iters;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
|
BenchTimeType ParseBenchMinTime(const std::string& value) {
|
||||||
|
BenchTimeType ret;
|
||||||
|
|
||||||
|
if (value.empty()) {
|
||||||
|
ret.tag = BenchTimeType::TIME;
|
||||||
|
ret.time = 0.0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.back() == 'x') {
|
||||||
|
char* p_end;
|
||||||
|
// Reset errno before it's changed by strtol.
|
||||||
|
errno = 0;
|
||||||
|
IterationCount num_iters = std::strtol(value.c_str(), &p_end, 10);
|
||||||
|
|
||||||
|
// After a valid parse, p_end should have been set to
|
||||||
|
// point to the 'x' suffix.
|
||||||
|
BM_CHECK(errno == 0 && p_end != nullptr && *p_end == 'x')
|
||||||
|
<< "Malformed iters value passed to --benchmark_min_time: `" << value
|
||||||
|
<< "`. Expected --benchmark_min_time=<integer>x.";
|
||||||
|
|
||||||
|
ret.tag = BenchTimeType::ITERS;
|
||||||
|
ret.iters = num_iters;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_suffix = value.back() == 's';
|
||||||
|
if (!has_suffix) {
|
||||||
|
BM_VLOG(0) << "Value passed to --benchmark_min_time should have a suffix. "
|
||||||
|
"Eg., `30s` for 30-seconds.";
|
||||||
|
}
|
||||||
|
|
||||||
|
char* p_end;
|
||||||
|
// Reset errno before it's changed by strtod.
|
||||||
|
errno = 0;
|
||||||
|
double min_time = std::strtod(value.c_str(), &p_end);
|
||||||
|
|
||||||
|
// After a successful parse, p_end should point to the suffix 's',
|
||||||
|
// or the end of the string if the suffix was omitted.
|
||||||
|
BM_CHECK(errno == 0 && p_end != nullptr &&
|
||||||
|
((has_suffix && *p_end == 's') || *p_end == '\0'))
|
||||||
|
<< "Malformed seconds value passed to --benchmark_min_time: `" << value
|
||||||
|
<< "`. Expected --benchmark_min_time=<float>x.";
|
||||||
|
|
||||||
|
ret.tag = BenchTimeType::TIME;
|
||||||
|
ret.time = min_time;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
BenchmarkRunner::BenchmarkRunner(
|
||||||
|
const benchmark::internal::BenchmarkInstance& b_,
|
||||||
|
PerfCountersMeasurement* pcm_,
|
||||||
|
BenchmarkReporter::PerFamilyRunReports* reports_for_family_)
|
||||||
|
: b(b_),
|
||||||
|
reports_for_family(reports_for_family_),
|
||||||
|
parsed_benchtime_flag(ParseBenchMinTime(FLAGS_benchmark_min_time)),
|
||||||
|
min_time(ComputeMinTime(b_, parsed_benchtime_flag)),
|
||||||
|
min_warmup_time((!IsZero(b.min_time()) && b.min_warmup_time() > 0.0)
|
||||||
|
? b.min_warmup_time()
|
||||||
|
: FLAGS_benchmark_min_warmup_time),
|
||||||
|
warmup_done(!(min_warmup_time > 0.0)),
|
||||||
|
repeats(b.repetitions() != 0 ? b.repetitions()
|
||||||
|
: FLAGS_benchmark_repetitions),
|
||||||
|
has_explicit_iteration_count(b.iterations() != 0 ||
|
||||||
|
parsed_benchtime_flag.tag ==
|
||||||
|
BenchTimeType::ITERS),
|
||||||
|
pool(static_cast<size_t>(b.threads() - 1)),
|
||||||
|
iters(has_explicit_iteration_count
|
||||||
|
? ComputeIters(b_, parsed_benchtime_flag)
|
||||||
|
: 1),
|
||||||
|
perf_counters_measurement_ptr(pcm_) {
|
||||||
|
run_results.display_report_aggregates_only =
|
||||||
|
(FLAGS_benchmark_report_aggregates_only ||
|
||||||
|
FLAGS_benchmark_display_aggregates_only);
|
||||||
|
run_results.file_report_aggregates_only =
|
||||||
|
FLAGS_benchmark_report_aggregates_only;
|
||||||
|
if (b.aggregation_report_mode() != internal::ARM_Unspecified) {
|
||||||
|
run_results.display_report_aggregates_only =
|
||||||
|
(b.aggregation_report_mode() &
|
||||||
|
internal::ARM_DisplayReportAggregatesOnly);
|
||||||
|
run_results.file_report_aggregates_only =
|
||||||
|
(b.aggregation_report_mode() & internal::ARM_FileReportAggregatesOnly);
|
||||||
|
BM_CHECK(FLAGS_benchmark_perf_counters.empty() ||
|
||||||
|
(perf_counters_measurement_ptr->num_counters() == 0))
|
||||||
|
<< "Perf counters were requested but could not be set up.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BenchmarkRunner::IterationResults BenchmarkRunner::DoNIterations() {
|
||||||
|
BM_VLOG(2) << "Running " << b.name().str() << " for " << iters << "\n";
|
||||||
|
|
||||||
|
std::unique_ptr<internal::ThreadManager> manager;
|
||||||
|
manager.reset(new internal::ThreadManager(b.threads()));
|
||||||
|
|
||||||
|
// Run all but one thread in separate 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(), perf_counters_measurement_ptr);
|
||||||
|
}
|
||||||
|
// And run one thread here directly.
|
||||||
|
// (If we were asked to run just one thread, we don't create new threads.)
|
||||||
|
// Yes, we need to do this here *after* we start the separate threads.
|
||||||
|
RunInThread(&b, iters, 0, manager.get(), perf_counters_measurement_ptr);
|
||||||
|
|
||||||
|
// The main thread has finished. Now let's wait for the other threads.
|
||||||
|
manager->WaitForAllThreads();
|
||||||
|
for (std::thread& thread : pool) thread.join();
|
||||||
|
|
||||||
|
IterationResults i;
|
||||||
|
// Acquire the measurements/counters from the manager, UNDER THE LOCK!
|
||||||
|
{
|
||||||
|
MutexLock l(manager->GetBenchmarkMutex());
|
||||||
|
i.results = manager->results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And get rid of the manager.
|
||||||
|
manager.reset();
|
||||||
|
|
||||||
|
// Adjust real/manual time stats since they were reported per thread.
|
||||||
|
i.results.real_time_used /= b.threads();
|
||||||
|
i.results.manual_time_used /= b.threads();
|
||||||
|
// If we were measuring whole-process CPU usage, adjust the CPU time too.
|
||||||
|
if (b.measure_process_cpu_time()) i.results.cpu_time_used /= b.threads();
|
||||||
|
|
||||||
|
BM_VLOG(2) << "Ran in " << i.results.cpu_time_used << "/"
|
||||||
|
<< i.results.real_time_used << "\n";
|
||||||
|
|
||||||
|
// By using KeepRunningBatch a benchmark can iterate more times than
|
||||||
|
// requested, so take the iteration count from i.results.
|
||||||
|
i.iters = i.results.iterations / b.threads();
|
||||||
|
|
||||||
|
// Base decisions off of real time if requested by this benchmark.
|
||||||
|
i.seconds = i.results.cpu_time_used;
|
||||||
|
if (b.use_manual_time()) {
|
||||||
|
i.seconds = i.results.manual_time_used;
|
||||||
|
} else if (b.use_real_time()) {
|
||||||
|
i.seconds = i.results.real_time_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
IterationCount BenchmarkRunner::PredictNumItersNeeded(
|
||||||
|
const IterationResults& i) const {
|
||||||
|
// See how much iterations should be increased by.
|
||||||
|
// Note: Avoid division by zero with max(seconds, 1ns).
|
||||||
|
double multiplier = GetMinTimeToApply() * 1.4 / std::max(i.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.
|
||||||
|
const bool is_significant = (i.seconds / GetMinTimeToApply()) > 0.1;
|
||||||
|
multiplier = is_significant ? multiplier : 10.0;
|
||||||
|
|
||||||
|
// So what seems to be the sufficiently-large iteration count? Round up.
|
||||||
|
const IterationCount max_next_iters = static_cast<IterationCount>(
|
||||||
|
std::llround(std::max(multiplier * static_cast<double>(i.iters),
|
||||||
|
static_cast<double>(i.iters) + 1.0)));
|
||||||
|
// But we do have *some* limits though..
|
||||||
|
const IterationCount next_iters = std::min(max_next_iters, kMaxIterations);
|
||||||
|
|
||||||
|
BM_VLOG(3) << "Next iters: " << next_iters << ", " << multiplier << "\n";
|
||||||
|
return next_iters; // round up before conversion to integer.
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BenchmarkRunner::ShouldReportIterationResults(
|
||||||
|
const IterationResults& i) const {
|
||||||
|
// Determine if this run should be reported;
|
||||||
|
// Either it has run for a sufficient amount of time
|
||||||
|
// or because an error was reported.
|
||||||
|
return i.results.skipped_ ||
|
||||||
|
i.iters >= kMaxIterations || // Too many iterations already.
|
||||||
|
i.seconds >=
|
||||||
|
GetMinTimeToApply() || // The elapsed time is large enough.
|
||||||
|
// CPU time is specified but the elapsed real time greatly exceeds
|
||||||
|
// the minimum time.
|
||||||
|
// Note that user provided timers are except from this test.
|
||||||
|
((i.results.real_time_used >= 5 * GetMinTimeToApply()) &&
|
||||||
|
!b.use_manual_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
double BenchmarkRunner::GetMinTimeToApply() const {
|
||||||
|
// In order to re-use functionality to run and measure benchmarks for running
|
||||||
|
// a warmup phase of the benchmark, we need a way of telling whether to apply
|
||||||
|
// min_time or min_warmup_time. This function will figure out if we are in the
|
||||||
|
// warmup phase and therefore need to apply min_warmup_time or if we already
|
||||||
|
// in the benchmarking phase and min_time needs to be applied.
|
||||||
|
return warmup_done ? min_time : min_warmup_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BenchmarkRunner::FinishWarmUp(const IterationCount& i) {
|
||||||
|
warmup_done = true;
|
||||||
|
iters = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BenchmarkRunner::RunWarmUp() {
|
||||||
|
// Use the same mechanisms for warming up the benchmark as used for actually
|
||||||
|
// running and measuring the benchmark.
|
||||||
|
IterationResults i_warmup;
|
||||||
|
// Dont use the iterations determined in the warmup phase for the actual
|
||||||
|
// measured benchmark phase. While this may be a good starting point for the
|
||||||
|
// benchmark and it would therefore get rid of the need to figure out how many
|
||||||
|
// iterations are needed if min_time is set again, this may also be a complete
|
||||||
|
// wrong guess since the warmup loops might be considerably slower (e.g
|
||||||
|
// because of caching effects).
|
||||||
|
const IterationCount i_backup = iters;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
b.Setup();
|
||||||
|
i_warmup = DoNIterations();
|
||||||
|
b.Teardown();
|
||||||
|
|
||||||
|
const bool finish = ShouldReportIterationResults(i_warmup);
|
||||||
|
|
||||||
|
if (finish) {
|
||||||
|
FinishWarmUp(i_backup);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although we are running "only" a warmup phase where running enough
|
||||||
|
// iterations at once without measuring time isn't as important as it is for
|
||||||
|
// the benchmarking phase, we still do it the same way as otherwise it is
|
||||||
|
// very confusing for the user to know how to choose a proper value for
|
||||||
|
// min_warmup_time if a different approach on running it is used.
|
||||||
|
iters = PredictNumItersNeeded(i_warmup);
|
||||||
|
assert(iters > i_warmup.iters &&
|
||||||
|
"if we did more iterations than we want to do the next time, "
|
||||||
|
"then we should have accepted the current iteration run.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BenchmarkRunner::DoOneRepetition() {
|
||||||
|
assert(HasRepeatsRemaining() && "Already done all repetitions?");
|
||||||
|
|
||||||
|
const bool is_the_first_repetition = num_repetitions_done == 0;
|
||||||
|
|
||||||
|
// In case a warmup phase is requested by the benchmark, run it now.
|
||||||
|
// After running the warmup phase the BenchmarkRunner should be in a state as
|
||||||
|
// this warmup never happened except the fact that warmup_done is set. Every
|
||||||
|
// other manipulation of the BenchmarkRunner instance would be a bug! Please
|
||||||
|
// fix it.
|
||||||
|
if (!warmup_done) RunWarmUp();
|
||||||
|
|
||||||
|
IterationResults i;
|
||||||
|
// We *may* be gradually increasing the length (iteration count)
|
||||||
|
// of the benchmark until we decide the results are significant.
|
||||||
|
// And once we do, we report those last results and exit.
|
||||||
|
// Please do note that the if there are repetitions, the iteration count
|
||||||
|
// is *only* calculated for the *first* repetition, and other repetitions
|
||||||
|
// simply use that precomputed iteration count.
|
||||||
|
for (;;) {
|
||||||
|
b.Setup();
|
||||||
|
i = DoNIterations();
|
||||||
|
b.Teardown();
|
||||||
|
|
||||||
|
// Do we consider the results to be significant?
|
||||||
|
// If we are doing repetitions, and the first repetition was already done,
|
||||||
|
// it has calculated the correct iteration time, so we have run that very
|
||||||
|
// iteration count just now. No need to calculate anything. Just report.
|
||||||
|
// Else, the normal rules apply.
|
||||||
|
const bool results_are_significant = !is_the_first_repetition ||
|
||||||
|
has_explicit_iteration_count ||
|
||||||
|
ShouldReportIterationResults(i);
|
||||||
|
|
||||||
|
if (results_are_significant) break; // Good, let's report them!
|
||||||
|
|
||||||
|
// Nope, bad iteration. Let's re-estimate the hopefully-sufficient
|
||||||
|
// iteration count, and run the benchmark again...
|
||||||
|
|
||||||
|
iters = PredictNumItersNeeded(i);
|
||||||
|
assert(iters > i.iters &&
|
||||||
|
"if we did more iterations than we want to do the next time, "
|
||||||
|
"then we should have accepted the current iteration run.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oh, one last thing, we need to also produce the 'memory measurements'..
|
||||||
|
MemoryManager::Result* memory_result = nullptr;
|
||||||
|
IterationCount memory_iterations = 0;
|
||||||
|
if (memory_manager != nullptr) {
|
||||||
|
// TODO(vyng): Consider making BenchmarkReporter::Run::memory_result an
|
||||||
|
// optional so we don't have to own the Result here.
|
||||||
|
// Can't do it now due to cxx03.
|
||||||
|
memory_results.push_back(MemoryManager::Result());
|
||||||
|
memory_result = &memory_results.back();
|
||||||
|
// Only run a few iterations to reduce the impact of one-time
|
||||||
|
// allocations in benchmarks that are not properly managed.
|
||||||
|
memory_iterations = std::min<IterationCount>(16, iters);
|
||||||
|
memory_manager->Start();
|
||||||
|
std::unique_ptr<internal::ThreadManager> manager;
|
||||||
|
manager.reset(new internal::ThreadManager(1));
|
||||||
|
b.Setup();
|
||||||
|
RunInThread(&b, memory_iterations, 0, manager.get(),
|
||||||
|
perf_counters_measurement_ptr);
|
||||||
|
manager->WaitForAllThreads();
|
||||||
|
manager.reset();
|
||||||
|
b.Teardown();
|
||||||
|
memory_manager->Stop(*memory_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, now actually report.
|
||||||
|
BenchmarkReporter::Run report =
|
||||||
|
CreateRunReport(b, i.results, memory_iterations, memory_result, i.seconds,
|
||||||
|
num_repetitions_done, repeats);
|
||||||
|
|
||||||
|
if (reports_for_family) {
|
||||||
|
++reports_for_family->num_runs_done;
|
||||||
|
if (!report.skipped) reports_for_family->Runs.push_back(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_results.non_aggregates.push_back(report);
|
||||||
|
|
||||||
|
++num_repetitions_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunResults&& BenchmarkRunner::GetResults() {
|
||||||
|
assert(!HasRepeatsRemaining() && "Did not run all repetitions yet?");
|
||||||
|
|
||||||
|
// Calculate additional statistics over the repetitions of this instance.
|
||||||
|
run_results.aggregates_only = ComputeStats(run_results.non_aggregates);
|
||||||
|
|
||||||
|
return std::move(run_results);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
131
third_party/benchmark/src/benchmark_runner.h
vendored
Normal file
131
third_party/benchmark/src/benchmark_runner.h
vendored
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// 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_RUNNER_H_
|
||||||
|
#define BENCHMARK_RUNNER_H_
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark_api_internal.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "perf_counters.h"
|
||||||
|
#include "thread_manager.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
BM_DECLARE_string(benchmark_min_time);
|
||||||
|
BM_DECLARE_double(benchmark_min_warmup_time);
|
||||||
|
BM_DECLARE_int32(benchmark_repetitions);
|
||||||
|
BM_DECLARE_bool(benchmark_report_aggregates_only);
|
||||||
|
BM_DECLARE_bool(benchmark_display_aggregates_only);
|
||||||
|
BM_DECLARE_string(benchmark_perf_counters);
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
extern MemoryManager* memory_manager;
|
||||||
|
|
||||||
|
struct RunResults {
|
||||||
|
std::vector<BenchmarkReporter::Run> non_aggregates;
|
||||||
|
std::vector<BenchmarkReporter::Run> aggregates_only;
|
||||||
|
|
||||||
|
bool display_report_aggregates_only = false;
|
||||||
|
bool file_report_aggregates_only = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BENCHMARK_EXPORT BenchTimeType {
|
||||||
|
enum { ITERS, TIME } tag;
|
||||||
|
union {
|
||||||
|
IterationCount iters;
|
||||||
|
double time;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
BenchTimeType ParseBenchMinTime(const std::string& value);
|
||||||
|
|
||||||
|
class BenchmarkRunner {
|
||||||
|
public:
|
||||||
|
BenchmarkRunner(const benchmark::internal::BenchmarkInstance& b_,
|
||||||
|
benchmark::internal::PerfCountersMeasurement* pmc_,
|
||||||
|
BenchmarkReporter::PerFamilyRunReports* reports_for_family);
|
||||||
|
|
||||||
|
int GetNumRepeats() const { return repeats; }
|
||||||
|
|
||||||
|
bool HasRepeatsRemaining() const {
|
||||||
|
return GetNumRepeats() != num_repetitions_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoOneRepetition();
|
||||||
|
|
||||||
|
RunResults&& GetResults();
|
||||||
|
|
||||||
|
BenchmarkReporter::PerFamilyRunReports* GetReportsForFamily() const {
|
||||||
|
return reports_for_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
double GetMinTime() const { return min_time; }
|
||||||
|
|
||||||
|
bool HasExplicitIters() const { return has_explicit_iteration_count; }
|
||||||
|
|
||||||
|
IterationCount GetIters() const { return iters; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RunResults run_results;
|
||||||
|
|
||||||
|
const benchmark::internal::BenchmarkInstance& b;
|
||||||
|
BenchmarkReporter::PerFamilyRunReports* reports_for_family;
|
||||||
|
|
||||||
|
BenchTimeType parsed_benchtime_flag;
|
||||||
|
const double min_time;
|
||||||
|
const double min_warmup_time;
|
||||||
|
bool warmup_done;
|
||||||
|
const int repeats;
|
||||||
|
const bool has_explicit_iteration_count;
|
||||||
|
|
||||||
|
int num_repetitions_done = 0;
|
||||||
|
|
||||||
|
std::vector<std::thread> pool;
|
||||||
|
|
||||||
|
std::vector<MemoryManager::Result> memory_results;
|
||||||
|
|
||||||
|
IterationCount iters; // preserved between repetitions!
|
||||||
|
// So only the first repetition has to find/calculate it,
|
||||||
|
// the other repetitions will just use that precomputed iteration count.
|
||||||
|
|
||||||
|
PerfCountersMeasurement* const perf_counters_measurement_ptr = nullptr;
|
||||||
|
|
||||||
|
struct IterationResults {
|
||||||
|
internal::ThreadManager::Result results;
|
||||||
|
IterationCount iters;
|
||||||
|
double seconds;
|
||||||
|
};
|
||||||
|
IterationResults DoNIterations();
|
||||||
|
|
||||||
|
IterationCount PredictNumItersNeeded(const IterationResults& i) const;
|
||||||
|
|
||||||
|
bool ShouldReportIterationResults(const IterationResults& i) const;
|
||||||
|
|
||||||
|
double GetMinTimeToApply() const;
|
||||||
|
|
||||||
|
void FinishWarmUp(const IterationCount& i);
|
||||||
|
|
||||||
|
void RunWarmUp();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_RUNNER_H_
|
11
third_party/benchmark/src/check.cc
vendored
Normal file
11
third_party/benchmark/src/check.cc
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
static AbortHandlerT* handler = &std::abort;
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT AbortHandlerT*& GetAbortHandler() { return handler; }
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace benchmark
|
106
third_party/benchmark/src/check.h
vendored
Normal file
106
third_party/benchmark/src/check.h
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#ifndef CHECK_H_
|
||||||
|
#define CHECK_H_
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "benchmark/export.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define BENCHMARK_NOEXCEPT noexcept
|
||||||
|
#define BENCHMARK_NOEXCEPT_OP(x) noexcept(x)
|
||||||
|
#elif defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
#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_NOEXCEPT
|
||||||
|
#define BENCHMARK_NOEXCEPT_OP(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
typedef void(AbortHandlerT)();
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
AbortHandlerT*& GetAbortHandler();
|
||||||
|
|
||||||
|
BENCHMARK_NORETURN inline void CallAbortHandler() {
|
||||||
|
GetAbortHandler()();
|
||||||
|
std::abort(); // fallback to enforce noreturn
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckHandler is the class constructed by failing BM_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_; }
|
||||||
|
|
||||||
|
#if defined(COMPILER_MSVC)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4722)
|
||||||
|
#endif
|
||||||
|
BENCHMARK_NORETURN ~CheckHandler() BENCHMARK_NOEXCEPT_OP(false) {
|
||||||
|
log_ << std::endl;
|
||||||
|
CallAbortHandler();
|
||||||
|
}
|
||||||
|
#if defined(COMPILER_MSVC)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CheckHandler& operator=(const CheckHandler&) = delete;
|
||||||
|
CheckHandler(const CheckHandler&) = delete;
|
||||||
|
CheckHandler() = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogType& log_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
// The BM_CHECK macro returns a std::ostream object that can have extra
|
||||||
|
// information written to it.
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define BM_CHECK(b) \
|
||||||
|
(b ? ::benchmark::internal::GetNullLogInstance() \
|
||||||
|
: ::benchmark::internal::CheckHandler(#b, __FILE__, __func__, __LINE__) \
|
||||||
|
.GetLog())
|
||||||
|
#else
|
||||||
|
#define BM_CHECK(b) ::benchmark::internal::GetNullLogInstance()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// preserve whitespacing between operators for alignment
|
||||||
|
#define BM_CHECK_EQ(a, b) BM_CHECK((a) == (b))
|
||||||
|
#define BM_CHECK_NE(a, b) BM_CHECK((a) != (b))
|
||||||
|
#define BM_CHECK_GE(a, b) BM_CHECK((a) >= (b))
|
||||||
|
#define BM_CHECK_LE(a, b) BM_CHECK((a) <= (b))
|
||||||
|
#define BM_CHECK_GT(a, b) BM_CHECK((a) > (b))
|
||||||
|
#define BM_CHECK_LT(a, b) BM_CHECK((a) < (b))
|
||||||
|
|
||||||
|
#define BM_CHECK_FLOAT_EQ(a, b, eps) BM_CHECK(std::fabs((a) - (b)) < (eps))
|
||||||
|
#define BM_CHECK_FLOAT_NE(a, b, eps) BM_CHECK(std::fabs((a) - (b)) >= (eps))
|
||||||
|
#define BM_CHECK_FLOAT_GE(a, b, eps) BM_CHECK((a) - (b) > -(eps))
|
||||||
|
#define BM_CHECK_FLOAT_LE(a, b, eps) BM_CHECK((b) - (a) > -(eps))
|
||||||
|
#define BM_CHECK_FLOAT_GT(a, b, eps) BM_CHECK((a) - (b) > (eps))
|
||||||
|
#define BM_CHECK_FLOAT_LT(a, b, eps) BM_CHECK((b) - (a) > (eps))
|
||||||
|
//clang-format on
|
||||||
|
|
||||||
|
#endif // CHECK_H_
|
200
third_party/benchmark/src/colorprint.cc
vendored
Normal file
200
third_party/benchmark/src/colorprint.cc
vendored
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// 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 <io.h>
|
||||||
|
#include <windows.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 = vsnprintf(local_buff, size, msg, args_cp);
|
||||||
|
|
||||||
|
va_end(args_cp);
|
||||||
|
|
||||||
|
// currently there is no error handling for failure, so this is hack.
|
||||||
|
BM_CHECK(ret >= 0);
|
||||||
|
|
||||||
|
if (ret == 0) { // handle empty expansion
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (static_cast<size_t>(ret) < size) {
|
||||||
|
return local_buff;
|
||||||
|
}
|
||||||
|
// we did not provide a long enough buffer on our first attempt.
|
||||||
|
size = static_cast<size_t>(ret) + 1; // + 1 for the null byte
|
||||||
|
std::unique_ptr<char[]> buff(new char[size]);
|
||||||
|
ret = vsnprintf(buff.get(), size, msg, args);
|
||||||
|
BM_CHECK(ret > 0 && (static_cast<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.
|
||||||
|
out.flush();
|
||||||
|
SetConsoleTextAttribute(stdout_handle,
|
||||||
|
GetPlatformColorCode(color) | FOREGROUND_INTENSITY);
|
||||||
|
out << FormatString(fmt, args);
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
// 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/v1.13.0/googletest/src/gtest.cc#L3225-L3259>.
|
||||||
|
const char* const SUPPORTED_TERM_VALUES[] = {
|
||||||
|
"xterm",
|
||||||
|
"xterm-color",
|
||||||
|
"xterm-256color",
|
||||||
|
"screen",
|
||||||
|
"screen-256color",
|
||||||
|
"tmux",
|
||||||
|
"tmux-256color",
|
||||||
|
"rxvt-unicode",
|
||||||
|
"rxvt-unicode-256color",
|
||||||
|
"linux",
|
||||||
|
"cygwin",
|
||||||
|
"xterm-kitty",
|
||||||
|
"alacritty",
|
||||||
|
"foot",
|
||||||
|
"foot-extra",
|
||||||
|
"wezterm",
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
33
third_party/benchmark/src/colorprint.h
vendored
Normal file
33
third_party/benchmark/src/colorprint.h
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#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_
|
298
third_party/benchmark/src/commandlineflags.cc
vendored
Normal file
298
third_party/benchmark/src/commandlineflags.cc
vendored
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
// 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 <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "../src/string_util.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses 'str' into KV pairs. If successful, writes the result to *value and
|
||||||
|
// returns true; otherwise leaves *value unchanged and returns false.
|
||||||
|
bool ParseKvPairs(const std::string& src_text, const char* str,
|
||||||
|
std::map<std::string, std::string>* value) {
|
||||||
|
std::map<std::string, std::string> kvs;
|
||||||
|
for (const auto& kvpair : StrSplit(str, ',')) {
|
||||||
|
const auto kv = StrSplit(kvpair, '=');
|
||||||
|
if (kv.size() != 2) {
|
||||||
|
std::cerr << src_text << " is expected to be a comma-separated list of "
|
||||||
|
<< "<key>=<value> strings, but actually has value \"" << str
|
||||||
|
<< "\".\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!kvs.emplace(kv[0], kv[1]).second) {
|
||||||
|
std::cerr << src_text << " is expected to contain unique keys but key \""
|
||||||
|
<< kv[0] << "\" was repeated.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = kvs;
|
||||||
|
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 env_var;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool BoolFromEnv(const char* flag, bool default_val) {
|
||||||
|
const std::string env_var = FlagToEnvVar(flag);
|
||||||
|
const char* const value_str = getenv(env_var.c_str());
|
||||||
|
return value_str == nullptr ? default_val : IsTruthyFlagValue(value_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
int32_t Int32FromEnv(const char* flag, int32_t default_val) {
|
||||||
|
const std::string env_var = FlagToEnvVar(flag);
|
||||||
|
const char* const value_str = getenv(env_var.c_str());
|
||||||
|
int32_t value = default_val;
|
||||||
|
if (value_str == nullptr ||
|
||||||
|
!ParseInt32(std::string("Environment variable ") + env_var, value_str,
|
||||||
|
&value)) {
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
double DoubleFromEnv(const char* flag, double default_val) {
|
||||||
|
const std::string env_var = FlagToEnvVar(flag);
|
||||||
|
const char* const value_str = getenv(env_var.c_str());
|
||||||
|
double value = default_val;
|
||||||
|
if (value_str == nullptr ||
|
||||||
|
!ParseDouble(std::string("Environment variable ") + env_var, value_str,
|
||||||
|
&value)) {
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
const char* StringFromEnv(const char* flag, const char* default_val) {
|
||||||
|
const std::string env_var = FlagToEnvVar(flag);
|
||||||
|
const char* const value = getenv(env_var.c_str());
|
||||||
|
return value == nullptr ? default_val : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
std::map<std::string, std::string> KvPairsFromEnv(
|
||||||
|
const char* flag, std::map<std::string, std::string> default_val) {
|
||||||
|
const std::string env_var = FlagToEnvVar(flag);
|
||||||
|
const char* const value_str = getenv(env_var.c_str());
|
||||||
|
|
||||||
|
if (value_str == nullptr) return default_val;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> value;
|
||||||
|
if (!ParseKvPairs("Environment variable " + env_var, value_str, &value)) {
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool ParseKeyValueFlag(const char* str, const char* flag,
|
||||||
|
std::map<std::string, std::string>* value) {
|
||||||
|
const char* const value_str = ParseFlagValue(str, flag, false);
|
||||||
|
|
||||||
|
if (value_str == nullptr) return false;
|
||||||
|
|
||||||
|
for (const auto& kvpair : StrSplit(value_str, ',')) {
|
||||||
|
const auto kv = StrSplit(kvpair, '=');
|
||||||
|
if (kv.size() != 2) return false;
|
||||||
|
value->emplace(kv[0], kv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool IsFlag(const char* str, const char* flag) {
|
||||||
|
return (ParseFlagValue(str, flag, true) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool IsTruthyFlagValue(const std::string& value) {
|
||||||
|
if (value.size() == 1) {
|
||||||
|
char v = value[0];
|
||||||
|
return isalnum(v) &&
|
||||||
|
!(v == '0' || v == 'f' || v == 'F' || v == 'n' || v == 'N');
|
||||||
|
}
|
||||||
|
if (!value.empty()) {
|
||||||
|
std::string value_lower(value);
|
||||||
|
std::transform(value_lower.begin(), value_lower.end(), value_lower.begin(),
|
||||||
|
[](char c) { return static_cast<char>(::tolower(c)); });
|
||||||
|
return !(value_lower == "false" || value_lower == "no" ||
|
||||||
|
value_lower == "off");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
133
third_party/benchmark/src/commandlineflags.h
vendored
Normal file
133
third_party/benchmark/src/commandlineflags.h
vendored
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#ifndef BENCHMARK_COMMANDLINEFLAGS_H_
|
||||||
|
#define BENCHMARK_COMMANDLINEFLAGS_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "benchmark/export.h"
|
||||||
|
|
||||||
|
// Macro for referencing flags.
|
||||||
|
#define FLAG(name) FLAGS_##name
|
||||||
|
|
||||||
|
// Macros for declaring flags.
|
||||||
|
#define BM_DECLARE_bool(name) BENCHMARK_EXPORT extern bool FLAG(name)
|
||||||
|
#define BM_DECLARE_int32(name) BENCHMARK_EXPORT extern int32_t FLAG(name)
|
||||||
|
#define BM_DECLARE_double(name) BENCHMARK_EXPORT extern double FLAG(name)
|
||||||
|
#define BM_DECLARE_string(name) BENCHMARK_EXPORT extern std::string FLAG(name)
|
||||||
|
#define BM_DECLARE_kvpairs(name) \
|
||||||
|
BENCHMARK_EXPORT extern std::map<std::string, std::string> FLAG(name)
|
||||||
|
|
||||||
|
// Macros for defining flags.
|
||||||
|
#define BM_DEFINE_bool(name, default_val) \
|
||||||
|
BENCHMARK_EXPORT bool FLAG(name) = benchmark::BoolFromEnv(#name, default_val)
|
||||||
|
#define BM_DEFINE_int32(name, default_val) \
|
||||||
|
BENCHMARK_EXPORT int32_t FLAG(name) = \
|
||||||
|
benchmark::Int32FromEnv(#name, default_val)
|
||||||
|
#define BM_DEFINE_double(name, default_val) \
|
||||||
|
BENCHMARK_EXPORT double FLAG(name) = \
|
||||||
|
benchmark::DoubleFromEnv(#name, default_val)
|
||||||
|
#define BM_DEFINE_string(name, default_val) \
|
||||||
|
BENCHMARK_EXPORT std::string FLAG(name) = \
|
||||||
|
benchmark::StringFromEnv(#name, default_val)
|
||||||
|
#define BM_DEFINE_kvpairs(name, default_val) \
|
||||||
|
BENCHMARK_EXPORT std::map<std::string, std::string> FLAG(name) = \
|
||||||
|
benchmark::KvPairsFromEnv(#name, default_val)
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
// Parses a bool from the environment variable corresponding to the given flag.
|
||||||
|
//
|
||||||
|
// If the variable exists, returns IsTruthyFlagValue() value; if not,
|
||||||
|
// returns the given default value.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool BoolFromEnv(const char* flag, bool default_val);
|
||||||
|
|
||||||
|
// Parses an Int32 from the environment variable corresponding to the given
|
||||||
|
// flag.
|
||||||
|
//
|
||||||
|
// If the variable exists, returns ParseInt32() value; if not, returns
|
||||||
|
// the given default value.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
int32_t Int32FromEnv(const char* flag, int32_t default_val);
|
||||||
|
|
||||||
|
// Parses an Double from the environment variable corresponding to the given
|
||||||
|
// flag.
|
||||||
|
//
|
||||||
|
// If the variable exists, returns ParseDouble(); if not, returns
|
||||||
|
// the given default value.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
double DoubleFromEnv(const char* flag, double default_val);
|
||||||
|
|
||||||
|
// Parses a string from the environment variable corresponding to the given
|
||||||
|
// flag.
|
||||||
|
//
|
||||||
|
// If variable exists, returns its value; if not, returns
|
||||||
|
// the given default value.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
const char* StringFromEnv(const char* flag, const char* default_val);
|
||||||
|
|
||||||
|
// Parses a set of kvpairs from the environment variable corresponding to the
|
||||||
|
// given flag.
|
||||||
|
//
|
||||||
|
// If variable exists, returns its value; if not, returns
|
||||||
|
// the given default value.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
std::map<std::string, std::string> KvPairsFromEnv(
|
||||||
|
const char* flag, std::map<std::string, std::string> 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.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool ParseStringFlag(const char* str, const char* flag, std::string* value);
|
||||||
|
|
||||||
|
// Parses a string for a kvpairs flag in the form "--flag=key=value,key=value"
|
||||||
|
//
|
||||||
|
// On success, stores the value of the flag in *value and returns true. On
|
||||||
|
// failure returns false, though *value may have been mutated.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool ParseKeyValueFlag(const char* str, const char* flag,
|
||||||
|
std::map<std::string, std::string>* value);
|
||||||
|
|
||||||
|
// Returns true if the string matches the flag.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
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. Also returns false if the value matches
|
||||||
|
// one of 'no', 'false', 'off' (case-insensitive). As a special case, also
|
||||||
|
// returns true if value is the empty string.
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool IsTruthyFlagValue(const std::string& value);
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_COMMANDLINEFLAGS_H_
|
259
third_party/benchmark/src/complexity.cc
vendored
Normal file
259
third_party/benchmark/src/complexity.cc
vendored
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
// 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 "complexity.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
// Internal function to calculate the different scalability forms
|
||||||
|
BigOFunc* FittingCurve(BigO complexity) {
|
||||||
|
static const double kLog2E = 1.44269504088896340736;
|
||||||
|
switch (complexity) {
|
||||||
|
case oN:
|
||||||
|
return [](IterationCount n) -> double { return static_cast<double>(n); };
|
||||||
|
case oNSquared:
|
||||||
|
return [](IterationCount n) -> double { return std::pow(n, 2); };
|
||||||
|
case oNCubed:
|
||||||
|
return [](IterationCount n) -> double { return std::pow(n, 3); };
|
||||||
|
case oLogN:
|
||||||
|
/* Note: can't use log2 because Android's GNU STL lacks it */
|
||||||
|
return [](IterationCount n) {
|
||||||
|
return kLog2E * std::log(static_cast<double>(n));
|
||||||
|
};
|
||||||
|
case oNLogN:
|
||||||
|
/* Note: can't use log2 because Android's GNU STL lacks it */
|
||||||
|
return [](IterationCount n) {
|
||||||
|
return kLog2E * static_cast<double>(n) *
|
||||||
|
std::log(static_cast<double>(n));
|
||||||
|
};
|
||||||
|
case o1:
|
||||||
|
default:
|
||||||
|
return [](IterationCount) { 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 expression.
|
||||||
|
// - n : Vector containing the size of the benchmark tests.
|
||||||
|
// - time : Vector containing the times for the benchmark tests.
|
||||||
|
// - fitting_curve : lambda expression (e.g. [](ComplexityN n) {return n; };).
|
||||||
|
|
||||||
|
// For a deeper explanation on the algorithm logic, please refer to
|
||||||
|
// https://en.wikipedia.org/wiki/Least_squares#Least_squares,_regression_analysis_and_statistics
|
||||||
|
|
||||||
|
LeastSq MinimalLeastSq(const std::vector<ComplexityN>& n,
|
||||||
|
const std::vector<double>& time,
|
||||||
|
BigOFunc* fitting_curve) {
|
||||||
|
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_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 += std::pow((time[i] - fit), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalized RMS by the mean of the observed values
|
||||||
|
double mean = sigma_time / static_cast<double>(n.size());
|
||||||
|
result.rms = std::sqrt(rms / static_cast<double>(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<ComplexityN>& n,
|
||||||
|
const std::vector<double>& time, const BigO complexity) {
|
||||||
|
BM_CHECK_EQ(n.size(), time.size());
|
||||||
|
BM_CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two
|
||||||
|
// benchmark runs are given
|
||||||
|
BM_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> ComputeBigO(
|
||||||
|
const std::vector<BenchmarkReporter::Run>& reports) {
|
||||||
|
typedef BenchmarkReporter::Run Run;
|
||||||
|
std::vector<Run> results;
|
||||||
|
|
||||||
|
if (reports.size() < 2) return results;
|
||||||
|
|
||||||
|
// Accumulators.
|
||||||
|
std::vector<ComplexityN> n;
|
||||||
|
std::vector<double> real_time;
|
||||||
|
std::vector<double> cpu_time;
|
||||||
|
|
||||||
|
// Populate the accumulators.
|
||||||
|
for (const Run& run : reports) {
|
||||||
|
BM_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 /
|
||||||
|
static_cast<double>(run.iterations));
|
||||||
|
cpu_time.push_back(run.cpu_accumulated_time /
|
||||||
|
static_cast<double>(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 {
|
||||||
|
const BigO* InitialBigO = &reports[0].complexity;
|
||||||
|
const bool use_real_time_for_initial_big_o =
|
||||||
|
reports[0].use_real_time_for_initial_big_o;
|
||||||
|
if (use_real_time_for_initial_big_o) {
|
||||||
|
result_real = MinimalLeastSq(n, real_time, *InitialBigO);
|
||||||
|
InitialBigO = &result_real.complexity;
|
||||||
|
// The Big-O complexity for CPU time must have the same Big-O function!
|
||||||
|
}
|
||||||
|
result_cpu = MinimalLeastSq(n, cpu_time, *InitialBigO);
|
||||||
|
InitialBigO = &result_cpu.complexity;
|
||||||
|
if (!use_real_time_for_initial_big_o) {
|
||||||
|
result_real = MinimalLeastSq(n, real_time, *InitialBigO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the 'args' when reporting complexity.
|
||||||
|
auto run_name = reports[0].run_name;
|
||||||
|
run_name.args.clear();
|
||||||
|
|
||||||
|
// Get the data from the accumulator to BenchmarkReporter::Run's.
|
||||||
|
Run big_o;
|
||||||
|
big_o.run_name = run_name;
|
||||||
|
big_o.family_index = reports[0].family_index;
|
||||||
|
big_o.per_family_instance_index = reports[0].per_family_instance_index;
|
||||||
|
big_o.run_type = BenchmarkReporter::Run::RT_Aggregate;
|
||||||
|
big_o.repetitions = reports[0].repetitions;
|
||||||
|
big_o.repetition_index = Run::no_repetition_index;
|
||||||
|
big_o.threads = reports[0].threads;
|
||||||
|
big_o.aggregate_name = "BigO";
|
||||||
|
big_o.aggregate_unit = StatisticUnit::kTime;
|
||||||
|
big_o.report_label = reports[0].report_label;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// All the time results are reported after being multiplied by the
|
||||||
|
// time unit multiplier. But since RMS is a relative quantity it
|
||||||
|
// should not be multiplied at all. So, here, we _divide_ it by the
|
||||||
|
// multiplier so that when it is multiplied later the result is the
|
||||||
|
// correct one.
|
||||||
|
double multiplier = GetTimeUnitMultiplier(reports[0].time_unit);
|
||||||
|
|
||||||
|
// Only add label to mean/stddev if it is same for all runs
|
||||||
|
Run rms;
|
||||||
|
rms.run_name = run_name;
|
||||||
|
rms.family_index = reports[0].family_index;
|
||||||
|
rms.per_family_instance_index = reports[0].per_family_instance_index;
|
||||||
|
rms.run_type = BenchmarkReporter::Run::RT_Aggregate;
|
||||||
|
rms.aggregate_name = "RMS";
|
||||||
|
rms.aggregate_unit = StatisticUnit::kPercentage;
|
||||||
|
rms.report_label = big_o.report_label;
|
||||||
|
rms.iterations = 0;
|
||||||
|
rms.repetition_index = Run::no_repetition_index;
|
||||||
|
rms.repetitions = reports[0].repetitions;
|
||||||
|
rms.threads = reports[0].threads;
|
||||||
|
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;
|
||||||
|
// don't forget to keep the time unit, or we won't be able to
|
||||||
|
// recover the correct value.
|
||||||
|
rms.time_unit = reports[0].time_unit;
|
||||||
|
|
||||||
|
results.push_back(big_o);
|
||||||
|
results.push_back(rms);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
55
third_party/benchmark/src/complexity.h
vendored
Normal file
55
third_party/benchmark/src/complexity.h
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// 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.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
// 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 coefficient 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_
|
210
third_party/benchmark/src/console_reporter.cc
vendored
Normal file
210
third_party/benchmark/src/console_reporter.cc
vendored
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// 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 <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "colorprint.h"
|
||||||
|
#include "commandlineflags.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#include "counter.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool ConsoleReporter::ReportContext(const Context& context) {
|
||||||
|
name_field_width_ = context.name_field_width;
|
||||||
|
printed_header_ = false;
|
||||||
|
prev_counters_.clear();
|
||||||
|
|
||||||
|
PrintBasicContext(&GetErrorStream(), context);
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_OS_WINDOWS
|
||||||
|
if ((output_options_ & OO_Color)) {
|
||||||
|
auto stdOutBuf = std::cout.rdbuf();
|
||||||
|
auto outStreamBuf = GetOutputStream().rdbuf();
|
||||||
|
if (stdOutBuf != outStreamBuf) {
|
||||||
|
GetErrorStream()
|
||||||
|
<< "Color printing is only supported for stdout on windows."
|
||||||
|
" Disabling color printing\n";
|
||||||
|
output_options_ = static_cast<OutputOptions>(output_options_ & ~OO_Color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
void ConsoleReporter::PrintHeader(const Run& run) {
|
||||||
|
std::string str =
|
||||||
|
FormatString("%-*s %13s %15s %12s", static_cast<int>(name_field_width_),
|
||||||
|
"Benchmark", "Time", "CPU", "Iterations");
|
||||||
|
if (!run.counters.empty()) {
|
||||||
|
if (output_options_ & OO_Tabular) {
|
||||||
|
for (auto const& c : run.counters) {
|
||||||
|
str += FormatString(" %10s", c.first.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str += " UserCounters...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string line = std::string(str.length(), '-');
|
||||||
|
GetOutputStream() << line << "\n" << str << "\n" << line << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) {
|
||||||
|
for (const auto& run : reports) {
|
||||||
|
// print the header:
|
||||||
|
// --- if none was printed yet
|
||||||
|
bool print_header = !printed_header_;
|
||||||
|
// --- or if the format is tabular and this run
|
||||||
|
// has different fields from the prev header
|
||||||
|
print_header |= (output_options_ & OO_Tabular) &&
|
||||||
|
(!internal::SameNames(run.counters, prev_counters_));
|
||||||
|
if (print_header) {
|
||||||
|
printed_header_ = true;
|
||||||
|
prev_counters_ = run.counters;
|
||||||
|
PrintHeader(run);
|
||||||
|
}
|
||||||
|
// As an alternative to printing the headers like this, we could sort
|
||||||
|
// the benchmarks by header and then print. But this would require
|
||||||
|
// waiting for the full results before printing, or printing twice.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string FormatTime(double time) {
|
||||||
|
// For the time columns of the console printer 13 digits are reserved. One of
|
||||||
|
// them is a space and max two of them are the time unit (e.g ns). That puts
|
||||||
|
// us at 10 digits usable for the number.
|
||||||
|
// Align decimal places...
|
||||||
|
if (time < 1.0) {
|
||||||
|
return FormatString("%10.3f", time);
|
||||||
|
}
|
||||||
|
if (time < 10.0) {
|
||||||
|
return FormatString("%10.2f", time);
|
||||||
|
}
|
||||||
|
if (time < 100.0) {
|
||||||
|
return FormatString("%10.1f", time);
|
||||||
|
}
|
||||||
|
// Assuming the time is at max 9.9999e+99 and we have 10 digits for the
|
||||||
|
// number, we get 10-1(.)-1(e)-1(sign)-2(exponent) = 5 digits to print.
|
||||||
|
if (time > 9999999999 /*max 10 digit number*/) {
|
||||||
|
return FormatString("%1.4e", time);
|
||||||
|
}
|
||||||
|
return FormatString("%10.0f", time);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
void ConsoleReporter::PrintRunData(const Run& result) {
|
||||||
|
typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...);
|
||||||
|
auto& Out = GetOutputStream();
|
||||||
|
PrinterFn* printer = (output_options_ & OO_Color)
|
||||||
|
? static_cast<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 (internal::SkippedWithError == result.skipped) {
|
||||||
|
printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'",
|
||||||
|
result.skip_message.c_str());
|
||||||
|
printer(Out, COLOR_DEFAULT, "\n");
|
||||||
|
return;
|
||||||
|
} else if (internal::SkippedWithMessage == result.skipped) {
|
||||||
|
printer(Out, COLOR_WHITE, "SKIPPED: \'%s\'", result.skip_message.c_str());
|
||||||
|
printer(Out, COLOR_DEFAULT, "\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double real_time = result.GetAdjustedRealTime();
|
||||||
|
const double cpu_time = result.GetAdjustedCPUTime();
|
||||||
|
const std::string real_time_str = FormatTime(real_time);
|
||||||
|
const std::string cpu_time_str = FormatTime(cpu_time);
|
||||||
|
|
||||||
|
if (result.report_big_o) {
|
||||||
|
std::string big_o = GetBigOString(result.complexity);
|
||||||
|
printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ", real_time,
|
||||||
|
big_o.c_str(), cpu_time, big_o.c_str());
|
||||||
|
} else if (result.report_rms) {
|
||||||
|
printer(Out, COLOR_YELLOW, "%10.0f %-4s %10.0f %-4s ", real_time * 100, "%",
|
||||||
|
cpu_time * 100, "%");
|
||||||
|
} else if (result.run_type != Run::RT_Aggregate ||
|
||||||
|
result.aggregate_unit == StatisticUnit::kTime) {
|
||||||
|
const char* timeLabel = GetTimeUnitString(result.time_unit);
|
||||||
|
printer(Out, COLOR_YELLOW, "%s %-4s %s %-4s ", real_time_str.c_str(),
|
||||||
|
timeLabel, cpu_time_str.c_str(), timeLabel);
|
||||||
|
} else {
|
||||||
|
assert(result.aggregate_unit == StatisticUnit::kPercentage);
|
||||||
|
printer(Out, COLOR_YELLOW, "%10.2f %-4s %10.2f %-4s ",
|
||||||
|
(100. * result.real_accumulated_time), "%",
|
||||||
|
(100. * result.cpu_accumulated_time), "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.report_big_o && !result.report_rms) {
|
||||||
|
printer(Out, COLOR_CYAN, "%10lld", result.iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& c : result.counters) {
|
||||||
|
const std::size_t cNameLen =
|
||||||
|
std::max(std::string::size_type(10), c.first.length());
|
||||||
|
std::string s;
|
||||||
|
const char* unit = "";
|
||||||
|
if (result.run_type == Run::RT_Aggregate &&
|
||||||
|
result.aggregate_unit == StatisticUnit::kPercentage) {
|
||||||
|
s = StrFormat("%.2f", 100. * c.second.value);
|
||||||
|
unit = "%";
|
||||||
|
} else {
|
||||||
|
s = HumanReadableNumber(c.second.value, c.second.oneK);
|
||||||
|
if (c.second.flags & Counter::kIsRate)
|
||||||
|
unit = (c.second.flags & Counter::kInvert) ? "s" : "/s";
|
||||||
|
}
|
||||||
|
if (output_options_ & OO_Tabular) {
|
||||||
|
printer(Out, COLOR_DEFAULT, " %*s%s", cNameLen - strlen(unit), s.c_str(),
|
||||||
|
unit);
|
||||||
|
} else {
|
||||||
|
printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.report_label.empty()) {
|
||||||
|
printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
printer(Out, COLOR_DEFAULT, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
80
third_party/benchmark/src/counter.cc
vendored
Normal file
80
third_party/benchmark/src/counter.cc
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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 "counter.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
double Finish(Counter const& c, IterationCount iterations, double cpu_time,
|
||||||
|
double num_threads) {
|
||||||
|
double v = c.value;
|
||||||
|
if (c.flags & Counter::kIsRate) {
|
||||||
|
v /= cpu_time;
|
||||||
|
}
|
||||||
|
if (c.flags & Counter::kAvgThreads) {
|
||||||
|
v /= num_threads;
|
||||||
|
}
|
||||||
|
if (c.flags & Counter::kIsIterationInvariant) {
|
||||||
|
v *= static_cast<double>(iterations);
|
||||||
|
}
|
||||||
|
if (c.flags & Counter::kAvgIterations) {
|
||||||
|
v /= static_cast<double>(iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.flags & Counter::kInvert) { // Invert is *always* last.
|
||||||
|
v = 1.0 / v;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finish(UserCounters* l, IterationCount iterations, double cpu_time,
|
||||||
|
double num_threads) {
|
||||||
|
for (auto& c : *l) {
|
||||||
|
c.second.value = Finish(c.second, iterations, cpu_time, num_threads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Increment(UserCounters* l, UserCounters const& r) {
|
||||||
|
// add counters present in both or just in *l
|
||||||
|
for (auto& c : *l) {
|
||||||
|
auto it = r.find(c.first);
|
||||||
|
if (it != r.end()) {
|
||||||
|
c.second.value = c.second + it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add counters present in r, but not in *l
|
||||||
|
for (auto const& tc : r) {
|
||||||
|
auto it = l->find(tc.first);
|
||||||
|
if (it == l->end()) {
|
||||||
|
(*l)[tc.first] = tc.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SameNames(UserCounters const& l, UserCounters const& r) {
|
||||||
|
if (&l == &r) return true;
|
||||||
|
if (l.size() != r.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (auto const& c : l) {
|
||||||
|
if (r.find(c.first) == r.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
} // end namespace benchmark
|
32
third_party/benchmark/src/counter.h
vendored
Normal file
32
third_party/benchmark/src/counter.h
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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_COUNTER_H_
|
||||||
|
#define BENCHMARK_COUNTER_H_
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
// these counter-related functions are hidden to reduce API surface.
|
||||||
|
namespace internal {
|
||||||
|
void Finish(UserCounters* l, IterationCount iterations, double time,
|
||||||
|
double num_threads);
|
||||||
|
void Increment(UserCounters* l, UserCounters const& r);
|
||||||
|
bool SameNames(UserCounters const& l, UserCounters const& r);
|
||||||
|
} // end namespace internal
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_COUNTER_H_
|
169
third_party/benchmark/src/csv_reporter.cc
vendored
Normal file
169
third_party/benchmark/src/csv_reporter.cc
vendored
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// 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 <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#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"};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string CsvEscape(const std::string& s) {
|
||||||
|
std::string tmp;
|
||||||
|
tmp.reserve(s.size() + 2);
|
||||||
|
for (char c : s) {
|
||||||
|
switch (c) {
|
||||||
|
case '"':
|
||||||
|
tmp += "\"\"";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tmp += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '"' + tmp + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
bool CSVReporter::ReportContext(const Context& context) {
|
||||||
|
PrintBasicContext(&GetErrorStream(), context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
void CSVReporter::ReportRuns(const std::vector<Run>& reports) {
|
||||||
|
std::ostream& Out = GetOutputStream();
|
||||||
|
|
||||||
|
if (!printed_header_) {
|
||||||
|
// save the names of all the user counters
|
||||||
|
for (const auto& run : reports) {
|
||||||
|
for (const auto& cnt : run.counters) {
|
||||||
|
if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
|
||||||
|
continue;
|
||||||
|
user_counter_names_.insert(cnt.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print the header
|
||||||
|
for (auto B = elements.begin(); B != elements.end();) {
|
||||||
|
Out << *B++;
|
||||||
|
if (B != elements.end()) Out << ",";
|
||||||
|
}
|
||||||
|
for (auto B = user_counter_names_.begin();
|
||||||
|
B != user_counter_names_.end();) {
|
||||||
|
Out << ",\"" << *B++ << "\"";
|
||||||
|
}
|
||||||
|
Out << "\n";
|
||||||
|
|
||||||
|
printed_header_ = true;
|
||||||
|
} else {
|
||||||
|
// check that all the current counters are saved in the name set
|
||||||
|
for (const auto& run : reports) {
|
||||||
|
for (const auto& cnt : run.counters) {
|
||||||
|
if (cnt.first == "bytes_per_second" || cnt.first == "items_per_second")
|
||||||
|
continue;
|
||||||
|
BM_CHECK(user_counter_names_.find(cnt.first) !=
|
||||||
|
user_counter_names_.end())
|
||||||
|
<< "All counters must be present in each run. "
|
||||||
|
<< "Counter named \"" << cnt.first
|
||||||
|
<< "\" was not in a run after being added to the header";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print results for each run
|
||||||
|
for (const auto& run : reports) {
|
||||||
|
PrintRunData(run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
void CSVReporter::PrintRunData(const Run& run) {
|
||||||
|
std::ostream& Out = GetOutputStream();
|
||||||
|
Out << CsvEscape(run.benchmark_name()) << ",";
|
||||||
|
if (run.skipped) {
|
||||||
|
Out << std::string(elements.size() - 3, ',');
|
||||||
|
Out << std::boolalpha << (internal::SkippedWithError == run.skipped) << ",";
|
||||||
|
Out << CsvEscape(run.skip_message) << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not print iteration on bigO and RMS report
|
||||||
|
if (!run.report_big_o && !run.report_rms) {
|
||||||
|
Out << run.iterations;
|
||||||
|
}
|
||||||
|
Out << ",";
|
||||||
|
|
||||||
|
if (run.run_type != Run::RT_Aggregate ||
|
||||||
|
run.aggregate_unit == StatisticUnit::kTime) {
|
||||||
|
Out << run.GetAdjustedRealTime() << ",";
|
||||||
|
Out << run.GetAdjustedCPUTime() << ",";
|
||||||
|
} else {
|
||||||
|
assert(run.aggregate_unit == StatisticUnit::kPercentage);
|
||||||
|
Out << run.real_accumulated_time << ",";
|
||||||
|
Out << run.cpu_accumulated_time << ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not print timeLabel on bigO and RMS report
|
||||||
|
if (run.report_big_o) {
|
||||||
|
Out << GetBigOString(run.complexity);
|
||||||
|
} else if (!run.report_rms &&
|
||||||
|
run.aggregate_unit != StatisticUnit::kPercentage) {
|
||||||
|
Out << GetTimeUnitString(run.time_unit);
|
||||||
|
}
|
||||||
|
Out << ",";
|
||||||
|
|
||||||
|
if (run.counters.find("bytes_per_second") != run.counters.end()) {
|
||||||
|
Out << run.counters.at("bytes_per_second");
|
||||||
|
}
|
||||||
|
Out << ",";
|
||||||
|
if (run.counters.find("items_per_second") != run.counters.end()) {
|
||||||
|
Out << run.counters.at("items_per_second");
|
||||||
|
}
|
||||||
|
Out << ",";
|
||||||
|
if (!run.report_label.empty()) {
|
||||||
|
Out << CsvEscape(run.report_label);
|
||||||
|
}
|
||||||
|
Out << ",,"; // for error_occurred and error_message
|
||||||
|
|
||||||
|
// Print user counters
|
||||||
|
for (const auto& ucn : user_counter_names_) {
|
||||||
|
auto it = run.counters.find(ucn);
|
||||||
|
if (it == run.counters.end()) {
|
||||||
|
Out << ",";
|
||||||
|
} else {
|
||||||
|
Out << "," << it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Out << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
242
third_party/benchmark/src/cycleclock.h
vendored
Normal file
242
third_party/benchmark/src/cycleclock.h
vendored
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// 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/benchmark.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) && !defined(_M_ARM64) && \
|
||||||
|
!defined(_M_ARM64EC)
|
||||||
|
extern "C" uint64_t __rdtsc();
|
||||||
|
#pragma intrinsic(__rdtsc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(BENCHMARK_OS_WINDOWS) || defined(BENCHMARK_OS_MINGW)
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_OS_EMSCRIPTEN
|
||||||
|
#include <emscripten.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 static_cast<int64_t>(mach_absolute_time());
|
||||||
|
#elif defined(BENCHMARK_OS_EMSCRIPTEN)
|
||||||
|
// this goes above x86-specific code because old versions of Emscripten
|
||||||
|
// define __x86_64__, although they have nothing to do with it.
|
||||||
|
return static_cast<int64_t>(emscripten_get_now() * 1e+6);
|
||||||
|
#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 static_cast<int64_t>((high << 32) | low);
|
||||||
|
#elif defined(__powerpc__) || defined(__ppc__)
|
||||||
|
// This returns a time-base, which is not always precisely a cycle-count.
|
||||||
|
#if defined(__powerpc64__) || defined(__ppc64__)
|
||||||
|
int64_t tb;
|
||||||
|
asm volatile("mfspr %0, 268" : "=r"(tb));
|
||||||
|
return tb;
|
||||||
|
#else
|
||||||
|
uint32_t tbl, tbu0, tbu1;
|
||||||
|
asm volatile(
|
||||||
|
"mftbu %0\n"
|
||||||
|
"mftb %1\n"
|
||||||
|
"mftbu %2"
|
||||||
|
: "=r"(tbu0), "=r"(tbl), "=r"(tbu1));
|
||||||
|
tbl &= -static_cast<int32_t>(tbu0 == tbu1);
|
||||||
|
// high 32 bits in tbu1; low 32 bits in tbl (tbu0 is no longer needed)
|
||||||
|
return (static_cast<uint64_t>(tbu1) << 32) | tbl;
|
||||||
|
#endif
|
||||||
|
#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) && (defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||||
|
// See // https://docs.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics
|
||||||
|
// and https://reviews.llvm.org/D53115
|
||||||
|
int64_t virtual_timer_value;
|
||||||
|
virtual_timer_value = _ReadStatusReg(ARM64_CNTVCT);
|
||||||
|
return virtual_timer_value;
|
||||||
|
#elif defined(COMPILER_MSVC)
|
||||||
|
return __rdtsc();
|
||||||
|
#elif defined(BENCHMARK_OS_NACL)
|
||||||
|
// Native Client validator on x86/x86-64 allows RDTSC instructions,
|
||||||
|
// and this case is handled above. Native Client validator on ARM
|
||||||
|
// rejects MRC instructions (used in the ARM-specific sequence below),
|
||||||
|
// so we handle it here. Portable Native Client compiles to
|
||||||
|
// architecture-agnostic bytecode, which doesn't provide any
|
||||||
|
// cycle counter access mnemonics.
|
||||||
|
|
||||||
|
// Native Client does not provide any API to access cycle counter.
|
||||||
|
// Use clock_gettime(CLOCK_MONOTONIC, ...) instead of gettimeofday
|
||||||
|
// because is provides nanosecond resolution (which is noticeable at
|
||||||
|
// least for PNaCl modules running on x86 Mac & Linux).
|
||||||
|
// Initialize to always return 0 if clock_gettime fails.
|
||||||
|
struct timespec ts = {0, 0};
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return static_cast<int64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
|
||||||
|
#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)
|
||||||
|
// V6 is the earliest arch that has a standard cyclecount
|
||||||
|
// Native Client validator doesn't allow MRC instructions.
|
||||||
|
#if (__ARM_ARCH >= 6)
|
||||||
|
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__) || defined(__m68k__)
|
||||||
|
// 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;
|
||||||
|
#elif defined(__loongarch__) || defined(__csky__)
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, nullptr);
|
||||||
|
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
|
||||||
|
#elif defined(__s390__) // Covers both s390 and s390x.
|
||||||
|
// Return the CPU clock.
|
||||||
|
uint64_t tsc;
|
||||||
|
#if defined(BENCHMARK_OS_ZOS)
|
||||||
|
// z/OS HLASM syntax.
|
||||||
|
asm(" stck %0" : "=m"(tsc) : : "cc");
|
||||||
|
#else
|
||||||
|
// Linux on Z syntax.
|
||||||
|
asm("stck %0" : "=Q"(tsc) : : "cc");
|
||||||
|
#endif
|
||||||
|
return tsc;
|
||||||
|
#elif defined(__riscv) // RISC-V
|
||||||
|
// Use RDTIME (and RDTIMEH on riscv32).
|
||||||
|
// RDCYCLE is a privileged instruction since Linux 6.6.
|
||||||
|
#if __riscv_xlen == 32
|
||||||
|
uint32_t cycles_lo, cycles_hi0, cycles_hi1;
|
||||||
|
// This asm also includes the PowerPC overflow handling strategy, as above.
|
||||||
|
// Implemented in assembly because Clang insisted on branching.
|
||||||
|
asm volatile(
|
||||||
|
"rdtimeh %0\n"
|
||||||
|
"rdtime %1\n"
|
||||||
|
"rdtimeh %2\n"
|
||||||
|
"sub %0, %0, %2\n"
|
||||||
|
"seqz %0, %0\n"
|
||||||
|
"sub %0, zero, %0\n"
|
||||||
|
"and %1, %1, %0\n"
|
||||||
|
: "=r"(cycles_hi0), "=r"(cycles_lo), "=r"(cycles_hi1));
|
||||||
|
return (static_cast<uint64_t>(cycles_hi1) << 32) | cycles_lo;
|
||||||
|
#else
|
||||||
|
uint64_t cycles;
|
||||||
|
asm volatile("rdtime %0" : "=r"(cycles));
|
||||||
|
return cycles;
|
||||||
|
#endif
|
||||||
|
#elif defined(__e2k__) || defined(__elbrus__)
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, nullptr);
|
||||||
|
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
|
||||||
|
#elif defined(__hexagon__)
|
||||||
|
uint64_t pcycle;
|
||||||
|
asm volatile("%0 = C15:14" : "=r"(pcycle));
|
||||||
|
return static_cast<double>(pcycle);
|
||||||
|
#elif defined(__alpha__)
|
||||||
|
// Alpha has a cycle counter, the PCC register, but it is an unsigned 32-bit
|
||||||
|
// integer and thus wraps every ~4s, making using it for tick counts
|
||||||
|
// unreliable beyond this time range. The real-time clock is low-precision,
|
||||||
|
// roughtly ~1ms, but it is the only option that can reasonable count
|
||||||
|
// indefinitely.
|
||||||
|
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_
|
111
third_party/benchmark/src/internal_macros.h
vendored
Normal file
111
third_party/benchmark/src/internal_macros.h
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#ifndef BENCHMARK_INTERNAL_MACROS_H_
|
||||||
|
#define BENCHMARK_INTERNAL_MACROS_H_
|
||||||
|
|
||||||
|
/* Needed to detect STL */
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#ifndef __has_feature
|
||||||
|
#define __has_feature(x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#if !defined(COMPILER_CLANG)
|
||||||
|
#define COMPILER_CLANG
|
||||||
|
#endif
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#if !defined(COMPILER_MSVC)
|
||||||
|
#define COMPILER_MSVC
|
||||||
|
#endif
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#if !defined(COMPILER_GCC)
|
||||||
|
#define COMPILER_GCC
|
||||||
|
#endif
|
||||||
|
#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
|
||||||
|
// WINAPI_FAMILY_PARTITION is defined in winapifamily.h.
|
||||||
|
// We include windows.h which implicitly includes winapifamily.h for compatibility.
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#if defined(WINAPI_FAMILY_PARTITION)
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
#define BENCHMARK_OS_WINDOWS_WIN32 1
|
||||||
|
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||||
|
#define BENCHMARK_OS_WINDOWS_RT 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
#define BENCHMARK_OS_MINGW 1
|
||||||
|
#endif
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define BENCHMARK_OS_APPLE 1
|
||||||
|
#include "TargetConditionals.h"
|
||||||
|
#if defined(TARGET_OS_MAC)
|
||||||
|
#define BENCHMARK_OS_MACOSX 1
|
||||||
|
#if defined(TARGET_OS_IPHONE)
|
||||||
|
#define BENCHMARK_OS_IOS 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#define BENCHMARK_OS_FREEBSD 1
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
#define BENCHMARK_OS_NETBSD 1
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#define BENCHMARK_OS_OPENBSD 1
|
||||||
|
#elif defined(__DragonFly__)
|
||||||
|
#define BENCHMARK_OS_DRAGONFLY 1
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define BENCHMARK_OS_LINUX 1
|
||||||
|
#elif defined(__native_client__)
|
||||||
|
#define BENCHMARK_OS_NACL 1
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
#define BENCHMARK_OS_EMSCRIPTEN 1
|
||||||
|
#elif defined(__rtems__)
|
||||||
|
#define BENCHMARK_OS_RTEMS 1
|
||||||
|
#elif defined(__Fuchsia__)
|
||||||
|
#define BENCHMARK_OS_FUCHSIA 1
|
||||||
|
#elif defined (__SVR4) && defined (__sun)
|
||||||
|
#define BENCHMARK_OS_SOLARIS 1
|
||||||
|
#elif defined(__QNX__)
|
||||||
|
#define BENCHMARK_OS_QNX 1
|
||||||
|
#elif defined(__MVS__)
|
||||||
|
#define BENCHMARK_OS_ZOS 1
|
||||||
|
#elif defined(__hexagon__)
|
||||||
|
#define BENCHMARK_OS_QURT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ANDROID__) && defined(__GLIBCXX__)
|
||||||
|
#define BENCHMARK_STL_ANDROID_GNUSTL 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !__has_feature(cxx_exceptions) && !defined(__cpp_exceptions) \
|
||||||
|
&& !defined(__EXCEPTIONS)
|
||||||
|
#define BENCHMARK_HAS_NO_EXCEPTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
|
||||||
|
#define BENCHMARK_MAYBE_UNUSED __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define BENCHMARK_MAYBE_UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#endif // BENCHMARK_INTERNAL_MACROS_H_
|
327
third_party/benchmark/src/json_reporter.cc
vendored
Normal file
327
third_party/benchmark/src/json_reporter.cc
vendored
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
// 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 <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iomanip> // for setprecision
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "complexity.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string StrEscape(const std::string& s) {
|
||||||
|
std::string tmp;
|
||||||
|
tmp.reserve(s.size());
|
||||||
|
for (char c : s) {
|
||||||
|
switch (c) {
|
||||||
|
case '\b':
|
||||||
|
tmp += "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
tmp += "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
tmp += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
tmp += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
tmp += "\\t";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
tmp += "\\\\";
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
tmp += "\\\"";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tmp += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatKV(std::string const& key, std::string const& value) {
|
||||||
|
return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(),
|
||||||
|
StrEscape(value).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatKV(std::string const& key, const char* value) {
|
||||||
|
return StrFormat("\"%s\": \"%s\"", StrEscape(key).c_str(),
|
||||||
|
StrEscape(value).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatKV(std::string const& key, bool value) {
|
||||||
|
return StrFormat("\"%s\": %s", StrEscape(key).c_str(),
|
||||||
|
value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatKV(std::string const& key, int64_t value) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << '"' << StrEscape(key) << "\": " << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatKV(std::string const& key, double value) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << '"' << StrEscape(key) << "\": ";
|
||||||
|
|
||||||
|
if (std::isnan(value))
|
||||||
|
ss << (value < 0 ? "-" : "") << "NaN";
|
||||||
|
else if (std::isinf(value))
|
||||||
|
ss << (value < 0 ? "-" : "") << "Infinity";
|
||||||
|
else {
|
||||||
|
const auto max_digits10 =
|
||||||
|
std::numeric_limits<decltype(value)>::max_digits10;
|
||||||
|
const auto max_fractional_digits10 = max_digits10 - 1;
|
||||||
|
ss << std::scientific << std::setprecision(max_fractional_digits10)
|
||||||
|
<< value;
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t RoundDouble(double v) { return std::lround(v); }
|
||||||
|
|
||||||
|
} // 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("host_name", context.sys_info.name) << ",\n";
|
||||||
|
|
||||||
|
if (Context::executable_name) {
|
||||||
|
out << indent << FormatKV("executable", Context::executable_name) << ",\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
CPUInfo const& info = context.cpu_info;
|
||||||
|
out << indent << FormatKV("num_cpus", static_cast<int64_t>(info.num_cpus))
|
||||||
|
<< ",\n";
|
||||||
|
out << indent
|
||||||
|
<< FormatKV("mhz_per_cpu",
|
||||||
|
RoundDouble(info.cycles_per_second / 1000000.0))
|
||||||
|
<< ",\n";
|
||||||
|
if (CPUInfo::Scaling::UNKNOWN != info.scaling) {
|
||||||
|
out << indent
|
||||||
|
<< FormatKV("cpu_scaling_enabled",
|
||||||
|
info.scaling == CPUInfo::Scaling::ENABLED ? true : false)
|
||||||
|
<< ",\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << indent << "\"caches\": [\n";
|
||||||
|
indent = std::string(6, ' ');
|
||||||
|
std::string cache_indent(8, ' ');
|
||||||
|
for (size_t i = 0; i < info.caches.size(); ++i) {
|
||||||
|
auto& CI = info.caches[i];
|
||||||
|
out << indent << "{\n";
|
||||||
|
out << cache_indent << FormatKV("type", CI.type) << ",\n";
|
||||||
|
out << cache_indent << FormatKV("level", static_cast<int64_t>(CI.level))
|
||||||
|
<< ",\n";
|
||||||
|
out << cache_indent << FormatKV("size", static_cast<int64_t>(CI.size))
|
||||||
|
<< ",\n";
|
||||||
|
out << cache_indent
|
||||||
|
<< FormatKV("num_sharing", static_cast<int64_t>(CI.num_sharing))
|
||||||
|
<< "\n";
|
||||||
|
out << indent << "}";
|
||||||
|
if (i != info.caches.size() - 1) out << ",";
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
indent = std::string(4, ' ');
|
||||||
|
out << indent << "],\n";
|
||||||
|
out << indent << "\"load_avg\": [";
|
||||||
|
for (auto it = info.load_avg.begin(); it != info.load_avg.end();) {
|
||||||
|
out << *it++;
|
||||||
|
if (it != info.load_avg.end()) out << ",";
|
||||||
|
}
|
||||||
|
out << "],\n";
|
||||||
|
|
||||||
|
out << indent << FormatKV("library_version", GetBenchmarkVersion());
|
||||||
|
out << ",\n";
|
||||||
|
|
||||||
|
#if defined(NDEBUG)
|
||||||
|
const char build_type[] = "release";
|
||||||
|
#else
|
||||||
|
const char build_type[] = "debug";
|
||||||
|
#endif
|
||||||
|
out << indent << FormatKV("library_build_type", build_type);
|
||||||
|
out << ",\n";
|
||||||
|
|
||||||
|
// NOTE: our json schema is not strictly tied to the library version!
|
||||||
|
out << indent << FormatKV("json_schema_version", int64_t(1));
|
||||||
|
|
||||||
|
std::map<std::string, std::string>* global_context =
|
||||||
|
internal::GetGlobalContext();
|
||||||
|
|
||||||
|
if (global_context != nullptr) {
|
||||||
|
for (const auto& kv : *global_context) {
|
||||||
|
out << ",\n";
|
||||||
|
out << indent << FormatKV(kv.first, kv.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << "\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";
|
||||||
|
out << indent << FormatKV("family_index", run.family_index) << ",\n";
|
||||||
|
out << indent
|
||||||
|
<< FormatKV("per_family_instance_index", run.per_family_instance_index)
|
||||||
|
<< ",\n";
|
||||||
|
out << indent << FormatKV("run_name", run.run_name.str()) << ",\n";
|
||||||
|
out << indent << FormatKV("run_type", [&run]() -> const char* {
|
||||||
|
switch (run.run_type) {
|
||||||
|
case BenchmarkReporter::Run::RT_Iteration:
|
||||||
|
return "iteration";
|
||||||
|
case BenchmarkReporter::Run::RT_Aggregate:
|
||||||
|
return "aggregate";
|
||||||
|
}
|
||||||
|
BENCHMARK_UNREACHABLE();
|
||||||
|
}()) << ",\n";
|
||||||
|
out << indent << FormatKV("repetitions", run.repetitions) << ",\n";
|
||||||
|
if (run.run_type != BenchmarkReporter::Run::RT_Aggregate) {
|
||||||
|
out << indent << FormatKV("repetition_index", run.repetition_index)
|
||||||
|
<< ",\n";
|
||||||
|
}
|
||||||
|
out << indent << FormatKV("threads", run.threads) << ",\n";
|
||||||
|
if (run.run_type == BenchmarkReporter::Run::RT_Aggregate) {
|
||||||
|
out << indent << FormatKV("aggregate_name", run.aggregate_name) << ",\n";
|
||||||
|
out << indent << FormatKV("aggregate_unit", [&run]() -> const char* {
|
||||||
|
switch (run.aggregate_unit) {
|
||||||
|
case StatisticUnit::kTime:
|
||||||
|
return "time";
|
||||||
|
case StatisticUnit::kPercentage:
|
||||||
|
return "percentage";
|
||||||
|
}
|
||||||
|
BENCHMARK_UNREACHABLE();
|
||||||
|
}()) << ",\n";
|
||||||
|
}
|
||||||
|
if (internal::SkippedWithError == run.skipped) {
|
||||||
|
out << indent << FormatKV("error_occurred", true) << ",\n";
|
||||||
|
out << indent << FormatKV("error_message", run.skip_message) << ",\n";
|
||||||
|
} else if (internal::SkippedWithMessage == run.skipped) {
|
||||||
|
out << indent << FormatKV("skipped", true) << ",\n";
|
||||||
|
out << indent << FormatKV("skip_message", run.skip_message) << ",\n";
|
||||||
|
}
|
||||||
|
if (!run.report_big_o && !run.report_rms) {
|
||||||
|
out << indent << FormatKV("iterations", run.iterations) << ",\n";
|
||||||
|
if (run.run_type != Run::RT_Aggregate ||
|
||||||
|
run.aggregate_unit == StatisticUnit::kTime) {
|
||||||
|
out << indent << FormatKV("real_time", run.GetAdjustedRealTime())
|
||||||
|
<< ",\n";
|
||||||
|
out << indent << FormatKV("cpu_time", run.GetAdjustedCPUTime());
|
||||||
|
} else {
|
||||||
|
assert(run.aggregate_unit == StatisticUnit::kPercentage);
|
||||||
|
out << indent << FormatKV("real_time", run.real_accumulated_time)
|
||||||
|
<< ",\n";
|
||||||
|
out << indent << FormatKV("cpu_time", run.cpu_accumulated_time);
|
||||||
|
}
|
||||||
|
out << ",\n"
|
||||||
|
<< indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit));
|
||||||
|
} else if (run.report_big_o) {
|
||||||
|
out << indent << FormatKV("cpu_coefficient", run.GetAdjustedCPUTime())
|
||||||
|
<< ",\n";
|
||||||
|
out << indent << FormatKV("real_coefficient", 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& c : run.counters) {
|
||||||
|
out << ",\n" << indent << FormatKV(c.first, c.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run.memory_result) {
|
||||||
|
const MemoryManager::Result memory_result = *run.memory_result;
|
||||||
|
out << ",\n" << indent << FormatKV("allocs_per_iter", run.allocs_per_iter);
|
||||||
|
out << ",\n"
|
||||||
|
<< indent << FormatKV("max_bytes_used", memory_result.max_bytes_used);
|
||||||
|
|
||||||
|
auto report_if_present = [&out, &indent](const std::string& label,
|
||||||
|
int64_t val) {
|
||||||
|
if (val != MemoryManager::TombstoneValue)
|
||||||
|
out << ",\n" << indent << FormatKV(label, val);
|
||||||
|
};
|
||||||
|
|
||||||
|
report_if_present("total_allocated_bytes",
|
||||||
|
memory_result.total_allocated_bytes);
|
||||||
|
report_if_present("net_heap_growth", memory_result.net_heap_growth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!run.report_label.empty()) {
|
||||||
|
out << ",\n" << indent << FormatKV("label", run.report_label);
|
||||||
|
}
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
const int64_t MemoryManager::TombstoneValue =
|
||||||
|
std::numeric_limits<int64_t>::max();
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
88
third_party/benchmark/src/log.h
vendored
Normal file
88
third_party/benchmark/src/log.h
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#ifndef BENCHMARK_LOG_H_
|
||||||
|
#define BENCHMARK_LOG_H_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
// NOTE: this is also defined in benchmark.h but we're trying to avoid a
|
||||||
|
// dependency.
|
||||||
|
// The _MSVC_LANG check should detect Visual Studio 2015 Update 3 and newer.
|
||||||
|
#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
|
||||||
|
#define BENCHMARK_HAS_CXX11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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_;
|
||||||
|
|
||||||
|
// NOTE: we could use BENCHMARK_DISALLOW_COPY_AND_ASSIGN but we shouldn't have
|
||||||
|
// a dependency on benchmark.h from here.
|
||||||
|
#ifndef BENCHMARK_HAS_CXX11
|
||||||
|
LogType(const LogType&);
|
||||||
|
LogType& operator=(const LogType&);
|
||||||
|
#else
|
||||||
|
LogType(const LogType&) = delete;
|
||||||
|
LogType& operator=(const LogType&) = delete;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
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 null_log(static_cast<std::ostream*>(nullptr));
|
||||||
|
return null_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LogType& GetErrorLogInstance() {
|
||||||
|
static LogType error_log(&std::clog);
|
||||||
|
return error_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LogType& GetLogInstanceForLevel(int level) {
|
||||||
|
if (level <= LogLevel()) {
|
||||||
|
return GetErrorLogInstance();
|
||||||
|
}
|
||||||
|
return GetNullLogInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace internal
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#define BM_VLOG(x) \
|
||||||
|
(::benchmark::internal::GetLogInstanceForLevel(x) << "-- LOG(" << x << "):" \
|
||||||
|
" ")
|
||||||
|
// clang-format on
|
||||||
|
#endif
|
155
third_party/benchmark/src/mutex.h
vendored
Normal file
155
third_party/benchmark/src/mutex.h
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#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 provide 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_) {
|
||||||
|
BM_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_
|
283
third_party/benchmark/src/perf_counters.cc
vendored
Normal file
283
third_party/benchmark/src/perf_counters.cc
vendored
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
// Copyright 2021 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 "perf_counters.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined HAVE_LIBPFM
|
||||||
|
#include "perfmon/pfmlib.h"
|
||||||
|
#include "perfmon/pfmlib_perf_event.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
constexpr size_t PerfCounterValues::kMaxCounters;
|
||||||
|
|
||||||
|
#if defined HAVE_LIBPFM
|
||||||
|
|
||||||
|
size_t PerfCounterValues::Read(const std::vector<int>& leaders) {
|
||||||
|
// Create a pointer for multiple reads
|
||||||
|
const size_t bufsize = values_.size() * sizeof(values_[0]);
|
||||||
|
char* ptr = reinterpret_cast<char*>(values_.data());
|
||||||
|
size_t size = bufsize;
|
||||||
|
for (int lead : leaders) {
|
||||||
|
auto read_bytes = ::read(lead, ptr, size);
|
||||||
|
if (read_bytes >= ssize_t(sizeof(uint64_t))) {
|
||||||
|
// Actual data bytes are all bytes minus initial padding
|
||||||
|
std::size_t data_bytes =
|
||||||
|
static_cast<std::size_t>(read_bytes) - sizeof(uint64_t);
|
||||||
|
// This should be very cheap since it's in hot cache
|
||||||
|
std::memmove(ptr, ptr + sizeof(uint64_t), data_bytes);
|
||||||
|
// Increment our counters
|
||||||
|
ptr += data_bytes;
|
||||||
|
size -= data_bytes;
|
||||||
|
} else {
|
||||||
|
int err = errno;
|
||||||
|
GetErrorLogInstance() << "Error reading lead " << lead << " errno:" << err
|
||||||
|
<< " " << ::strerror(err) << "\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (bufsize - size) / sizeof(uint64_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool PerfCounters::kSupported = true;
|
||||||
|
|
||||||
|
// Initializes libpfm only on the first call. Returns whether that single
|
||||||
|
// initialization was successful.
|
||||||
|
bool PerfCounters::Initialize() {
|
||||||
|
// Function-scope static gets initialized only once on first call.
|
||||||
|
static const bool success = []() {
|
||||||
|
return pfm_initialize() == PFM_SUCCESS;
|
||||||
|
}();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerfCounters::IsCounterSupported(const std::string& name) {
|
||||||
|
Initialize();
|
||||||
|
perf_event_attr_t attr;
|
||||||
|
std::memset(&attr, 0, sizeof(attr));
|
||||||
|
pfm_perf_encode_arg_t arg;
|
||||||
|
std::memset(&arg, 0, sizeof(arg));
|
||||||
|
arg.attr = &attr;
|
||||||
|
const int mode = PFM_PLM3; // user mode only
|
||||||
|
int ret = pfm_get_os_event_encoding(name.c_str(), mode, PFM_OS_PERF_EVENT_EXT,
|
||||||
|
&arg);
|
||||||
|
return (ret == PFM_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfCounters PerfCounters::Create(
|
||||||
|
const std::vector<std::string>& counter_names) {
|
||||||
|
if (!counter_names.empty()) {
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid counters will populate these arrays but we start empty
|
||||||
|
std::vector<std::string> valid_names;
|
||||||
|
std::vector<int> counter_ids;
|
||||||
|
std::vector<int> leader_ids;
|
||||||
|
|
||||||
|
// Resize to the maximum possible
|
||||||
|
valid_names.reserve(counter_names.size());
|
||||||
|
counter_ids.reserve(counter_names.size());
|
||||||
|
|
||||||
|
const int kCounterMode = PFM_PLM3; // user mode only
|
||||||
|
|
||||||
|
// Group leads will be assigned on demand. The idea is that once we cannot
|
||||||
|
// create a counter descriptor, the reason is that this group has maxed out
|
||||||
|
// so we set the group_id again to -1 and retry - giving the algorithm a
|
||||||
|
// chance to create a new group leader to hold the next set of counters.
|
||||||
|
int group_id = -1;
|
||||||
|
|
||||||
|
// Loop through all performance counters
|
||||||
|
for (size_t i = 0; i < counter_names.size(); ++i) {
|
||||||
|
// we are about to push into the valid names vector
|
||||||
|
// check if we did not reach the maximum
|
||||||
|
if (valid_names.size() == PerfCounterValues::kMaxCounters) {
|
||||||
|
// Log a message if we maxed out and stop adding
|
||||||
|
GetErrorLogInstance()
|
||||||
|
<< counter_names.size() << " counters were requested. The maximum is "
|
||||||
|
<< PerfCounterValues::kMaxCounters << " and " << valid_names.size()
|
||||||
|
<< " were already added. All remaining counters will be ignored\n";
|
||||||
|
// stop the loop and return what we have already
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this name is empty
|
||||||
|
const auto& name = counter_names[i];
|
||||||
|
if (name.empty()) {
|
||||||
|
GetErrorLogInstance()
|
||||||
|
<< "A performance counter name was the empty string\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here first means first in group, ie the group leader
|
||||||
|
const bool is_first = (group_id < 0);
|
||||||
|
|
||||||
|
// This struct will be populated by libpfm from the counter string
|
||||||
|
// and then fed into the syscall perf_event_open
|
||||||
|
struct perf_event_attr attr {};
|
||||||
|
attr.size = sizeof(attr);
|
||||||
|
|
||||||
|
// This is the input struct to libpfm.
|
||||||
|
pfm_perf_encode_arg_t arg{};
|
||||||
|
arg.attr = &attr;
|
||||||
|
const int pfm_get = pfm_get_os_event_encoding(name.c_str(), kCounterMode,
|
||||||
|
PFM_OS_PERF_EVENT, &arg);
|
||||||
|
if (pfm_get != PFM_SUCCESS) {
|
||||||
|
GetErrorLogInstance()
|
||||||
|
<< "Unknown performance counter name: " << name << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We then proceed to populate the remaining fields in our attribute struct
|
||||||
|
// Note: the man page for perf_event_create suggests inherit = true and
|
||||||
|
// read_format = PERF_FORMAT_GROUP don't work together, but that's not the
|
||||||
|
// case.
|
||||||
|
attr.disabled = is_first;
|
||||||
|
attr.inherit = true;
|
||||||
|
attr.pinned = is_first;
|
||||||
|
attr.exclude_kernel = true;
|
||||||
|
attr.exclude_user = false;
|
||||||
|
attr.exclude_hv = true;
|
||||||
|
|
||||||
|
// Read all counters in a group in one read.
|
||||||
|
attr.read_format = PERF_FORMAT_GROUP;
|
||||||
|
|
||||||
|
int id = -1;
|
||||||
|
while (id < 0) {
|
||||||
|
static constexpr size_t kNrOfSyscallRetries = 5;
|
||||||
|
// Retry syscall as it was interrupted often (b/64774091).
|
||||||
|
for (size_t num_retries = 0; num_retries < kNrOfSyscallRetries;
|
||||||
|
++num_retries) {
|
||||||
|
id = perf_event_open(&attr, 0, -1, group_id, 0);
|
||||||
|
if (id >= 0 || errno != EINTR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (id < 0) {
|
||||||
|
// If the file descriptor is negative we might have reached a limit
|
||||||
|
// in the current group. Set the group_id to -1 and retry
|
||||||
|
if (group_id >= 0) {
|
||||||
|
// Create a new group
|
||||||
|
group_id = -1;
|
||||||
|
} else {
|
||||||
|
// At this point we have already retried to set a new group id and
|
||||||
|
// failed. We then give up.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We failed to get a new file descriptor. We might have reached a hard
|
||||||
|
// hardware limit that cannot be resolved even with group multiplexing
|
||||||
|
if (id < 0) {
|
||||||
|
GetErrorLogInstance() << "***WARNING** Failed to get a file descriptor "
|
||||||
|
"for performance counter "
|
||||||
|
<< name << ". Ignoring\n";
|
||||||
|
|
||||||
|
// We give up on this counter but try to keep going
|
||||||
|
// as the others would be fine
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (group_id < 0) {
|
||||||
|
// This is a leader, store and assign it to the current file descriptor
|
||||||
|
leader_ids.push_back(id);
|
||||||
|
group_id = id;
|
||||||
|
}
|
||||||
|
// This is a valid counter, add it to our descriptor's list
|
||||||
|
counter_ids.push_back(id);
|
||||||
|
valid_names.push_back(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all group leaders activating them
|
||||||
|
// There is another option of starting ALL counters in a process but
|
||||||
|
// that would be far reaching an intrusion. If the user is using PMCs
|
||||||
|
// by themselves then this would have a side effect on them. It is
|
||||||
|
// friendlier to loop through all groups individually.
|
||||||
|
for (int lead : leader_ids) {
|
||||||
|
if (ioctl(lead, PERF_EVENT_IOC_ENABLE) != 0) {
|
||||||
|
// This should never happen but if it does, we give up on the
|
||||||
|
// entire batch as recovery would be a mess.
|
||||||
|
GetErrorLogInstance() << "***WARNING*** Failed to start counters. "
|
||||||
|
"Claring out all counters.\n";
|
||||||
|
|
||||||
|
// Close all peformance counters
|
||||||
|
for (int id : counter_ids) {
|
||||||
|
::close(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an empty object so our internal state is still good and
|
||||||
|
// the process can continue normally without impact
|
||||||
|
return NoCounters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PerfCounters(std::move(valid_names), std::move(counter_ids),
|
||||||
|
std::move(leader_ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfCounters::CloseCounters() const {
|
||||||
|
if (counter_ids_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int lead : leader_ids_) {
|
||||||
|
ioctl(lead, PERF_EVENT_IOC_DISABLE);
|
||||||
|
}
|
||||||
|
for (int fd : counter_ids_) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else // defined HAVE_LIBPFM
|
||||||
|
size_t PerfCounterValues::Read(const std::vector<int>&) { return 0; }
|
||||||
|
|
||||||
|
const bool PerfCounters::kSupported = false;
|
||||||
|
|
||||||
|
bool PerfCounters::Initialize() { return false; }
|
||||||
|
|
||||||
|
bool PerfCounters::IsCounterSupported(const std::string&) { return false; }
|
||||||
|
|
||||||
|
PerfCounters PerfCounters::Create(
|
||||||
|
const std::vector<std::string>& counter_names) {
|
||||||
|
if (!counter_names.empty()) {
|
||||||
|
GetErrorLogInstance() << "Performance counters not supported.\n";
|
||||||
|
}
|
||||||
|
return NoCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfCounters::CloseCounters() const {}
|
||||||
|
#endif // defined HAVE_LIBPFM
|
||||||
|
|
||||||
|
PerfCountersMeasurement::PerfCountersMeasurement(
|
||||||
|
const std::vector<std::string>& counter_names)
|
||||||
|
: start_values_(counter_names.size()), end_values_(counter_names.size()) {
|
||||||
|
counters_ = PerfCounters::Create(counter_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfCounters& PerfCounters::operator=(PerfCounters&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
CloseCounters();
|
||||||
|
|
||||||
|
counter_ids_ = std::move(other.counter_ids_);
|
||||||
|
leader_ids_ = std::move(other.leader_ids_);
|
||||||
|
counter_names_ = std::move(other.counter_names_);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace benchmark
|
200
third_party/benchmark/src/perf_counters.h
vendored
Normal file
200
third_party/benchmark/src/perf_counters.h
vendored
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// Copyright 2021 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_PERF_COUNTERS_H
|
||||||
|
#define BENCHMARK_PERF_COUNTERS_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_OS_WINDOWS
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
// C4251: <symbol> needs to have dll-interface to be used by clients of class
|
||||||
|
#pragma warning(disable : 4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Typically, we can only read a small number of counters. There is also a
|
||||||
|
// padding preceding counter values, when reading multiple counters with one
|
||||||
|
// syscall (which is desirable). PerfCounterValues abstracts these details.
|
||||||
|
// The implementation ensures the storage is inlined, and allows 0-based
|
||||||
|
// indexing into the counter values.
|
||||||
|
// The object is used in conjunction with a PerfCounters object, by passing it
|
||||||
|
// to Snapshot(). The Read() method relocates individual reads, discarding
|
||||||
|
// the initial padding from each group leader in the values buffer such that
|
||||||
|
// all user accesses through the [] operator are correct.
|
||||||
|
class BENCHMARK_EXPORT PerfCounterValues {
|
||||||
|
public:
|
||||||
|
explicit PerfCounterValues(size_t nr_counters) : nr_counters_(nr_counters) {
|
||||||
|
BM_CHECK_LE(nr_counters_, kMaxCounters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are reading correctly now so the values don't need to skip padding
|
||||||
|
uint64_t operator[](size_t pos) const { return values_[pos]; }
|
||||||
|
|
||||||
|
// Increased the maximum to 32 only since the buffer
|
||||||
|
// is std::array<> backed
|
||||||
|
static constexpr size_t kMaxCounters = 32;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class PerfCounters;
|
||||||
|
// Get the byte buffer in which perf counters can be captured.
|
||||||
|
// This is used by PerfCounters::Read
|
||||||
|
std::pair<char*, size_t> get_data_buffer() {
|
||||||
|
return {reinterpret_cast<char*>(values_.data()),
|
||||||
|
sizeof(uint64_t) * (kPadding + nr_counters_)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// This reading is complex and as the goal of this class is to
|
||||||
|
// abstract away the intrincacies of the reading process, this is
|
||||||
|
// a better place for it
|
||||||
|
size_t Read(const std::vector<int>& leaders);
|
||||||
|
|
||||||
|
// Move the padding to 2 due to the reading algorithm (1st padding plus a
|
||||||
|
// current read padding)
|
||||||
|
static constexpr size_t kPadding = 2;
|
||||||
|
std::array<uint64_t, kPadding + kMaxCounters> values_;
|
||||||
|
const size_t nr_counters_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collect PMU counters. The object, once constructed, is ready to be used by
|
||||||
|
// calling read(). PMU counter collection is enabled from the time create() is
|
||||||
|
// called, to obtain the object, until the object's destructor is called.
|
||||||
|
class BENCHMARK_EXPORT PerfCounters final {
|
||||||
|
public:
|
||||||
|
// True iff this platform supports performance counters.
|
||||||
|
static const bool kSupported;
|
||||||
|
|
||||||
|
// Returns an empty object
|
||||||
|
static PerfCounters NoCounters() { return PerfCounters(); }
|
||||||
|
|
||||||
|
~PerfCounters() { CloseCounters(); }
|
||||||
|
PerfCounters() = default;
|
||||||
|
PerfCounters(PerfCounters&&) = default;
|
||||||
|
PerfCounters(const PerfCounters&) = delete;
|
||||||
|
PerfCounters& operator=(PerfCounters&&) noexcept;
|
||||||
|
PerfCounters& operator=(const PerfCounters&) = delete;
|
||||||
|
|
||||||
|
// Platform-specific implementations may choose to do some library
|
||||||
|
// initialization here.
|
||||||
|
static bool Initialize();
|
||||||
|
|
||||||
|
// Check if the given counter is supported, if the app wants to
|
||||||
|
// check before passing
|
||||||
|
static bool IsCounterSupported(const std::string& name);
|
||||||
|
|
||||||
|
// Return a PerfCounters object ready to read the counters with the names
|
||||||
|
// specified. The values are user-mode only. The counter name format is
|
||||||
|
// implementation and OS specific.
|
||||||
|
// In case of failure, this method will in the worst case return an
|
||||||
|
// empty object whose state will still be valid.
|
||||||
|
static PerfCounters Create(const std::vector<std::string>& counter_names);
|
||||||
|
|
||||||
|
// Take a snapshot of the current value of the counters into the provided
|
||||||
|
// valid PerfCounterValues storage. The values are populated such that:
|
||||||
|
// names()[i]'s value is (*values)[i]
|
||||||
|
BENCHMARK_ALWAYS_INLINE bool Snapshot(PerfCounterValues* values) const {
|
||||||
|
#ifndef BENCHMARK_OS_WINDOWS
|
||||||
|
assert(values != nullptr);
|
||||||
|
return values->Read(leader_ids_) == counter_ids_.size();
|
||||||
|
#else
|
||||||
|
(void)values;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& names() const { return counter_names_; }
|
||||||
|
size_t num_counters() const { return counter_names_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PerfCounters(const std::vector<std::string>& counter_names,
|
||||||
|
std::vector<int>&& counter_ids, std::vector<int>&& leader_ids)
|
||||||
|
: counter_ids_(std::move(counter_ids)),
|
||||||
|
leader_ids_(std::move(leader_ids)),
|
||||||
|
counter_names_(counter_names) {}
|
||||||
|
|
||||||
|
void CloseCounters() const;
|
||||||
|
|
||||||
|
std::vector<int> counter_ids_;
|
||||||
|
std::vector<int> leader_ids_;
|
||||||
|
std::vector<std::string> counter_names_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typical usage of the above primitives.
|
||||||
|
class BENCHMARK_EXPORT PerfCountersMeasurement final {
|
||||||
|
public:
|
||||||
|
PerfCountersMeasurement(const std::vector<std::string>& counter_names);
|
||||||
|
|
||||||
|
size_t num_counters() const { return counters_.num_counters(); }
|
||||||
|
|
||||||
|
std::vector<std::string> names() const { return counters_.names(); }
|
||||||
|
|
||||||
|
BENCHMARK_ALWAYS_INLINE bool Start() {
|
||||||
|
if (num_counters() == 0) return true;
|
||||||
|
// Tell the compiler to not move instructions above/below where we take
|
||||||
|
// the snapshot.
|
||||||
|
ClobberMemory();
|
||||||
|
valid_read_ &= counters_.Snapshot(&start_values_);
|
||||||
|
ClobberMemory();
|
||||||
|
|
||||||
|
return valid_read_;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_ALWAYS_INLINE bool Stop(
|
||||||
|
std::vector<std::pair<std::string, double>>& measurements) {
|
||||||
|
if (num_counters() == 0) return true;
|
||||||
|
// Tell the compiler to not move instructions above/below where we take
|
||||||
|
// the snapshot.
|
||||||
|
ClobberMemory();
|
||||||
|
valid_read_ &= counters_.Snapshot(&end_values_);
|
||||||
|
ClobberMemory();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < counters_.names().size(); ++i) {
|
||||||
|
double measurement = static_cast<double>(end_values_[i]) -
|
||||||
|
static_cast<double>(start_values_[i]);
|
||||||
|
measurements.push_back({counters_.names()[i], measurement});
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid_read_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PerfCounters counters_;
|
||||||
|
bool valid_read_ = true;
|
||||||
|
PerfCounterValues start_values_;
|
||||||
|
PerfCounterValues end_values_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace benchmark
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // BENCHMARK_PERF_COUNTERS_H
|
158
third_party/benchmark/src/re.h
vendored
Normal file
158
third_party/benchmark/src/re.h
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// 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_
|
||||||
|
|
||||||
|
#include "internal_macros.h"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#if !defined(HAVE_STD_REGEX) && \
|
||||||
|
!defined(HAVE_GNU_POSIX_REGEX) && \
|
||||||
|
!defined(HAVE_POSIX_REGEX)
|
||||||
|
// No explicit regex selection; detect based on builtin hints.
|
||||||
|
#if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE)
|
||||||
|
#define HAVE_POSIX_REGEX 1
|
||||||
|
#elif __cplusplus >= 199711L
|
||||||
|
#define HAVE_STD_REGEX 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Prefer C regex libraries when compiling w/o exceptions so that we can
|
||||||
|
// correctly report errors.
|
||||||
|
#if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \
|
||||||
|
defined(HAVE_STD_REGEX) && \
|
||||||
|
(defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX))
|
||||||
|
#undef HAVE_STD_REGEX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#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) {
|
||||||
|
#ifdef BENCHMARK_HAS_NO_EXCEPTIONS
|
||||||
|
((void)error); // suppress unused warning
|
||||||
|
#else
|
||||||
|
try {
|
||||||
|
#endif
|
||||||
|
re_ = std::regex(spec, std::regex_constants::extended);
|
||||||
|
init_ = true;
|
||||||
|
#ifndef BENCHMARK_HAS_NO_EXCEPTIONS
|
||||||
|
}
|
||||||
|
catch (const std::regex_error& e) {
|
||||||
|
if (error) {
|
||||||
|
*error = e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
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.
|
||||||
|
BM_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_
|
118
third_party/benchmark/src/reporter.cc
vendored
Normal file
118
third_party/benchmark/src/reporter.cc
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// 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 <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
BenchmarkReporter::BenchmarkReporter()
|
||||||
|
: output_stream_(&std::cout), error_stream_(&std::cerr) {}
|
||||||
|
|
||||||
|
BenchmarkReporter::~BenchmarkReporter() {}
|
||||||
|
|
||||||
|
void BenchmarkReporter::PrintBasicContext(std::ostream *out,
|
||||||
|
Context const &context) {
|
||||||
|
BM_CHECK(out) << "cannot be null";
|
||||||
|
auto &Out = *out;
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_OS_QURT
|
||||||
|
// Date/time information is not available on QuRT.
|
||||||
|
// Attempting to get it via this call cause the binary to crash.
|
||||||
|
Out << LocalDateTimeString() << "\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (context.executable_name)
|
||||||
|
Out << "Running " << context.executable_name << "\n";
|
||||||
|
|
||||||
|
const CPUInfo &info = context.cpu_info;
|
||||||
|
Out << "Run on (" << info.num_cpus << " X "
|
||||||
|
<< (info.cycles_per_second / 1000000.0) << " MHz CPU "
|
||||||
|
<< ((info.num_cpus > 1) ? "s" : "") << ")\n";
|
||||||
|
if (info.caches.size() != 0) {
|
||||||
|
Out << "CPU Caches:\n";
|
||||||
|
for (auto &CInfo : info.caches) {
|
||||||
|
Out << " L" << CInfo.level << " " << CInfo.type << " "
|
||||||
|
<< (CInfo.size / 1024) << " KiB";
|
||||||
|
if (CInfo.num_sharing != 0)
|
||||||
|
Out << " (x" << (info.num_cpus / CInfo.num_sharing) << ")";
|
||||||
|
Out << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!info.load_avg.empty()) {
|
||||||
|
Out << "Load Average: ";
|
||||||
|
for (auto It = info.load_avg.begin(); It != info.load_avg.end();) {
|
||||||
|
Out << StrFormat("%.2f", *It++);
|
||||||
|
if (It != info.load_avg.end()) Out << ", ";
|
||||||
|
}
|
||||||
|
Out << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, std::string> *global_context =
|
||||||
|
internal::GetGlobalContext();
|
||||||
|
|
||||||
|
if (global_context != nullptr) {
|
||||||
|
for (const auto &kv : *global_context) {
|
||||||
|
Out << kv.first << ": " << kv.second << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CPUInfo::Scaling::ENABLED == info.scaling) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// No initializer because it's already initialized to NULL.
|
||||||
|
const char *BenchmarkReporter::Context::executable_name;
|
||||||
|
|
||||||
|
BenchmarkReporter::Context::Context()
|
||||||
|
: cpu_info(CPUInfo::Get()), sys_info(SystemInfo::Get()) {}
|
||||||
|
|
||||||
|
std::string BenchmarkReporter::Run::benchmark_name() const {
|
||||||
|
std::string name = run_name.str();
|
||||||
|
if (run_type == RT_Aggregate) {
|
||||||
|
name += "_" + aggregate_name;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
214
third_party/benchmark/src/statistics.cc
vendored
Normal file
214
third_party/benchmark/src/statistics.cc
vendored
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
|
||||||
|
// Copyright 2017 Roman Lebedev. 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 "statistics.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <numeric>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
auto StatisticsSum = [](const std::vector<double>& v) {
|
||||||
|
return std::accumulate(v.begin(), v.end(), 0.0);
|
||||||
|
};
|
||||||
|
|
||||||
|
double StatisticsMean(const std::vector<double>& v) {
|
||||||
|
if (v.empty()) return 0.0;
|
||||||
|
return StatisticsSum(v) * (1.0 / static_cast<double>(v.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
double StatisticsMedian(const std::vector<double>& v) {
|
||||||
|
if (v.size() < 3) return StatisticsMean(v);
|
||||||
|
std::vector<double> copy(v);
|
||||||
|
|
||||||
|
auto center = copy.begin() + v.size() / 2;
|
||||||
|
std::nth_element(copy.begin(), center, copy.end());
|
||||||
|
|
||||||
|
// Did we have an odd number of samples? If yes, then center is the median.
|
||||||
|
// If not, then we are looking for the average between center and the value
|
||||||
|
// before. Instead of resorting, we just look for the max value before it,
|
||||||
|
// which is not necessarily the element immediately preceding `center` Since
|
||||||
|
// `copy` is only partially sorted by `nth_element`.
|
||||||
|
if (v.size() % 2 == 1) return *center;
|
||||||
|
auto center2 = std::max_element(copy.begin(), center);
|
||||||
|
return (*center + *center2) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the sum of the squares of this sample set
|
||||||
|
auto SumSquares = [](const std::vector<double>& v) {
|
||||||
|
return std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto Sqr = [](const double dat) { return dat * dat; };
|
||||||
|
auto Sqrt = [](const double dat) {
|
||||||
|
// Avoid NaN due to imprecision in the calculations
|
||||||
|
if (dat < 0.0) return 0.0;
|
||||||
|
return std::sqrt(dat);
|
||||||
|
};
|
||||||
|
|
||||||
|
double StatisticsStdDev(const std::vector<double>& v) {
|
||||||
|
const auto mean = StatisticsMean(v);
|
||||||
|
if (v.empty()) return mean;
|
||||||
|
|
||||||
|
// Sample standard deviation is undefined for n = 1
|
||||||
|
if (v.size() == 1) return 0.0;
|
||||||
|
|
||||||
|
const double avg_squares =
|
||||||
|
SumSquares(v) * (1.0 / static_cast<double>(v.size()));
|
||||||
|
return Sqrt(static_cast<double>(v.size()) /
|
||||||
|
(static_cast<double>(v.size()) - 1.0) *
|
||||||
|
(avg_squares - Sqr(mean)));
|
||||||
|
}
|
||||||
|
|
||||||
|
double StatisticsCV(const std::vector<double>& v) {
|
||||||
|
if (v.size() < 2) return 0.0;
|
||||||
|
|
||||||
|
const auto stddev = StatisticsStdDev(v);
|
||||||
|
const auto mean = StatisticsMean(v);
|
||||||
|
|
||||||
|
if (std::fpclassify(mean) == FP_ZERO) return 0.0;
|
||||||
|
|
||||||
|
return stddev / mean;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.skipped; });
|
||||||
|
|
||||||
|
if (reports.size() - static_cast<size_t>(error_count) < 2) {
|
||||||
|
// We don't report aggregated data if there was a single run.
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulators.
|
||||||
|
std::vector<double> real_accumulated_time_stat;
|
||||||
|
std::vector<double> cpu_accumulated_time_stat;
|
||||||
|
|
||||||
|
real_accumulated_time_stat.reserve(reports.size());
|
||||||
|
cpu_accumulated_time_stat.reserve(reports.size());
|
||||||
|
|
||||||
|
// All repetitions should be run with the same number of iterations so we
|
||||||
|
// can take this information from the first benchmark.
|
||||||
|
const IterationCount run_iterations = reports.front().iterations;
|
||||||
|
// create stats for user counters
|
||||||
|
struct CounterStat {
|
||||||
|
Counter c;
|
||||||
|
std::vector<double> s;
|
||||||
|
};
|
||||||
|
std::map<std::string, CounterStat> counter_stats;
|
||||||
|
for (Run const& r : reports) {
|
||||||
|
for (auto const& cnt : r.counters) {
|
||||||
|
auto it = counter_stats.find(cnt.first);
|
||||||
|
if (it == counter_stats.end()) {
|
||||||
|
it = counter_stats
|
||||||
|
.emplace(cnt.first,
|
||||||
|
CounterStat{cnt.second, std::vector<double>{}})
|
||||||
|
.first;
|
||||||
|
it->second.s.reserve(reports.size());
|
||||||
|
} else {
|
||||||
|
BM_CHECK_EQ(it->second.c.flags, cnt.second.flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the accumulators.
|
||||||
|
for (Run const& run : reports) {
|
||||||
|
BM_CHECK_EQ(reports[0].benchmark_name(), run.benchmark_name());
|
||||||
|
BM_CHECK_EQ(run_iterations, run.iterations);
|
||||||
|
if (run.skipped) continue;
|
||||||
|
real_accumulated_time_stat.emplace_back(run.real_accumulated_time);
|
||||||
|
cpu_accumulated_time_stat.emplace_back(run.cpu_accumulated_time);
|
||||||
|
// user counters
|
||||||
|
for (auto const& cnt : run.counters) {
|
||||||
|
auto it = counter_stats.find(cnt.first);
|
||||||
|
BM_CHECK_NE(it, counter_stats.end());
|
||||||
|
it->second.s.emplace_back(cnt.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add label if it is same for all runs
|
||||||
|
std::string report_label = reports[0].report_label;
|
||||||
|
for (std::size_t i = 1; i < reports.size(); i++) {
|
||||||
|
if (reports[i].report_label != report_label) {
|
||||||
|
report_label = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const double iteration_rescale_factor =
|
||||||
|
double(reports.size()) / double(run_iterations);
|
||||||
|
|
||||||
|
for (const auto& Stat : *reports[0].statistics) {
|
||||||
|
// Get the data from the accumulator to BenchmarkReporter::Run's.
|
||||||
|
Run data;
|
||||||
|
data.run_name = reports[0].run_name;
|
||||||
|
data.family_index = reports[0].family_index;
|
||||||
|
data.per_family_instance_index = reports[0].per_family_instance_index;
|
||||||
|
data.run_type = BenchmarkReporter::Run::RT_Aggregate;
|
||||||
|
data.threads = reports[0].threads;
|
||||||
|
data.repetitions = reports[0].repetitions;
|
||||||
|
data.repetition_index = Run::no_repetition_index;
|
||||||
|
data.aggregate_name = Stat.name_;
|
||||||
|
data.aggregate_unit = Stat.unit_;
|
||||||
|
data.report_label = report_label;
|
||||||
|
|
||||||
|
// It is incorrect to say that an aggregate is computed over
|
||||||
|
// run's iterations, because those iterations already got averaged.
|
||||||
|
// Similarly, if there are N repetitions with 1 iterations each,
|
||||||
|
// an aggregate will be computed over N measurements, not 1.
|
||||||
|
// Thus it is best to simply use the count of separate reports.
|
||||||
|
data.iterations = static_cast<IterationCount>(reports.size());
|
||||||
|
|
||||||
|
data.real_accumulated_time = Stat.compute_(real_accumulated_time_stat);
|
||||||
|
data.cpu_accumulated_time = Stat.compute_(cpu_accumulated_time_stat);
|
||||||
|
|
||||||
|
if (data.aggregate_unit == StatisticUnit::kTime) {
|
||||||
|
// We will divide these times by data.iterations when reporting, but the
|
||||||
|
// data.iterations is not necessarily the scale of these measurements,
|
||||||
|
// because in each repetition, these timers are sum over all the iters.
|
||||||
|
// And if we want to say that the stats are over N repetitions and not
|
||||||
|
// M iterations, we need to multiply these by (N/M).
|
||||||
|
data.real_accumulated_time *= iteration_rescale_factor;
|
||||||
|
data.cpu_accumulated_time *= iteration_rescale_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.time_unit = reports[0].time_unit;
|
||||||
|
|
||||||
|
// user counters
|
||||||
|
for (auto const& kv : counter_stats) {
|
||||||
|
// Do *NOT* rescale the custom counters. They are already properly scaled.
|
||||||
|
const auto uc_stat = Stat.compute_(kv.second.s);
|
||||||
|
auto c = Counter(uc_stat, counter_stats[kv.first].c.flags,
|
||||||
|
counter_stats[kv.first].c.oneK);
|
||||||
|
data.counters[kv.first] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push_back(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
44
third_party/benchmark/src/statistics.h
vendored
Normal file
44
third_party/benchmark/src/statistics.h
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
|
||||||
|
// Copyright 2017 Roman Lebedev. 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 STATISTICS_H_
|
||||||
|
#define STATISTICS_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
// Return a vector containing the mean, median and standard deviation
|
||||||
|
// information (and any user-specified info) for the specified list of reports.
|
||||||
|
// If 'reports' contains less than two non-errored runs an empty vector is
|
||||||
|
// returned
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
std::vector<BenchmarkReporter::Run> ComputeStats(
|
||||||
|
const std::vector<BenchmarkReporter::Run>& reports);
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
double StatisticsMean(const std::vector<double>& v);
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
double StatisticsMedian(const std::vector<double>& v);
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
double StatisticsStdDev(const std::vector<double>& v);
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
double StatisticsCV(const std::vector<double>& v);
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
#endif // STATISTICS_H_
|
254
third_party/benchmark/src/string_util.cc
vendored
Normal file
254
third_party/benchmark/src/string_util.cc
vendored
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
#include "string_util.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#ifdef BENCHMARK_STL_ANDROID_GNUSTL
|
||||||
|
#include <cerrno>
|
||||||
|
#endif
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "arraysize.h"
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace {
|
||||||
|
// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta.
|
||||||
|
const char* const kBigSIUnits[] = {"k", "M", "G", "T", "P", "E", "Z", "Y"};
|
||||||
|
// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi.
|
||||||
|
const char* const kBigIECUnits[] = {"Ki", "Mi", "Gi", "Ti",
|
||||||
|
"Pi", "Ei", "Zi", "Yi"};
|
||||||
|
// milli, micro, nano, pico, femto, atto, zepto, yocto.
|
||||||
|
const char* const kSmallSIUnits[] = {"m", "u", "n", "p", "f", "a", "z", "y"};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
void ToExponentAndMantissa(double val, 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(1.0, 1.0 / std::pow(10.0, precision));
|
||||||
|
const double big_threshold = (adjusted_threshold * one_k) - 1;
|
||||||
|
const double small_threshold = adjusted_threshold;
|
||||||
|
// Values in ]simple_threshold,small_threshold[ will be printed as-is
|
||||||
|
const double simple_threshold = 0.01;
|
||||||
|
|
||||||
|
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 = static_cast<int64_t>(i + 1);
|
||||||
|
*mantissa = mantissa_stream.str();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mantissa_stream << val;
|
||||||
|
*exponent = 0;
|
||||||
|
} else if (val < small_threshold) {
|
||||||
|
// Negative powers
|
||||||
|
if (val < simple_threshold) {
|
||||||
|
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* const* array =
|
||||||
|
(exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits);
|
||||||
|
|
||||||
|
return std::string(array[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToBinaryStringFullySpecified(double value, int precision,
|
||||||
|
Counter::OneK one_k) {
|
||||||
|
std::string mantissa;
|
||||||
|
int64_t exponent;
|
||||||
|
ToExponentAndMantissa(value, precision,
|
||||||
|
one_k == Counter::kIs1024 ? 1024.0 : 1000.0, &mantissa,
|
||||||
|
&exponent);
|
||||||
|
return mantissa + ExponentToPrefix(exponent, one_k == Counter::kIs1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StrFormatImp(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;
|
||||||
|
|
||||||
|
// 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
|
||||||
|
// in the android-ndk
|
||||||
|
auto ret = vsnprintf(local_buff.data(), local_buff.size(), msg, args_cp);
|
||||||
|
|
||||||
|
va_end(args_cp);
|
||||||
|
|
||||||
|
// handle empty expansion
|
||||||
|
if (ret == 0) return std::string{};
|
||||||
|
if (static_cast<std::size_t>(ret) < local_buff.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
|
||||||
|
std::size_t 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
|
||||||
|
vsnprintf(buff_ptr.get(), size, msg, args);
|
||||||
|
return std::string(buff_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
|
std::string HumanReadableNumber(double n, Counter::OneK one_k) {
|
||||||
|
return ToBinaryStringFullySpecified(n, 1, one_k);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StrFormat(const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
std::string tmp = StrFormatImp(format, args);
|
||||||
|
va_end(args);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> StrSplit(const std::string& str, char delim) {
|
||||||
|
if (str.empty()) return {};
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
size_t first = 0;
|
||||||
|
size_t next = str.find(delim);
|
||||||
|
for (; next != std::string::npos;
|
||||||
|
first = next + 1, next = str.find(delim, first)) {
|
||||||
|
ret.push_back(str.substr(first, next - first));
|
||||||
|
}
|
||||||
|
ret.push_back(str.substr(first));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_STL_ANDROID_GNUSTL
|
||||||
|
/*
|
||||||
|
* GNU STL in Android NDK lacks support for some C++11 functions, including
|
||||||
|
* stoul, stoi, stod. We reimplement them here using C functions strtoul,
|
||||||
|
* strtol, strtod. Note that reimplemented functions are in benchmark::
|
||||||
|
* namespace, not std:: namespace.
|
||||||
|
*/
|
||||||
|
unsigned long stoul(const std::string& str, size_t* pos, int base) {
|
||||||
|
/* Record previous errno */
|
||||||
|
const int oldErrno = errno;
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
const char* strStart = str.c_str();
|
||||||
|
char* strEnd = const_cast<char*>(strStart);
|
||||||
|
const unsigned long result = strtoul(strStart, &strEnd, base);
|
||||||
|
|
||||||
|
const int strtoulErrno = errno;
|
||||||
|
/* Restore previous errno */
|
||||||
|
errno = oldErrno;
|
||||||
|
|
||||||
|
/* Check for errors and return */
|
||||||
|
if (strtoulErrno == ERANGE) {
|
||||||
|
throw std::out_of_range("stoul failed: " + str +
|
||||||
|
" is outside of range of unsigned long");
|
||||||
|
} else if (strEnd == strStart || strtoulErrno != 0) {
|
||||||
|
throw std::invalid_argument("stoul failed: " + str + " is not an integer");
|
||||||
|
}
|
||||||
|
if (pos != nullptr) {
|
||||||
|
*pos = static_cast<size_t>(strEnd - strStart);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stoi(const std::string& str, size_t* pos, int base) {
|
||||||
|
/* Record previous errno */
|
||||||
|
const int oldErrno = errno;
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
const char* strStart = str.c_str();
|
||||||
|
char* strEnd = const_cast<char*>(strStart);
|
||||||
|
const long result = strtol(strStart, &strEnd, base);
|
||||||
|
|
||||||
|
const int strtolErrno = errno;
|
||||||
|
/* Restore previous errno */
|
||||||
|
errno = oldErrno;
|
||||||
|
|
||||||
|
/* Check for errors and return */
|
||||||
|
if (strtolErrno == ERANGE || long(int(result)) != result) {
|
||||||
|
throw std::out_of_range("stoul failed: " + str +
|
||||||
|
" is outside of range of int");
|
||||||
|
} else if (strEnd == strStart || strtolErrno != 0) {
|
||||||
|
throw std::invalid_argument("stoul failed: " + str + " is not an integer");
|
||||||
|
}
|
||||||
|
if (pos != nullptr) {
|
||||||
|
*pos = static_cast<size_t>(strEnd - strStart);
|
||||||
|
}
|
||||||
|
return int(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
double stod(const std::string& str, size_t* pos) {
|
||||||
|
/* Record previous errno */
|
||||||
|
const int oldErrno = errno;
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
const char* strStart = str.c_str();
|
||||||
|
char* strEnd = const_cast<char*>(strStart);
|
||||||
|
const double result = strtod(strStart, &strEnd);
|
||||||
|
|
||||||
|
/* Restore previous errno */
|
||||||
|
const int strtodErrno = errno;
|
||||||
|
errno = oldErrno;
|
||||||
|
|
||||||
|
/* Check for errors and return */
|
||||||
|
if (strtodErrno == ERANGE) {
|
||||||
|
throw std::out_of_range("stoul failed: " + str +
|
||||||
|
" is outside of range of int");
|
||||||
|
} else if (strEnd == strStart || strtodErrno != 0) {
|
||||||
|
throw std::invalid_argument("stoul failed: " + str + " is not an integer");
|
||||||
|
}
|
||||||
|
if (pos != nullptr) {
|
||||||
|
*pos = static_cast<size_t>(strEnd - strStart);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
70
third_party/benchmark/src/string_util.h
vendored
Normal file
70
third_party/benchmark/src/string_util.h
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef BENCHMARK_STRING_UTIL_H_
|
||||||
|
#define BENCHMARK_STRING_UTIL_H_
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "benchmark/export.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
std::string HumanReadableNumber(double n, Counter::OneK one_k);
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
#if defined(__MINGW32__)
|
||||||
|
__attribute__((format(__MINGW_PRINTF_FORMAT, 1, 2)))
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
__attribute__((format(printf, 1, 2)))
|
||||||
|
#endif
|
||||||
|
std::string
|
||||||
|
StrFormat(const char* format, ...);
|
||||||
|
|
||||||
|
inline std::ostream& StrCatImp(std::ostream& out) BENCHMARK_NOEXCEPT {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class First, class... Rest>
|
||||||
|
inline std::ostream& StrCatImp(std::ostream& out, First&& f, Rest&&... rest) {
|
||||||
|
out << std::forward<First>(f);
|
||||||
|
return StrCatImp(out, std::forward<Rest>(rest)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
inline std::string StrCat(Args&&... args) {
|
||||||
|
std::ostringstream ss;
|
||||||
|
StrCatImp(ss, std::forward<Args>(args)...);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_EXPORT
|
||||||
|
std::vector<std::string> StrSplit(const std::string& str, char delim);
|
||||||
|
|
||||||
|
// Disable lint checking for this block since it re-implements C functions.
|
||||||
|
// NOLINTBEGIN
|
||||||
|
#ifdef BENCHMARK_STL_ANDROID_GNUSTL
|
||||||
|
/*
|
||||||
|
* GNU STL in Android NDK lacks support for some C++11 functions, including
|
||||||
|
* stoul, stoi, stod. We reimplement them here using C functions strtoul,
|
||||||
|
* strtol, strtod. Note that reimplemented functions are in benchmark::
|
||||||
|
* namespace, not std:: namespace.
|
||||||
|
*/
|
||||||
|
unsigned long stoul(const std::string& str, size_t* pos = nullptr,
|
||||||
|
int base = 10);
|
||||||
|
int stoi(const std::string& str, size_t* pos = nullptr, int base = 10);
|
||||||
|
double stod(const std::string& str, size_t* pos = nullptr);
|
||||||
|
#else
|
||||||
|
using std::stod; // NOLINT(misc-unused-using-decls)
|
||||||
|
using std::stoi; // NOLINT(misc-unused-using-decls)
|
||||||
|
using std::stoul; // NOLINT(misc-unused-using-decls)
|
||||||
|
#endif
|
||||||
|
// NOLINTEND
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_STRING_UTIL_H_
|
871
third_party/benchmark/src/sysinfo.cc
vendored
Normal file
871
third_party/benchmark/src/sysinfo.cc
vendored
Normal file
@ -0,0 +1,871 @@
|
|||||||
|
// 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 "internal_macros.h"
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_OS_WINDOWS
|
||||||
|
#if !defined(WINVER) || WINVER < 0x0600
|
||||||
|
#undef WINVER
|
||||||
|
#define WINVER 0x0600
|
||||||
|
#endif // WINVER handling
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
|
||||||
|
#include <versionhelpers.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <codecvt>
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
#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 || \
|
||||||
|
defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD || \
|
||||||
|
defined BENCHMARK_OS_DRAGONFLY
|
||||||
|
#define BENCHMARK_HAS_SYSCTL
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(BENCHMARK_OS_SOLARIS)
|
||||||
|
#include <kstat.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
|
#if defined(BENCHMARK_OS_QNX)
|
||||||
|
#include <sys/syspage.h>
|
||||||
|
#endif
|
||||||
|
#if defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <qurt.h>
|
||||||
|
#endif
|
||||||
|
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <bitset>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <locale>
|
||||||
|
#include <memory>
|
||||||
|
#include <random>
|
||||||
|
#include <sstream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "check.h"
|
||||||
|
#include "cycleclock.h"
|
||||||
|
#include "internal_macros.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "string_util.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void PrintImp(std::ostream& out) { out << std::endl; }
|
||||||
|
|
||||||
|
template <class First, class... Rest>
|
||||||
|
void PrintImp(std::ostream& out, First&& f, Rest&&... rest) {
|
||||||
|
out << std::forward<First>(f);
|
||||||
|
PrintImp(out, std::forward<Rest>(rest)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) {
|
||||||
|
PrintImp(std::cerr, std::forward<Args>(args)...);
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_HAS_SYSCTL
|
||||||
|
|
||||||
|
/// ValueUnion - A type used to correctly alias the byte-for-byte output of
|
||||||
|
/// `sysctl` with the result type it's to be interpreted as.
|
||||||
|
struct ValueUnion {
|
||||||
|
union DataT {
|
||||||
|
int32_t int32_value;
|
||||||
|
int64_t int64_value;
|
||||||
|
// For correct aliasing of union members from bytes.
|
||||||
|
char bytes[8];
|
||||||
|
};
|
||||||
|
using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>;
|
||||||
|
|
||||||
|
// The size of the data union member + its trailing array size.
|
||||||
|
std::size_t size;
|
||||||
|
DataPtr buff;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ValueUnion() : size(0), buff(nullptr, &std::free) {}
|
||||||
|
|
||||||
|
explicit ValueUnion(std::size_t buff_size)
|
||||||
|
: size(sizeof(DataT) + buff_size),
|
||||||
|
buff(::new (std::malloc(size)) DataT(), &std::free) {}
|
||||||
|
|
||||||
|
ValueUnion(ValueUnion&& other) = default;
|
||||||
|
|
||||||
|
explicit operator bool() const { return bool(buff); }
|
||||||
|
|
||||||
|
char* data() const { return buff->bytes; }
|
||||||
|
|
||||||
|
std::string GetAsString() const { return std::string(data()); }
|
||||||
|
|
||||||
|
int64_t GetAsInteger() const {
|
||||||
|
if (size == sizeof(buff->int32_value))
|
||||||
|
return buff->int32_value;
|
||||||
|
else if (size == sizeof(buff->int64_value))
|
||||||
|
return buff->int64_value;
|
||||||
|
BENCHMARK_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, int N>
|
||||||
|
std::array<T, N> GetAsArray() {
|
||||||
|
const int arr_size = sizeof(T) * N;
|
||||||
|
BM_CHECK_LE(arr_size, size);
|
||||||
|
std::array<T, N> arr;
|
||||||
|
std::memcpy(arr.data(), data(), arr_size);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ValueUnion GetSysctlImp(std::string const& name) {
|
||||||
|
#if defined BENCHMARK_OS_OPENBSD
|
||||||
|
int mib[2];
|
||||||
|
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
if ((name == "hw.ncpu") || (name == "hw.cpuspeed")) {
|
||||||
|
ValueUnion buff(sizeof(int));
|
||||||
|
|
||||||
|
if (name == "hw.ncpu") {
|
||||||
|
mib[1] = HW_NCPU;
|
||||||
|
} else {
|
||||||
|
mib[1] = HW_CPUSPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sysctl(mib, 2, buff.data(), &buff.size, nullptr, 0) == -1) {
|
||||||
|
return ValueUnion();
|
||||||
|
}
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
return ValueUnion();
|
||||||
|
#else
|
||||||
|
std::size_t cur_buff_size = 0;
|
||||||
|
if (sysctlbyname(name.c_str(), nullptr, &cur_buff_size, nullptr, 0) == -1)
|
||||||
|
return ValueUnion();
|
||||||
|
|
||||||
|
ValueUnion buff(cur_buff_size);
|
||||||
|
if (sysctlbyname(name.c_str(), buff.data(), &buff.size, nullptr, 0) == 0)
|
||||||
|
return buff;
|
||||||
|
return ValueUnion();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_MAYBE_UNUSED
|
||||||
|
bool GetSysctl(std::string const& name, std::string* out) {
|
||||||
|
out->clear();
|
||||||
|
auto buff = GetSysctlImp(name);
|
||||||
|
if (!buff) return false;
|
||||||
|
out->assign(buff.data());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Tp,
|
||||||
|
class = typename std::enable_if<std::is_integral<Tp>::value>::type>
|
||||||
|
bool GetSysctl(std::string const& name, Tp* out) {
|
||||||
|
*out = 0;
|
||||||
|
auto buff = GetSysctlImp(name);
|
||||||
|
if (!buff) return false;
|
||||||
|
*out = static_cast<Tp>(buff.GetAsInteger());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Tp, size_t N>
|
||||||
|
bool GetSysctl(std::string const& name, std::array<Tp, N>* out) {
|
||||||
|
auto buff = GetSysctlImp(name);
|
||||||
|
if (!buff) return false;
|
||||||
|
*out = buff.GetAsArray<Tp, N>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class ArgT>
|
||||||
|
bool ReadFromFile(std::string const& fname, ArgT* arg) {
|
||||||
|
*arg = ArgT();
|
||||||
|
std::ifstream f(fname.c_str());
|
||||||
|
if (!f.is_open()) return false;
|
||||||
|
f >> *arg;
|
||||||
|
return f.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
CPUInfo::Scaling CpuScaling(int num_cpus) {
|
||||||
|
// We don't have a valid CPU count, so don't even bother.
|
||||||
|
if (num_cpus <= 0) return CPUInfo::Scaling::UNKNOWN;
|
||||||
|
#if defined(BENCHMARK_OS_QNX)
|
||||||
|
return CPUInfo::Scaling::UNKNOWN;
|
||||||
|
#elif !defined(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.
|
||||||
|
std::string res;
|
||||||
|
for (int cpu = 0; cpu < num_cpus; ++cpu) {
|
||||||
|
std::string governor_file =
|
||||||
|
StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor");
|
||||||
|
if (ReadFromFile(governor_file, &res) && res != "performance")
|
||||||
|
return CPUInfo::Scaling::ENABLED;
|
||||||
|
}
|
||||||
|
return CPUInfo::Scaling::DISABLED;
|
||||||
|
#else
|
||||||
|
return CPUInfo::Scaling::UNKNOWN;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int CountSetBitsInCPUMap(std::string val) {
|
||||||
|
auto CountBits = [](std::string part) {
|
||||||
|
using CPUMask = std::bitset<sizeof(std::uintptr_t) * CHAR_BIT>;
|
||||||
|
part = "0x" + part;
|
||||||
|
CPUMask mask(benchmark::stoul(part, nullptr, 16));
|
||||||
|
return static_cast<int>(mask.count());
|
||||||
|
};
|
||||||
|
std::size_t pos;
|
||||||
|
int total = 0;
|
||||||
|
while ((pos = val.find(',')) != std::string::npos) {
|
||||||
|
total += CountBits(val.substr(0, pos));
|
||||||
|
val = val.substr(pos + 1);
|
||||||
|
}
|
||||||
|
if (!val.empty()) {
|
||||||
|
total += CountBits(val);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_MAYBE_UNUSED
|
||||||
|
std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() {
|
||||||
|
std::vector<CPUInfo::CacheInfo> res;
|
||||||
|
std::string dir = "/sys/devices/system/cpu/cpu0/cache/";
|
||||||
|
int idx = 0;
|
||||||
|
while (true) {
|
||||||
|
CPUInfo::CacheInfo info;
|
||||||
|
std::string fpath = StrCat(dir, "index", idx++, "/");
|
||||||
|
std::ifstream f(StrCat(fpath, "size").c_str());
|
||||||
|
if (!f.is_open()) break;
|
||||||
|
std::string suffix;
|
||||||
|
f >> info.size;
|
||||||
|
if (f.fail())
|
||||||
|
PrintErrorAndDie("Failed while reading file '", fpath, "size'");
|
||||||
|
if (f.good()) {
|
||||||
|
f >> suffix;
|
||||||
|
if (f.bad())
|
||||||
|
PrintErrorAndDie(
|
||||||
|
"Invalid cache size format: failed to read size suffix");
|
||||||
|
else if (f && suffix != "K")
|
||||||
|
PrintErrorAndDie("Invalid cache size format: Expected bytes ", suffix);
|
||||||
|
else if (suffix == "K")
|
||||||
|
info.size *= 1024;
|
||||||
|
}
|
||||||
|
if (!ReadFromFile(StrCat(fpath, "type"), &info.type))
|
||||||
|
PrintErrorAndDie("Failed to read from file ", fpath, "type");
|
||||||
|
if (!ReadFromFile(StrCat(fpath, "level"), &info.level))
|
||||||
|
PrintErrorAndDie("Failed to read from file ", fpath, "level");
|
||||||
|
std::string map_str;
|
||||||
|
if (!ReadFromFile(StrCat(fpath, "shared_cpu_map"), &map_str))
|
||||||
|
PrintErrorAndDie("Failed to read from file ", fpath, "shared_cpu_map");
|
||||||
|
info.num_sharing = CountSetBitsInCPUMap(map_str);
|
||||||
|
res.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_OS_MACOSX
|
||||||
|
std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() {
|
||||||
|
std::vector<CPUInfo::CacheInfo> res;
|
||||||
|
std::array<int, 4> cache_counts{{0, 0, 0, 0}};
|
||||||
|
GetSysctl("hw.cacheconfig", &cache_counts);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::string name;
|
||||||
|
std::string type;
|
||||||
|
int level;
|
||||||
|
int num_sharing;
|
||||||
|
} cases[] = {{"hw.l1dcachesize", "Data", 1, cache_counts[1]},
|
||||||
|
{"hw.l1icachesize", "Instruction", 1, cache_counts[1]},
|
||||||
|
{"hw.l2cachesize", "Unified", 2, cache_counts[2]},
|
||||||
|
{"hw.l3cachesize", "Unified", 3, cache_counts[3]}};
|
||||||
|
for (auto& c : cases) {
|
||||||
|
int val;
|
||||||
|
if (!GetSysctl(c.name, &val)) continue;
|
||||||
|
CPUInfo::CacheInfo info;
|
||||||
|
info.type = c.type;
|
||||||
|
info.level = c.level;
|
||||||
|
info.size = val;
|
||||||
|
info.num_sharing = c.num_sharing;
|
||||||
|
res.push_back(std::move(info));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#elif defined(BENCHMARK_OS_WINDOWS)
|
||||||
|
std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() {
|
||||||
|
std::vector<CPUInfo::CacheInfo> res;
|
||||||
|
DWORD buffer_size = 0;
|
||||||
|
using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
|
||||||
|
using CInfo = CACHE_DESCRIPTOR;
|
||||||
|
|
||||||
|
using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>;
|
||||||
|
GetLogicalProcessorInformation(nullptr, &buffer_size);
|
||||||
|
UPtr buff(static_cast<PInfo*>(std::malloc(buffer_size)), &std::free);
|
||||||
|
if (!GetLogicalProcessorInformation(buff.get(), &buffer_size))
|
||||||
|
PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ",
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
|
PInfo* it = buff.get();
|
||||||
|
PInfo* end = buff.get() + (buffer_size / sizeof(PInfo));
|
||||||
|
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
if (it->Relationship != RelationCache) continue;
|
||||||
|
using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>;
|
||||||
|
BitSet b(it->ProcessorMask);
|
||||||
|
// To prevent duplicates, only consider caches where CPU 0 is specified
|
||||||
|
if (!b.test(0)) continue;
|
||||||
|
const CInfo& cache = it->Cache;
|
||||||
|
CPUInfo::CacheInfo C;
|
||||||
|
C.num_sharing = static_cast<int>(b.count());
|
||||||
|
C.level = cache.Level;
|
||||||
|
C.size = static_cast<int>(cache.Size);
|
||||||
|
C.type = "Unknown";
|
||||||
|
switch (cache.Type) {
|
||||||
|
case CacheUnified:
|
||||||
|
C.type = "Unified";
|
||||||
|
break;
|
||||||
|
case CacheInstruction:
|
||||||
|
C.type = "Instruction";
|
||||||
|
break;
|
||||||
|
case CacheData:
|
||||||
|
C.type = "Data";
|
||||||
|
break;
|
||||||
|
case CacheTrace:
|
||||||
|
C.type = "Trace";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res.push_back(C);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#elif BENCHMARK_OS_QNX
|
||||||
|
std::vector<CPUInfo::CacheInfo> GetCacheSizesQNX() {
|
||||||
|
std::vector<CPUInfo::CacheInfo> res;
|
||||||
|
struct cacheattr_entry* cache = SYSPAGE_ENTRY(cacheattr);
|
||||||
|
uint32_t const elsize = SYSPAGE_ELEMENT_SIZE(cacheattr);
|
||||||
|
int num = SYSPAGE_ENTRY_SIZE(cacheattr) / elsize;
|
||||||
|
for (int i = 0; i < num; ++i) {
|
||||||
|
CPUInfo::CacheInfo info;
|
||||||
|
switch (cache->flags) {
|
||||||
|
case CACHE_FLAG_INSTR:
|
||||||
|
info.type = "Instruction";
|
||||||
|
info.level = 1;
|
||||||
|
break;
|
||||||
|
case CACHE_FLAG_DATA:
|
||||||
|
info.type = "Data";
|
||||||
|
info.level = 1;
|
||||||
|
break;
|
||||||
|
case CACHE_FLAG_UNIFIED:
|
||||||
|
info.type = "Unified";
|
||||||
|
info.level = 2;
|
||||||
|
break;
|
||||||
|
case CACHE_FLAG_SHARED:
|
||||||
|
info.type = "Shared";
|
||||||
|
info.level = 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info.size = cache->line_size * cache->num_lines;
|
||||||
|
info.num_sharing = 0;
|
||||||
|
res.push_back(std::move(info));
|
||||||
|
cache = SYSPAGE_ARRAY_ADJ_OFFSET(cacheattr, cache, elsize);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<CPUInfo::CacheInfo> GetCacheSizes() {
|
||||||
|
#ifdef BENCHMARK_OS_MACOSX
|
||||||
|
return GetCacheSizesMacOSX();
|
||||||
|
#elif defined(BENCHMARK_OS_WINDOWS)
|
||||||
|
return GetCacheSizesWindows();
|
||||||
|
#elif defined(BENCHMARK_OS_QNX)
|
||||||
|
return GetCacheSizesQNX();
|
||||||
|
#elif defined(BENCHMARK_OS_QURT)
|
||||||
|
return std::vector<CPUInfo::CacheInfo>();
|
||||||
|
#else
|
||||||
|
return GetCacheSizesFromKVFS();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetSystemName() {
|
||||||
|
#if defined(BENCHMARK_OS_WINDOWS)
|
||||||
|
std::string str;
|
||||||
|
static constexpr int COUNT = MAX_COMPUTERNAME_LENGTH + 1;
|
||||||
|
TCHAR hostname[COUNT] = {'\0'};
|
||||||
|
DWORD DWCOUNT = COUNT;
|
||||||
|
if (!GetComputerName(hostname, &DWCOUNT)) return std::string("");
|
||||||
|
#ifndef UNICODE
|
||||||
|
str = std::string(hostname, DWCOUNT);
|
||||||
|
#else
|
||||||
|
// `WideCharToMultiByte` returns `0` when conversion fails.
|
||||||
|
int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, hostname,
|
||||||
|
DWCOUNT, NULL, 0, NULL, NULL);
|
||||||
|
str.resize(len);
|
||||||
|
WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, hostname, DWCOUNT, &str[0],
|
||||||
|
str.size(), NULL, NULL);
|
||||||
|
#endif
|
||||||
|
return str;
|
||||||
|
#elif defined(BENCHMARK_OS_QURT)
|
||||||
|
std::string str = "Hexagon DSP";
|
||||||
|
qurt_arch_version_t arch_version_struct;
|
||||||
|
if (qurt_sysenv_get_arch_version(&arch_version_struct) == QURT_EOK) {
|
||||||
|
str += " v";
|
||||||
|
str += std::to_string(arch_version_struct.arch_version);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
#else
|
||||||
|
#ifndef HOST_NAME_MAX
|
||||||
|
#ifdef BENCHMARK_HAS_SYSCTL // BSD/Mac doesn't have HOST_NAME_MAX defined
|
||||||
|
#define HOST_NAME_MAX 64
|
||||||
|
#elif defined(BENCHMARK_OS_NACL)
|
||||||
|
#define HOST_NAME_MAX 64
|
||||||
|
#elif defined(BENCHMARK_OS_QNX)
|
||||||
|
#define HOST_NAME_MAX 154
|
||||||
|
#elif defined(BENCHMARK_OS_RTEMS)
|
||||||
|
#define HOST_NAME_MAX 256
|
||||||
|
#elif defined(BENCHMARK_OS_SOLARIS)
|
||||||
|
#define HOST_NAME_MAX MAXHOSTNAMELEN
|
||||||
|
#elif defined(BENCHMARK_OS_ZOS)
|
||||||
|
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
||||||
|
#else
|
||||||
|
#pragma message("HOST_NAME_MAX not defined. using 64")
|
||||||
|
#define HOST_NAME_MAX 64
|
||||||
|
#endif
|
||||||
|
#endif // def HOST_NAME_MAX
|
||||||
|
char hostname[HOST_NAME_MAX];
|
||||||
|
int retVal = gethostname(hostname, HOST_NAME_MAX);
|
||||||
|
if (retVal != 0) return std::string("");
|
||||||
|
return std::string(hostname);
|
||||||
|
#endif // Catch-all POSIX block.
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetNumCPUsImpl() {
|
||||||
|
#ifdef BENCHMARK_HAS_SYSCTL
|
||||||
|
int num_cpu = -1;
|
||||||
|
if (GetSysctl("hw.ncpu", &num_cpu)) return num_cpu;
|
||||||
|
PrintErrorAndDie("Err: ", strerror(errno));
|
||||||
|
#elif defined(BENCHMARK_OS_WINDOWS)
|
||||||
|
SYSTEM_INFO sysinfo;
|
||||||
|
// Use memset as opposed to = {} to avoid GCC missing initializer false
|
||||||
|
// positives.
|
||||||
|
std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO));
|
||||||
|
GetSystemInfo(&sysinfo);
|
||||||
|
// number of logical processors in the current group
|
||||||
|
return static_cast<int>(sysinfo.dwNumberOfProcessors);
|
||||||
|
#elif defined(BENCHMARK_OS_SOLARIS)
|
||||||
|
// Returns -1 in case of a failure.
|
||||||
|
long num_cpu = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
if (num_cpu < 0) {
|
||||||
|
PrintErrorAndDie("sysconf(_SC_NPROCESSORS_ONLN) failed with error: ",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
return (int)num_cpu;
|
||||||
|
#elif defined(BENCHMARK_OS_QNX)
|
||||||
|
return static_cast<int>(_syspage_ptr->num_cpu);
|
||||||
|
#elif defined(BENCHMARK_OS_QURT)
|
||||||
|
qurt_sysenv_max_hthreads_t hardware_threads;
|
||||||
|
if (qurt_sysenv_get_max_hw_threads(&hardware_threads) != QURT_EOK) {
|
||||||
|
hardware_threads.max_hthreads = 1;
|
||||||
|
}
|
||||||
|
return hardware_threads.max_hthreads;
|
||||||
|
#else
|
||||||
|
int num_cpus = 0;
|
||||||
|
int max_id = -1;
|
||||||
|
std::ifstream f("/proc/cpuinfo");
|
||||||
|
if (!f.is_open()) {
|
||||||
|
PrintErrorAndDie("Failed to open /proc/cpuinfo");
|
||||||
|
}
|
||||||
|
#if defined(__alpha__)
|
||||||
|
const std::string Key = "cpus detected";
|
||||||
|
#else
|
||||||
|
const std::string Key = "processor";
|
||||||
|
#endif
|
||||||
|
std::string ln;
|
||||||
|
while (std::getline(f, ln)) {
|
||||||
|
if (ln.empty()) continue;
|
||||||
|
std::size_t split_idx = ln.find(':');
|
||||||
|
std::string value;
|
||||||
|
#if defined(__s390__)
|
||||||
|
// s390 has another format in /proc/cpuinfo
|
||||||
|
// it needs to be parsed differently
|
||||||
|
if (split_idx != std::string::npos)
|
||||||
|
value = ln.substr(Key.size() + 1, split_idx - Key.size() - 1);
|
||||||
|
#else
|
||||||
|
if (split_idx != std::string::npos) value = ln.substr(split_idx + 1);
|
||||||
|
#endif
|
||||||
|
if (ln.size() >= Key.size() && ln.compare(0, Key.size(), Key) == 0) {
|
||||||
|
num_cpus++;
|
||||||
|
if (!value.empty()) {
|
||||||
|
const int cur_id = benchmark::stoi(value);
|
||||||
|
max_id = std::max(cur_id, max_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f.bad()) {
|
||||||
|
PrintErrorAndDie("Failure reading /proc/cpuinfo");
|
||||||
|
}
|
||||||
|
if (!f.eof()) {
|
||||||
|
PrintErrorAndDie("Failed to read to end of /proc/cpuinfo");
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
if ((max_id + 1) != num_cpus) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"CPU ID assignments in /proc/cpuinfo seem messed up."
|
||||||
|
" This is usually caused by a bad BIOS.\n");
|
||||||
|
}
|
||||||
|
return num_cpus;
|
||||||
|
#endif
|
||||||
|
BENCHMARK_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetNumCPUs() {
|
||||||
|
const int num_cpus = GetNumCPUsImpl();
|
||||||
|
if (num_cpus < 1) {
|
||||||
|
PrintErrorAndDie(
|
||||||
|
"Unable to extract number of CPUs. If your platform uses "
|
||||||
|
"/proc/cpuinfo, custom support may need to be added.");
|
||||||
|
}
|
||||||
|
return num_cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThreadAffinityGuard final {
|
||||||
|
public:
|
||||||
|
ThreadAffinityGuard() : reset_affinity(SetAffinity()) {
|
||||||
|
if (!reset_affinity)
|
||||||
|
std::cerr << "***WARNING*** Failed to set thread affinity. Estimated CPU "
|
||||||
|
"frequency may be incorrect."
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ThreadAffinityGuard() {
|
||||||
|
if (!reset_affinity) return;
|
||||||
|
|
||||||
|
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
|
||||||
|
int ret = pthread_setaffinity_np(self, sizeof(previous_affinity),
|
||||||
|
&previous_affinity);
|
||||||
|
if (ret == 0) return;
|
||||||
|
#elif defined(BENCHMARK_OS_WINDOWS_WIN32)
|
||||||
|
DWORD_PTR ret = SetThreadAffinityMask(self, previous_affinity);
|
||||||
|
if (ret != 0) return;
|
||||||
|
#endif // def BENCHMARK_HAS_PTHREAD_AFFINITY
|
||||||
|
PrintErrorAndDie("Failed to reset thread affinity");
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadAffinityGuard(ThreadAffinityGuard&&) = delete;
|
||||||
|
ThreadAffinityGuard(const ThreadAffinityGuard&) = delete;
|
||||||
|
ThreadAffinityGuard& operator=(ThreadAffinityGuard&&) = delete;
|
||||||
|
ThreadAffinityGuard& operator=(const ThreadAffinityGuard&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool SetAffinity() {
|
||||||
|
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
|
||||||
|
int ret;
|
||||||
|
self = pthread_self();
|
||||||
|
ret = pthread_getaffinity_np(self, sizeof(previous_affinity),
|
||||||
|
&previous_affinity);
|
||||||
|
if (ret != 0) return false;
|
||||||
|
|
||||||
|
cpu_set_t affinity;
|
||||||
|
memcpy(&affinity, &previous_affinity, sizeof(affinity));
|
||||||
|
|
||||||
|
bool is_first_cpu = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < CPU_SETSIZE; ++i)
|
||||||
|
if (CPU_ISSET(i, &affinity)) {
|
||||||
|
if (is_first_cpu)
|
||||||
|
is_first_cpu = false;
|
||||||
|
else
|
||||||
|
CPU_CLR(i, &affinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_first_cpu) return false;
|
||||||
|
|
||||||
|
ret = pthread_setaffinity_np(self, sizeof(affinity), &affinity);
|
||||||
|
return ret == 0;
|
||||||
|
#elif defined(BENCHMARK_OS_WINDOWS_WIN32)
|
||||||
|
self = GetCurrentThread();
|
||||||
|
DWORD_PTR mask = static_cast<DWORD_PTR>(1) << GetCurrentProcessorNumber();
|
||||||
|
previous_affinity = SetThreadAffinityMask(self, mask);
|
||||||
|
return previous_affinity != 0;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif // def BENCHMARK_HAS_PTHREAD_AFFINITY
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(BENCHMARK_HAS_PTHREAD_AFFINITY)
|
||||||
|
pthread_t self;
|
||||||
|
cpu_set_t previous_affinity;
|
||||||
|
#elif defined(BENCHMARK_OS_WINDOWS_WIN32)
|
||||||
|
HANDLE self;
|
||||||
|
DWORD_PTR previous_affinity;
|
||||||
|
#endif // def BENCHMARK_HAS_PTHREAD_AFFINITY
|
||||||
|
bool reset_affinity;
|
||||||
|
};
|
||||||
|
|
||||||
|
double GetCPUCyclesPerSecond(CPUInfo::Scaling scaling) {
|
||||||
|
// Currently, scaling is only used on linux path here,
|
||||||
|
// suppress diagnostics about it being unused on other paths.
|
||||||
|
(void)scaling;
|
||||||
|
|
||||||
|
#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN
|
||||||
|
long freq;
|
||||||
|
|
||||||
|
// 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 (ReadFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)
|
||||||
|
// If CPU scaling is disabled, use the *current* frequency.
|
||||||
|
// Note that we specifically don't want to read cpuinfo_cur_freq,
|
||||||
|
// because it is only readable by root.
|
||||||
|
|| (scaling == CPUInfo::Scaling::DISABLED &&
|
||||||
|
ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
|
||||||
|
&freq))
|
||||||
|
// Otherwise, if CPU scaling may be in effect, we want to use
|
||||||
|
// the *maximum* frequency, not whatever CPU speed some random processor
|
||||||
|
// happens to be using now.
|
||||||
|
|| ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
|
||||||
|
&freq)) {
|
||||||
|
// The value is in kHz (as the file name suggests). For example, on a
|
||||||
|
// 2GHz warpstation, the file contains the value "2000000".
|
||||||
|
return static_cast<double>(freq) * 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double error_value = -1;
|
||||||
|
double bogo_clock = error_value;
|
||||||
|
|
||||||
|
std::ifstream f("/proc/cpuinfo");
|
||||||
|
if (!f.is_open()) {
|
||||||
|
std::cerr << "failed to open /proc/cpuinfo\n";
|
||||||
|
return error_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StartsWithKey = [](std::string const& Value, std::string const& Key) {
|
||||||
|
if (Key.size() > Value.size()) return false;
|
||||||
|
auto Cmp = [&](char X, char Y) {
|
||||||
|
return std::tolower(X) == std::tolower(Y);
|
||||||
|
};
|
||||||
|
return std::equal(Key.begin(), Key.end(), Value.begin(), Cmp);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string ln;
|
||||||
|
while (std::getline(f, ln)) {
|
||||||
|
if (ln.empty()) continue;
|
||||||
|
std::size_t split_idx = ln.find(':');
|
||||||
|
std::string value;
|
||||||
|
if (split_idx != std::string::npos) value = ln.substr(split_idx + 1);
|
||||||
|
// When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
|
||||||
|
// accept positive values. Some environments (virtual machines) report zero,
|
||||||
|
// which would cause infinite looping in WallTime_Init.
|
||||||
|
if (StartsWithKey(ln, "cpu MHz")) {
|
||||||
|
if (!value.empty()) {
|
||||||
|
double cycles_per_second = benchmark::stod(value) * 1000000.0;
|
||||||
|
if (cycles_per_second > 0) return cycles_per_second;
|
||||||
|
}
|
||||||
|
} else if (StartsWithKey(ln, "bogomips")) {
|
||||||
|
if (!value.empty()) {
|
||||||
|
bogo_clock = benchmark::stod(value) * 1000000.0;
|
||||||
|
if (bogo_clock < 0.0) bogo_clock = error_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f.bad()) {
|
||||||
|
std::cerr << "Failure reading /proc/cpuinfo\n";
|
||||||
|
return error_value;
|
||||||
|
}
|
||||||
|
if (!f.eof()) {
|
||||||
|
std::cerr << "Failed to read to end of /proc/cpuinfo\n";
|
||||||
|
return error_value;
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
// If we found the bogomips clock, but nothing better, we'll use it (but
|
||||||
|
// we're not happy about it); otherwise, fallback to the rough estimation
|
||||||
|
// below.
|
||||||
|
if (bogo_clock >= 0.0) return bogo_clock;
|
||||||
|
|
||||||
|
#elif defined BENCHMARK_HAS_SYSCTL
|
||||||
|
constexpr auto* freqStr =
|
||||||
|
#if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD)
|
||||||
|
"machdep.tsc_freq";
|
||||||
|
#elif defined BENCHMARK_OS_OPENBSD
|
||||||
|
"hw.cpuspeed";
|
||||||
|
#elif defined BENCHMARK_OS_DRAGONFLY
|
||||||
|
"hw.tsc_frequency";
|
||||||
|
#else
|
||||||
|
"hw.cpufrequency";
|
||||||
|
#endif
|
||||||
|
unsigned long long hz = 0;
|
||||||
|
#if defined BENCHMARK_OS_OPENBSD
|
||||||
|
if (GetSysctl(freqStr, &hz)) return static_cast<double>(hz * 1000000);
|
||||||
|
#else
|
||||||
|
if (GetSysctl(freqStr, &hz)) return static_cast<double>(hz);
|
||||||
|
#endif
|
||||||
|
fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
|
||||||
|
freqStr, strerror(errno));
|
||||||
|
fprintf(stderr,
|
||||||
|
"This does not affect benchmark measurements, only the "
|
||||||
|
"metadata output.\n");
|
||||||
|
|
||||||
|
#elif defined BENCHMARK_OS_WINDOWS_WIN32
|
||||||
|
// 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)))
|
||||||
|
return static_cast<double>(static_cast<int64_t>(data) *
|
||||||
|
static_cast<int64_t>(1000 * 1000)); // was mhz
|
||||||
|
#elif defined(BENCHMARK_OS_SOLARIS)
|
||||||
|
kstat_ctl_t* kc = kstat_open();
|
||||||
|
if (!kc) {
|
||||||
|
std::cerr << "failed to open /dev/kstat\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
kstat_t* ksp = kstat_lookup(kc, const_cast<char*>("cpu_info"), -1,
|
||||||
|
const_cast<char*>("cpu_info0"));
|
||||||
|
if (!ksp) {
|
||||||
|
std::cerr << "failed to lookup in /dev/kstat\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (kstat_read(kc, ksp, NULL) < 0) {
|
||||||
|
std::cerr << "failed to read from /dev/kstat\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
kstat_named_t* knp = (kstat_named_t*)kstat_data_lookup(
|
||||||
|
ksp, const_cast<char*>("current_clock_Hz"));
|
||||||
|
if (!knp) {
|
||||||
|
std::cerr << "failed to lookup data in /dev/kstat\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (knp->data_type != KSTAT_DATA_UINT64) {
|
||||||
|
std::cerr << "current_clock_Hz is of unexpected data type: "
|
||||||
|
<< knp->data_type << "\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
double clock_hz = knp->value.ui64;
|
||||||
|
kstat_close(kc);
|
||||||
|
return clock_hz;
|
||||||
|
#elif defined(BENCHMARK_OS_QNX)
|
||||||
|
return static_cast<double>(
|
||||||
|
static_cast<int64_t>(SYSPAGE_ENTRY(cpuinfo)->speed) *
|
||||||
|
static_cast<int64_t>(1000 * 1000));
|
||||||
|
#elif defined(BENCHMARK_OS_QURT)
|
||||||
|
// QuRT doesn't provide any API to query Hexagon frequency.
|
||||||
|
return 1000000000;
|
||||||
|
#endif
|
||||||
|
// If we've fallen through, attempt to roughly estimate the CPU clock rate.
|
||||||
|
|
||||||
|
// Make sure to use the same cycle counter when starting and stopping the
|
||||||
|
// cycle timer. We just pin the current thread to a cpu in the previous
|
||||||
|
// affinity set.
|
||||||
|
ThreadAffinityGuard affinity_guard;
|
||||||
|
|
||||||
|
static constexpr double estimate_time_s = 1.0;
|
||||||
|
const double start_time = ChronoClockNow();
|
||||||
|
const auto start_ticks = cycleclock::Now();
|
||||||
|
|
||||||
|
// Impose load instead of calling sleep() to make sure the cycle counter
|
||||||
|
// works.
|
||||||
|
using PRNG = std::minstd_rand;
|
||||||
|
using Result = PRNG::result_type;
|
||||||
|
PRNG rng(static_cast<Result>(start_ticks));
|
||||||
|
|
||||||
|
Result state = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
static constexpr size_t batch_size = 10000;
|
||||||
|
rng.discard(batch_size);
|
||||||
|
state += rng();
|
||||||
|
|
||||||
|
} while (ChronoClockNow() - start_time < estimate_time_s);
|
||||||
|
|
||||||
|
DoNotOptimize(state);
|
||||||
|
|
||||||
|
const auto end_ticks = cycleclock::Now();
|
||||||
|
const double end_time = ChronoClockNow();
|
||||||
|
|
||||||
|
return static_cast<double>(end_ticks - start_ticks) / (end_time - start_time);
|
||||||
|
// Reset the affinity of current thread when the lifetime of affinity_guard
|
||||||
|
// ends.
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> GetLoadAvg() {
|
||||||
|
#if (defined BENCHMARK_OS_FREEBSD || defined(BENCHMARK_OS_LINUX) || \
|
||||||
|
defined BENCHMARK_OS_MACOSX || defined BENCHMARK_OS_NETBSD || \
|
||||||
|
defined BENCHMARK_OS_OPENBSD || defined BENCHMARK_OS_DRAGONFLY) && \
|
||||||
|
!(defined(__ANDROID__) && __ANDROID_API__ < 29)
|
||||||
|
static constexpr int kMaxSamples = 3;
|
||||||
|
std::vector<double> res(kMaxSamples, 0.0);
|
||||||
|
const size_t nelem = static_cast<size_t>(getloadavg(res.data(), kMaxSamples));
|
||||||
|
if (nelem < 1) {
|
||||||
|
res.clear();
|
||||||
|
} else {
|
||||||
|
res.resize(nelem);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
#else
|
||||||
|
return {};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
|
const CPUInfo& CPUInfo::Get() {
|
||||||
|
static const CPUInfo* info = new CPUInfo();
|
||||||
|
return *info;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPUInfo::CPUInfo()
|
||||||
|
: num_cpus(GetNumCPUs()),
|
||||||
|
scaling(CpuScaling(num_cpus)),
|
||||||
|
cycles_per_second(GetCPUCyclesPerSecond(scaling)),
|
||||||
|
caches(GetCacheSizes()),
|
||||||
|
load_avg(GetLoadAvg()) {}
|
||||||
|
|
||||||
|
const SystemInfo& SystemInfo::Get() {
|
||||||
|
static const SystemInfo* info = new SystemInfo();
|
||||||
|
return *info;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemInfo::SystemInfo() : name(GetSystemName()) {}
|
||||||
|
} // end namespace benchmark
|
63
third_party/benchmark/src/thread_manager.h
vendored
Normal file
63
third_party/benchmark/src/thread_manager.h
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef BENCHMARK_THREAD_MANAGER_H
|
||||||
|
#define BENCHMARK_THREAD_MANAGER_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class ThreadManager {
|
||||||
|
public:
|
||||||
|
explicit 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; });
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Result {
|
||||||
|
IterationCount iterations = 0;
|
||||||
|
double real_time_used = 0;
|
||||||
|
double cpu_time_used = 0;
|
||||||
|
double manual_time_used = 0;
|
||||||
|
int64_t complexity_n = 0;
|
||||||
|
std::string report_label_;
|
||||||
|
std::string skip_message_;
|
||||||
|
internal::Skipped skipped_ = internal::NotSkipped;
|
||||||
|
UserCounters counters;
|
||||||
|
};
|
||||||
|
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_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_THREAD_MANAGER_H
|
86
third_party/benchmark/src/thread_timer.h
vendored
Normal file
86
third_party/benchmark/src/thread_timer.h
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#ifndef BENCHMARK_THREAD_TIMER_H
|
||||||
|
#define BENCHMARK_THREAD_TIMER_H
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class ThreadTimer {
|
||||||
|
explicit ThreadTimer(bool measure_process_cpu_time_)
|
||||||
|
: measure_process_cpu_time(measure_process_cpu_time_) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ThreadTimer Create() {
|
||||||
|
return ThreadTimer(/*measure_process_cpu_time_=*/false);
|
||||||
|
}
|
||||||
|
static ThreadTimer CreateProcessCpuTime() {
|
||||||
|
return ThreadTimer(/*measure_process_cpu_time_=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by each thread
|
||||||
|
void StartTimer() {
|
||||||
|
running_ = true;
|
||||||
|
start_real_time_ = ChronoClockNow();
|
||||||
|
start_cpu_time_ = ReadCpuTimerOfChoice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by each thread
|
||||||
|
void StopTimer() {
|
||||||
|
BM_CHECK(running_);
|
||||||
|
running_ = false;
|
||||||
|
real_time_used_ += ChronoClockNow() - start_real_time_;
|
||||||
|
// Floating point error can result in the subtraction producing a negative
|
||||||
|
// time. Guard against that.
|
||||||
|
cpu_time_used_ +=
|
||||||
|
std::max<double>(ReadCpuTimerOfChoice() - start_cpu_time_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() const {
|
||||||
|
BM_CHECK(!running_);
|
||||||
|
return real_time_used_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REQUIRES: timer is not running
|
||||||
|
double cpu_time_used() const {
|
||||||
|
BM_CHECK(!running_);
|
||||||
|
return cpu_time_used_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REQUIRES: timer is not running
|
||||||
|
double manual_time_used() const {
|
||||||
|
BM_CHECK(!running_);
|
||||||
|
return manual_time_used_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double ReadCpuTimerOfChoice() const {
|
||||||
|
if (measure_process_cpu_time) return ProcessCPUUsage();
|
||||||
|
return ThreadCPUUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// should the thread, or the process, time be measured?
|
||||||
|
const bool measure_process_cpu_time;
|
||||||
|
|
||||||
|
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 internal
|
||||||
|
} // namespace benchmark
|
||||||
|
|
||||||
|
#endif // BENCHMARK_THREAD_TIMER_H
|
276
third_party/benchmark/src/timers.cc
vendored
Normal file
276
third_party/benchmark/src/timers.cc
vendored
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
// 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>
|
||||||
|
#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA
|
||||||
|
#include <versionhelpers.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#if !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
#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_DRAGONFLY || \
|
||||||
|
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
|
||||||
|
#if defined(BENCHMARK_OS_QURT)
|
||||||
|
#include <qurt.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_OS_EMSCRIPTEN
|
||||||
|
#include <emscripten.h>
|
||||||
|
#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 "string_util.h"
|
||||||
|
|
||||||
|
namespace benchmark {
|
||||||
|
|
||||||
|
// Suppress unused warnings on helper functions.
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||||
|
#endif
|
||||||
|
#if defined(__NVCOMPILER)
|
||||||
|
#pragma diag_suppress declared_but_not_referenced
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
#elif !defined(BENCHMARK_OS_FUCHSIA) && !defined(BENCHMARK_OS_QURT)
|
||||||
|
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 static_cast<double>(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() {
|
||||||
|
#if 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");
|
||||||
|
#elif defined(BENCHMARK_OS_QURT)
|
||||||
|
return static_cast<double>(
|
||||||
|
qurt_timer_timetick_to_us(qurt_timer_get_ticks())) *
|
||||||
|
1.0e-6;
|
||||||
|
#elif defined(BENCHMARK_OS_EMSCRIPTEN)
|
||||||
|
// clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) returns 0 on Emscripten.
|
||||||
|
// Use Emscripten-specific API. Reported CPU time would be exactly the
|
||||||
|
// same as total time, but this is ok because there aren't long-latency
|
||||||
|
// synchronous system calls in Emscripten.
|
||||||
|
return emscripten_get_now() * 1e-3;
|
||||||
|
#elif defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX)
|
||||||
|
// FIXME We want to use clock_gettime, but its not available in MacOS 10.11.
|
||||||
|
// See https://github.com/google/benchmark/pull/292
|
||||||
|
struct timespec spec;
|
||||||
|
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0)
|
||||||
|
return MakeTime(spec);
|
||||||
|
DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");
|
||||||
|
#else
|
||||||
|
struct rusage ru;
|
||||||
|
if (getrusage(RUSAGE_SELF, &ru) == 0) return MakeTime(ru);
|
||||||
|
DiagnoseAndExit("getrusage(RUSAGE_SELF, ...) failed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
double ThreadCPUUsage() {
|
||||||
|
#if 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_QURT)
|
||||||
|
return static_cast<double>(
|
||||||
|
qurt_timer_timetick_to_us(qurt_timer_get_ticks())) *
|
||||||
|
1.0e-6;
|
||||||
|
#elif defined(BENCHMARK_OS_MACOSX)
|
||||||
|
// FIXME We want to use clock_gettime, but its not available in MacOS 10.11.
|
||||||
|
// See https://github.com/google/benchmark/pull/292
|
||||||
|
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,
|
||||||
|
reinterpret_cast<thread_info_t>(&info),
|
||||||
|
&count) == KERN_SUCCESS) {
|
||||||
|
return MakeTime(info);
|
||||||
|
}
|
||||||
|
DiagnoseAndExit("ThreadCPUUsage() failed when evaluating thread_info");
|
||||||
|
#elif defined(BENCHMARK_OS_EMSCRIPTEN)
|
||||||
|
// Emscripten doesn't support traditional threads
|
||||||
|
return ProcessCPUUsage();
|
||||||
|
#elif defined(BENCHMARK_OS_RTEMS)
|
||||||
|
// RTEMS doesn't support CLOCK_THREAD_CPUTIME_ID. See
|
||||||
|
// https://github.com/RTEMS/rtems/blob/master/cpukit/posix/src/clockgettime.c
|
||||||
|
return ProcessCPUUsage();
|
||||||
|
#elif defined(BENCHMARK_OS_ZOS)
|
||||||
|
// z/OS doesn't support CLOCK_THREAD_CPUTIME_ID.
|
||||||
|
return ProcessCPUUsage();
|
||||||
|
#elif defined(BENCHMARK_OS_SOLARIS)
|
||||||
|
struct rusage ru;
|
||||||
|
if (getrusage(RUSAGE_LWP, &ru) == 0) return MakeTime(ru);
|
||||||
|
DiagnoseAndExit("getrusage(RUSAGE_LWP, ...) failed");
|
||||||
|
#elif defined(CLOCK_THREAD_CPUTIME_ID)
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts);
|
||||||
|
DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed");
|
||||||
|
#else
|
||||||
|
#error Per-thread timing is not available on your system.
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LocalDateTimeString() {
|
||||||
|
// Write the local time in RFC3339 format yyyy-mm-ddTHH:MM:SS+/-HH:MM.
|
||||||
|
typedef std::chrono::system_clock Clock;
|
||||||
|
std::time_t now = Clock::to_time_t(Clock::now());
|
||||||
|
const std::size_t kTzOffsetLen = 6;
|
||||||
|
const std::size_t kTimestampLen = 19;
|
||||||
|
|
||||||
|
std::size_t tz_len;
|
||||||
|
std::size_t timestamp_len;
|
||||||
|
long int offset_minutes;
|
||||||
|
char tz_offset_sign = '+';
|
||||||
|
// tz_offset is set in one of three ways:
|
||||||
|
// * strftime with %z - This either returns empty or the ISO 8601 time. The
|
||||||
|
// maximum length an
|
||||||
|
// ISO 8601 string can be is 7 (e.g. -03:30, plus trailing zero).
|
||||||
|
// * snprintf with %c%02li:%02li - The maximum length is 41 (one for %c, up to
|
||||||
|
// 19 for %02li,
|
||||||
|
// one for :, up to 19 %02li, plus trailing zero).
|
||||||
|
// * A fixed string of "-00:00". The maximum length is 7 (-00:00, plus
|
||||||
|
// trailing zero).
|
||||||
|
//
|
||||||
|
// Thus, the maximum size this needs to be is 41.
|
||||||
|
char tz_offset[41];
|
||||||
|
// Long enough buffer to avoid format-overflow warnings
|
||||||
|
char storage[128];
|
||||||
|
|
||||||
|
#if defined(BENCHMARK_OS_WINDOWS)
|
||||||
|
std::tm* timeinfo_p = ::localtime(&now);
|
||||||
|
#else
|
||||||
|
std::tm timeinfo;
|
||||||
|
std::tm* timeinfo_p = &timeinfo;
|
||||||
|
::localtime_r(&now, &timeinfo);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tz_len = std::strftime(tz_offset, sizeof(tz_offset), "%z", timeinfo_p);
|
||||||
|
|
||||||
|
if (tz_len < kTzOffsetLen && tz_len > 1) {
|
||||||
|
// Timezone offset was written. strftime writes offset as +HHMM or -HHMM,
|
||||||
|
// RFC3339 specifies an offset as +HH:MM or -HH:MM. To convert, we parse
|
||||||
|
// the offset as an integer, then reprint it to a string.
|
||||||
|
|
||||||
|
offset_minutes = ::strtol(tz_offset, NULL, 10);
|
||||||
|
if (offset_minutes < 0) {
|
||||||
|
offset_minutes *= -1;
|
||||||
|
tz_offset_sign = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
tz_len = static_cast<size_t>(
|
||||||
|
::snprintf(tz_offset, sizeof(tz_offset), "%c%02li:%02li",
|
||||||
|
tz_offset_sign, offset_minutes / 100, offset_minutes % 100));
|
||||||
|
BM_CHECK(tz_len == kTzOffsetLen);
|
||||||
|
((void)tz_len); // Prevent unused variable warning in optimized build.
|
||||||
|
} else {
|
||||||
|
// Unknown offset. RFC3339 specifies that unknown local offsets should be
|
||||||
|
// written as UTC time with -00:00 timezone.
|
||||||
|
#if defined(BENCHMARK_OS_WINDOWS)
|
||||||
|
// Potential race condition if another thread calls localtime or gmtime.
|
||||||
|
timeinfo_p = ::gmtime(&now);
|
||||||
|
#else
|
||||||
|
::gmtime_r(&now, &timeinfo);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
strncpy(tz_offset, "-00:00", kTzOffsetLen + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp_len =
|
||||||
|
std::strftime(storage, sizeof(storage), "%Y-%m-%dT%H:%M:%S", timeinfo_p);
|
||||||
|
BM_CHECK(timestamp_len == kTimestampLen);
|
||||||
|
// Prevent unused variable warning in optimized build.
|
||||||
|
((void)kTimestampLen);
|
||||||
|
|
||||||
|
std::strncat(storage, tz_offset, sizeof(storage) - timestamp_len - 1);
|
||||||
|
return std::string(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace benchmark
|
48
third_party/benchmark/src/timers.h
vendored
Normal file
48
third_party/benchmark/src/timers.h
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#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
|
10709
third_party/curl/CHANGES
vendored
Normal file
10709
third_party/curl/CHANGES
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
third_party/curl/CMake/CMakeConfigurableFile.in
vendored
Normal file
24
third_party/curl/CMake/CMakeConfigurableFile.in
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#***************************************************************************
|
||||||
|
# _ _ ____ _
|
||||||
|
# Project ___| | | | _ \| |
|
||||||
|
# / __| | | | |_) | |
|
||||||
|
# | (__| |_| | _ <| |___
|
||||||
|
# \___|\___/|_| \_\_____|
|
||||||
|
#
|
||||||
|
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
#
|
||||||
|
# This software is licensed as described in the file COPYING, which
|
||||||
|
# you should have received as part of this distribution. The terms
|
||||||
|
# are also available at https://curl.se/docs/copyright.html.
|
||||||
|
#
|
||||||
|
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
# copies of the Software, and permit persons to whom the Software is
|
||||||
|
# furnished to do so, under the terms of the COPYING file.
|
||||||
|
#
|
||||||
|
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
# KIND, either express or implied.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: curl
|
||||||
|
#
|
||||||
|
###########################################################################
|
||||||
|
@CMAKE_CONFIGURABLE_FILE_CONTENT@
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user