• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Benchmark for Python.
2 
3 #include <map>
4 #include <string>
5 #include <vector>
6 
7 #include "pybind11/operators.h"
8 #include "pybind11/pybind11.h"
9 #include "pybind11/stl.h"
10 #include "pybind11/stl_bind.h"
11 
12 #include "benchmark/benchmark.h"
13 
14 PYBIND11_MAKE_OPAQUE(benchmark::UserCounters);
15 
16 namespace {
17 namespace py = ::pybind11;
18 
Initialize(const std::vector<std::string> & argv)19 std::vector<std::string> Initialize(const std::vector<std::string>& argv) {
20   // The `argv` pointers here become invalid when this function returns, but
21   // benchmark holds the pointer to `argv[0]`. We create a static copy of it
22   // so it persists, and replace the pointer below.
23   static std::string executable_name(argv[0]);
24   std::vector<char*> ptrs;
25   ptrs.reserve(argv.size());
26   for (auto& arg : argv) {
27     ptrs.push_back(const_cast<char*>(arg.c_str()));
28   }
29   ptrs[0] = const_cast<char*>(executable_name.c_str());
30   int argc = static_cast<int>(argv.size());
31   benchmark::Initialize(&argc, ptrs.data());
32   std::vector<std::string> remaining_argv;
33   remaining_argv.reserve(argc);
34   for (int i = 0; i < argc; ++i) {
35     remaining_argv.emplace_back(ptrs[i]);
36   }
37   return remaining_argv;
38 }
39 
RegisterBenchmark(const char * name,py::function f)40 benchmark::internal::Benchmark* RegisterBenchmark(const char* name,
41                                                   py::function f) {
42   return benchmark::RegisterBenchmark(
43       name, [f](benchmark::State& state) { f(&state); });
44 }
45 
PYBIND11_MODULE(_benchmark,m)46 PYBIND11_MODULE(_benchmark, m) {
47   using benchmark::TimeUnit;
48   py::enum_<TimeUnit>(m, "TimeUnit")
49       .value("kNanosecond", TimeUnit::kNanosecond)
50       .value("kMicrosecond", TimeUnit::kMicrosecond)
51       .value("kMillisecond", TimeUnit::kMillisecond)
52       .export_values();
53 
54   using benchmark::BigO;
55   py::enum_<BigO>(m, "BigO")
56       .value("oNone", BigO::oNone)
57       .value("o1", BigO::o1)
58       .value("oN", BigO::oN)
59       .value("oNSquared", BigO::oNSquared)
60       .value("oNCubed", BigO::oNCubed)
61       .value("oLogN", BigO::oLogN)
62       .value("oNLogN", BigO::oLogN)
63       .value("oAuto", BigO::oAuto)
64       .value("oLambda", BigO::oLambda)
65       .export_values();
66 
67   using benchmark::internal::Benchmark;
68   py::class_<Benchmark>(m, "Benchmark")
69       // For methods returning a pointer tor the current object, reference
70       // return policy is used to ask pybind not to take ownership oof the
71       // returned object and avoid calling delete on it.
72       // https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies
73       //
74       // For methods taking a const std::vector<...>&, a copy is created
75       // because a it is bound to a Python list.
76       // https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
77       .def("unit", &Benchmark::Unit, py::return_value_policy::reference)
78       .def("arg", &Benchmark::Arg, py::return_value_policy::reference)
79       .def("args", &Benchmark::Args, py::return_value_policy::reference)
80       .def("range", &Benchmark::Range, py::return_value_policy::reference,
81            py::arg("start"), py::arg("limit"))
82       .def("dense_range", &Benchmark::DenseRange,
83            py::return_value_policy::reference, py::arg("start"),
84            py::arg("limit"), py::arg("step") = 1)
85       .def("ranges", &Benchmark::Ranges, py::return_value_policy::reference)
86       .def("args_product", &Benchmark::ArgsProduct,
87            py::return_value_policy::reference)
88       .def("arg_name", &Benchmark::ArgName, py::return_value_policy::reference)
89       .def("arg_names", &Benchmark::ArgNames,
90            py::return_value_policy::reference)
91       .def("range_pair", &Benchmark::RangePair,
92            py::return_value_policy::reference, py::arg("lo1"), py::arg("hi1"),
93            py::arg("lo2"), py::arg("hi2"))
94       .def("range_multiplier", &Benchmark::RangeMultiplier,
95            py::return_value_policy::reference)
96       .def("min_time", &Benchmark::MinTime, py::return_value_policy::reference)
97       .def("iterations", &Benchmark::Iterations,
98            py::return_value_policy::reference)
99       .def("repetitions", &Benchmark::Repetitions,
100            py::return_value_policy::reference)
101       .def("report_aggregates_only", &Benchmark::ReportAggregatesOnly,
102            py::return_value_policy::reference, py::arg("value") = true)
103       .def("display_aggregates_only", &Benchmark::DisplayAggregatesOnly,
104            py::return_value_policy::reference, py::arg("value") = true)
105       .def("measure_process_cpu_time", &Benchmark::MeasureProcessCPUTime,
106            py::return_value_policy::reference)
107       .def("use_real_time", &Benchmark::UseRealTime,
108            py::return_value_policy::reference)
109       .def("use_manual_time", &Benchmark::UseManualTime,
110            py::return_value_policy::reference)
111       .def(
112           "complexity",
113           (Benchmark * (Benchmark::*)(benchmark::BigO)) & Benchmark::Complexity,
114           py::return_value_policy::reference,
115           py::arg("complexity") = benchmark::oAuto);
116 
117   using benchmark::Counter;
118   py::class_<Counter> py_counter(m, "Counter");
119 
120   py::enum_<Counter::Flags>(py_counter, "Flags")
121       .value("kDefaults", Counter::Flags::kDefaults)
122       .value("kIsRate", Counter::Flags::kIsRate)
123       .value("kAvgThreads", Counter::Flags::kAvgThreads)
124       .value("kAvgThreadsRate", Counter::Flags::kAvgThreadsRate)
125       .value("kIsIterationInvariant", Counter::Flags::kIsIterationInvariant)
126       .value("kIsIterationInvariantRate",
127              Counter::Flags::kIsIterationInvariantRate)
128       .value("kAvgIterations", Counter::Flags::kAvgIterations)
129       .value("kAvgIterationsRate", Counter::Flags::kAvgIterationsRate)
130       .value("kInvert", Counter::Flags::kInvert)
131       .export_values()
132       .def(py::self | py::self);
133 
134   py::enum_<Counter::OneK>(py_counter, "OneK")
135       .value("kIs1000", Counter::OneK::kIs1000)
136       .value("kIs1024", Counter::OneK::kIs1024)
137       .export_values();
138 
139   py_counter
140       .def(py::init<double, Counter::Flags, Counter::OneK>(),
141            py::arg("value") = 0., py::arg("flags") = Counter::kDefaults,
142            py::arg("k") = Counter::kIs1000)
143       .def(py::init([](double value) { return Counter(value); }))
144       .def_readwrite("value", &Counter::value)
145       .def_readwrite("flags", &Counter::flags)
146       .def_readwrite("oneK", &Counter::oneK);
147   py::implicitly_convertible<py::float_, Counter>();
148   py::implicitly_convertible<py::int_, Counter>();
149 
150   py::bind_map<benchmark::UserCounters>(m, "UserCounters");
151 
152   using benchmark::State;
153   py::class_<State>(m, "State")
154       .def("__bool__", &State::KeepRunning)
155       .def_property_readonly("keep_running", &State::KeepRunning)
156       .def("pause_timing", &State::PauseTiming)
157       .def("resume_timing", &State::ResumeTiming)
158       .def("skip_with_error", &State::SkipWithError)
159       .def_property_readonly("error_occured", &State::error_occurred)
160       .def("set_iteration_time", &State::SetIterationTime)
161       .def_property("bytes_processed", &State::bytes_processed,
162                     &State::SetBytesProcessed)
163       .def_property("complexity_n", &State::complexity_length_n,
164                     &State::SetComplexityN)
165       .def_property("items_processed", &State::items_processed,
166                     &State::SetItemsProcessed)
167       .def("set_label", (void (State::*)(const char*)) & State::SetLabel)
168       .def("range", &State::range, py::arg("pos") = 0)
169       .def_property_readonly("iterations", &State::iterations)
170       .def_readwrite("counters", &State::counters)
171       .def_readonly("thread_index", &State::thread_index)
172       .def_readonly("threads", &State::threads);
173 
174   m.def("Initialize", Initialize);
175   m.def("RegisterBenchmark", RegisterBenchmark,
176         py::return_value_policy::reference);
177   m.def("RunSpecifiedBenchmarks",
178         []() { benchmark::RunSpecifiedBenchmarks(); });
179 };
180 }  // namespace
181