// Benchmark for Python. #include "benchmark/benchmark.h" #include #include #include #include "pybind11/operators.h" #include "pybind11/pybind11.h" #include "pybind11/stl.h" #include "pybind11/stl_bind.h" PYBIND11_MAKE_OPAQUE(benchmark::UserCounters); namespace { namespace py = ::pybind11; std::vector Initialize(const std::vector& argv) { // The `argv` pointers here become invalid when this function returns, but // benchmark holds the pointer to `argv[0]`. We create a static copy of it // so it persists, and replace the pointer below. static std::string executable_name(argv[0]); std::vector ptrs; ptrs.reserve(argv.size()); for (auto& arg : argv) { ptrs.push_back(const_cast(arg.c_str())); } ptrs[0] = const_cast(executable_name.c_str()); int argc = static_cast(argv.size()); benchmark::Initialize(&argc, ptrs.data()); std::vector remaining_argv; remaining_argv.reserve(argc); for (int i = 0; i < argc; ++i) { remaining_argv.emplace_back(ptrs[i]); } return remaining_argv; } benchmark::internal::Benchmark* RegisterBenchmark(const char* name, py::function f) { return benchmark::RegisterBenchmark( name, [f](benchmark::State& state) { f(&state); }); } PYBIND11_MODULE(_benchmark, m) { using benchmark::TimeUnit; py::enum_(m, "TimeUnit") .value("kNanosecond", TimeUnit::kNanosecond) .value("kMicrosecond", TimeUnit::kMicrosecond) .value("kMillisecond", TimeUnit::kMillisecond) .value("kSecond", TimeUnit::kSecond) .export_values(); using benchmark::BigO; py::enum_(m, "BigO") .value("oNone", BigO::oNone) .value("o1", BigO::o1) .value("oN", BigO::oN) .value("oNSquared", BigO::oNSquared) .value("oNCubed", BigO::oNCubed) .value("oLogN", BigO::oLogN) .value("oNLogN", BigO::oLogN) .value("oAuto", BigO::oAuto) .value("oLambda", BigO::oLambda) .export_values(); using benchmark::internal::Benchmark; py::class_(m, "Benchmark") // For methods returning a pointer tor the current object, reference // return policy is used to ask pybind not to take ownership oof the // returned object and avoid calling delete on it. // https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies // // For methods taking a const std::vector<...>&, a copy is created // because a it is bound to a Python list. // https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html .def("unit", &Benchmark::Unit, py::return_value_policy::reference) .def("arg", &Benchmark::Arg, py::return_value_policy::reference) .def("args", &Benchmark::Args, py::return_value_policy::reference) .def("range", &Benchmark::Range, py::return_value_policy::reference, py::arg("start"), py::arg("limit")) .def("dense_range", &Benchmark::DenseRange, py::return_value_policy::reference, py::arg("start"), py::arg("limit"), py::arg("step") = 1) .def("ranges", &Benchmark::Ranges, py::return_value_policy::reference) .def("args_product", &Benchmark::ArgsProduct, py::return_value_policy::reference) .def("arg_name", &Benchmark::ArgName, py::return_value_policy::reference) .def("arg_names", &Benchmark::ArgNames, py::return_value_policy::reference) .def("range_pair", &Benchmark::RangePair, py::return_value_policy::reference, py::arg("lo1"), py::arg("hi1"), py::arg("lo2"), py::arg("hi2")) .def("range_multiplier", &Benchmark::RangeMultiplier, py::return_value_policy::reference) .def("min_time", &Benchmark::MinTime, py::return_value_policy::reference) .def("min_warmup_time", &Benchmark::MinWarmUpTime, py::return_value_policy::reference) .def("iterations", &Benchmark::Iterations, py::return_value_policy::reference) .def("repetitions", &Benchmark::Repetitions, py::return_value_policy::reference) .def("report_aggregates_only", &Benchmark::ReportAggregatesOnly, py::return_value_policy::reference, py::arg("value") = true) .def("display_aggregates_only", &Benchmark::DisplayAggregatesOnly, py::return_value_policy::reference, py::arg("value") = true) .def("measure_process_cpu_time", &Benchmark::MeasureProcessCPUTime, py::return_value_policy::reference) .def("use_real_time", &Benchmark::UseRealTime, py::return_value_policy::reference) .def("use_manual_time", &Benchmark::UseManualTime, py::return_value_policy::reference) .def( "complexity", (Benchmark * (Benchmark::*)(benchmark::BigO)) & Benchmark::Complexity, py::return_value_policy::reference, py::arg("complexity") = benchmark::oAuto); using benchmark::Counter; py::class_ py_counter(m, "Counter"); py::enum_(py_counter, "Flags") .value("kDefaults", Counter::Flags::kDefaults) .value("kIsRate", Counter::Flags::kIsRate) .value("kAvgThreads", Counter::Flags::kAvgThreads) .value("kAvgThreadsRate", Counter::Flags::kAvgThreadsRate) .value("kIsIterationInvariant", Counter::Flags::kIsIterationInvariant) .value("kIsIterationInvariantRate", Counter::Flags::kIsIterationInvariantRate) .value("kAvgIterations", Counter::Flags::kAvgIterations) .value("kAvgIterationsRate", Counter::Flags::kAvgIterationsRate) .value("kInvert", Counter::Flags::kInvert) .export_values() .def(py::self | py::self); py::enum_(py_counter, "OneK") .value("kIs1000", Counter::OneK::kIs1000) .value("kIs1024", Counter::OneK::kIs1024) .export_values(); py_counter .def(py::init(), py::arg("value") = 0., py::arg("flags") = Counter::kDefaults, py::arg("k") = Counter::kIs1000) .def(py::init([](double value) { return Counter(value); })) .def_readwrite("value", &Counter::value) .def_readwrite("flags", &Counter::flags) .def_readwrite("oneK", &Counter::oneK); py::implicitly_convertible(); py::implicitly_convertible(); py::bind_map(m, "UserCounters"); using benchmark::State; py::class_(m, "State") .def("__bool__", &State::KeepRunning) .def_property_readonly("keep_running", &State::KeepRunning) .def("pause_timing", &State::PauseTiming) .def("resume_timing", &State::ResumeTiming) .def("skip_with_error", &State::SkipWithError) .def_property_readonly("error_occurred", &State::error_occurred) .def("set_iteration_time", &State::SetIterationTime) .def_property("bytes_processed", &State::bytes_processed, &State::SetBytesProcessed) .def_property("complexity_n", &State::complexity_length_n, &State::SetComplexityN) .def_property("items_processed", &State::items_processed, &State::SetItemsProcessed) .def("set_label", (void (State::*)(const char*)) & State::SetLabel) .def("range", &State::range, py::arg("pos") = 0) .def_property_readonly("iterations", &State::iterations) .def_readwrite("counters", &State::counters) .def_property_readonly("thread_index", &State::thread_index) .def_property_readonly("threads", &State::threads); m.def("Initialize", Initialize); m.def("RegisterBenchmark", RegisterBenchmark, py::return_value_policy::reference); m.def("RunSpecifiedBenchmarks", []() { benchmark::RunSpecifiedBenchmarks(); }); m.def("ClearRegisteredBenchmarks", benchmark::ClearRegisteredBenchmarks); }; } // namespace