当我尝试测量算术运算的执行时间时,我遇到了非常奇怪的行为。包含for
循环体中具有一个算术运算的循环的代码块总是比相同的代码块执行得慢,但在for
循环体中具有两个算术运算。这是我最终测试的代码:
#include <iostream>
#include <chrono>
#define NUM_ITERATIONS 100000000
int main()
{
// Block 1: one operation in loop body
{
int64_t x = 0, y = 0;
auto start = std::chrono::high_resolution_clock::now();
for (long i = 0; i < NUM_ITERATIONS; i++) {x+=31;}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << diff.count() << " seconds. x,y = " << x << "," << y << std::endl;
}
// Block 2: two operations in loop …
Run Code Online (Sandbox Code Playgroud) 我知道该--benchmark_repetitions
标志,但这不是我需要的。我希望能够指定一个基准测试的迭代次数。我可以使用一个--benchmark_iterations
标志来设置所有基准的迭代次数。
我知道谷歌基准测试很聪明,可以计算出需要多少次迭代才能获得良好的测量结果。这对于大多数用例来说已经足够了,但我的用例有所不同。我需要能够精确控制迭代次数。
我有一个 C++ Google 基准测试程序。它使用谷歌的BENCHMARK_MAIN()
方法。现在我用 Go 脚本调用并执行编译后的程序。有没有办法将参数传递到我的基准程序中?(我知道 main 方法的常见方法,但我不确定如何在 Googletest 中执行此操作,因为它是在中实现的benchmark_api.h
,我不能只是更改它。)
更新:
到目前为止,我将宏主体复制到我的宏主体中benchmark.cpp
并添加了一行。这不是一个很好的解决方案,因为 Google 对此宏的可能更改(例如名称更改或添加的代码行)不会影响我的副本。它终于起作用了。
int main (int argc, char** argv)
{
MyNamespace::conf = {argv[1]};
::benchmark::Initialize (&argc, argv);
::benchmark::RunSpecifiedBenchmarks ();
}
Run Code Online (Sandbox Code Playgroud) c++ benchmarking google-api parameter-passing google-benchmark
void DoNotOptimize
我对Google Benchmark Framework 的功能实现有点困惑(定义来自这里):
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
#if defined(__clang__)
asm volatile("" : "+r,m"(value) : : "memory");
#else
asm volatile("" : "+m,r"(value) : : "memory");
#endif
}
Run Code Online (Sandbox Code Playgroud)
因此,它具体化了变量,如果变量非常量,也会告诉编译器忘记有关其先前值的任何信息。("+r"
是 RMW 操作数)。
并且总是使用"memory"
clobber,这是编译器对重新排序加载/存储的障碍,即确保所有全局可访问的对象的内存与 C++ 抽象机同步,并假设它们也可能已被修改。
我距离成为低级代码专家还很远,但据我了解其实现,该函数充当读/写屏障。因此,基本上,它确保传入的值位于寄存器或内存中。
虽然如果我想保留函数的结果(应该进行基准测试),这似乎是完全合理的,但我对编译器留下的自由度感到有点惊讶。
我对给定代码的理解是,编译器可能会在DoNotOptimize
调用时插入物化点,这意味着重复执行时(例如,在循环中)会产生大量开销。当不应优化的值只是单个标量值时,如果编译器确保该值驻留在寄存器中似乎就足够了。
区分指针和非指针不是一个好主意吗?例如:
template< class T >
inline __attribute__((always_inline))
void do_not_optimize( …
Run Code Online (Sandbox Code Playgroud) c++ assembly inline-assembly microbenchmark google-benchmark
我正在使用基准库来对一些代码进行基准测试。我想在一次调用实际基准代码之前调用一个设置方法,而不是每次都重复调用,以便进行多个基准方法调用。例如:
static void BM_SomeFunction(benchmark::State& state) {
// Perform setup here
for (auto _ : state) {
// This code gets timed
}
}
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,对于我指定的范围,设置代码将在这里被多次调用。我确实查看了夹具测试。但我的问题是可以不使用夹具测试来完成吗?如果是的话我们该怎么做呢?
我正在尝试构建 google-benchmark 并使用 cmake 将它与我的库一起使用。我已经成功构建了 google-benchmark 并使用 cmake 成功运行了所有测试。不幸的是,我无法使用 cmake 或 cl 在 windows 中将它与我的 c++ 代码正确链接。
我认为的问题是 google-benchmark 在 src 文件夹中构建库,即它构建在 src/Release/benchmark.lib 现在我不能在 cmake 中指向它,如果我使用 ${benchmark_LIBRARIES} 它在src 外的 Release 文件夹,因为这是构建所有库的常用位置。并且很难找到在 Windows 中工作的示例。
这是我尝试过的两种方法,都可以构建库并运行所有测试,但我无法正确地将库指向 target_link_library
include(ExternalProject)
ExternalProject_Add(googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG master
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build"
CONFIGURE_COMMAND ${CMAKE_COMMAND} -B ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build -S ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build --config Release
INSTALL_COMMAND ""
TEST_COMMAND ${CMAKE_CTEST_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build --build-config Release
)
Run Code Online (Sandbox Code Playgroud)
和
ExternalProject_Add(googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG master
PREFIX googlebenchmark
CMAKE_ARGS -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
BUILD_COMMAND ${CMAKE_COMMAND} --build …
Run Code Online (Sandbox Code Playgroud) 我正在测试密集矩阵乘法(作为好奇心)的一些微基准测试,我注意到一些非常奇怪的性能结果.
这是一个最小的工作示例:
#include <benchmark/benchmark.h>
#include <random>
constexpr long long n = 128;
struct mat_bench_fixture : public benchmark::Fixture
{
double *matA, *matB, *matC;
mat_bench_fixture()
{
matA = new double[n * n];
matB = new double[n * n];
matC = new double[n * n];
benchmark::DoNotOptimize(matA);
benchmark::DoNotOptimize(matB);
benchmark::DoNotOptimize(matC);
#if 0
delete[] matA;
delete[] matB;
delete[] matC;
benchmark::DoNotOptimize(matA);
benchmark::DoNotOptimize(matB);
benchmark::DoNotOptimize(matC);
matA = new double[n * n];
matB = new double[n * n];
matC = new double[n * n];
benchmark::DoNotOptimize(matA);
benchmark::DoNotOptimize(matB);
benchmark::DoNotOptimize(matC);
#endif
}
~mat_bench_fixture() …
Run Code Online (Sandbox Code Playgroud) 我正在尝试编译一个依赖于 Libtorch (Pytorch) 库的谷歌基准测试。我已经安装了Google Benchmark,因此make install
据我了解,我应该能够使用 find_package() 来添加两个依赖项。最后我添加了一些编译器标志。
这是我的 CMakeLists.txt:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(learned_b VERSION 1.0)
add_executable(PROJECT_NAME learned_benchmark.cpp)
find_package(Torch REQUIRED)
find_package(benchmark REQUIRED)
target_link_libraries(PROJECT_NAME "${TORCH_LIBRARIES}")
target_include_directories(PROJECT_NAME PUBLIC "${benchmark_INCLUDE_DIRS}")
target_link_libraries(PROJECT_NAME "${benchmark_LIBRARIES}")
SET(GCC_LINK_FLAGS "-isystem /Users/yhr/Programs/benchmark/include -lbenchmark -pthread")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_LINK_FLAGS}")
set_property(TARGET PROJECT_NAME PROPERTY CXX_STANDARD 11)
Run Code Online (Sandbox Code Playgroud)
应该注意的是,无论有没有 GCC_LINK_FLAGS,我总是会收到致命错误:找不到“benchmark/benchmark.h”文件。我的代码在仅依赖于 Pytorch 时正在编译和运行。是否可以将 find_package 与 google benchmark 一起使用?如果不是,我该如何正确处理这个问题?
编辑1:
这是我运行过的命令。
$ cd build
$ cmake -DCMAKE_PREFIX_PATH='/Users/yhr/Programs/libtorch;/Users/yhr/Programs/benchmark' ..
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/yhr/Programs/learnedbloomfilters/OpenBloomFilter
$ cd …
Run Code Online (Sandbox Code Playgroud) 我有一个类似的代码:
...
void benchMark(benchmark::State& state){
maxCapacity = state.range(0);
// set up some stuff
for (auto _ : state){
// time this code
}
}
BENCHMARK(benchMark)->DenseRange(2, 10, 1);
BENCHMARK_MAIN();
Run Code Online (Sandbox Code Playgroud)
我想把它改成这样:
...
void benchMark(benchmark::State& state){
maxCapacity = state.range(0);
// set up some stuff
for (auto _ : state){
// time this code
}
}
int main(){
BENCHMARK(benchMark)->DenseRange(2, 10, 1);
}
Run Code Online (Sandbox Code Playgroud)
我这样做只是为了以后可以为代码提供命令行参数支持。现在这段代码编译成功,但我根本没有得到任何输出。我猜代码甚至没有运行。第一个代码需要 5 分钟才能完成,但第二个代码几乎立即完成执行。我究竟做错了什么?
任何帮助都会很棒。谢谢..
编辑:
由于我无法分享完整的代码,这里有一个最小的可重现示例:
#include <iostream>
#include <benchmark/benchmark.h>
using namespace std;
void pointQuery(int maxCapacity){
long sum = 0;
for(int i=0; …
Run Code Online (Sandbox Code Playgroud) 我想使用 Google Benchmark 对各种排序算法进行基准测试。如果我使用
static void StdSort(benchmark::State& state) {
auto v = generate_random_vector(state.range(0));
for (auto _ : state)
std::sort(std::begin(v), std::end(v));
}
BENCHMARK(StdSort)->Arg(10)->Arg(1000)->Arg(1'000'000);
Run Code Online (Sandbox Code Playgroud)
大多数时候我最终都会对预先排序的向量进行排序。我在手册中读到,我可以使用手动计时来仅对我关心的部分进行基准测试:
static void StdSort(benchmark::State& state) {
auto v = generate_random_vector(state.range(0));
std::default_random_engine gen;
for (auto _ : state) {
auto start = std::chrono::high_resolution_clock::now();
std::sort(std::begin(v), std::end(v));
auto end = std::chrono::high_resolution_clock::now();
auto elapsed_seconds =
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
state.SetIterationTime(elapsed_seconds.count());
std::shuffle(std::begin(v), std::end(v), gen);
}
}
BENCHMARK(StdSort)->Arg(10)->Arg(1000)->Arg(1'000'000)->UseManualTime();
Run Code Online (Sandbox Code Playgroud)
这是使用 Google Benchmark 对排序算法进行基准测试的正确方法吗?有更好的方法吗?
google-benchmark ×10
c++ ×8
benchmarking ×6
assembly ×2
cmake ×2
performance ×2
google-api ×1
sorting ×1