2020-05-07 00:28:29 +08:00
|
|
|
// Benchmark for Python.
|
|
|
|
|
2022-02-14 18:48:53 +08:00
|
|
|
#include "benchmark/benchmark.h"
|
|
|
|
|
2020-09-10 16:57:30 +08:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "pybind11/operators.h"
|
2020-05-07 00:28:29 +08:00
|
|
|
#include "pybind11/pybind11.h"
|
|
|
|
#include "pybind11/stl.h"
|
2020-09-10 16:57:30 +08:00
|
|
|
#include "pybind11/stl_bind.h"
|
|
|
|
|
|
|
|
PYBIND11_MAKE_OPAQUE(benchmark::UserCounters);
|
2020-05-07 00:28:29 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
namespace py = ::pybind11;
|
|
|
|
|
|
|
|
std::vector<std::string> Initialize(const std::vector<std::string>& 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<char*> ptrs;
|
|
|
|
ptrs.reserve(argv.size());
|
|
|
|
for (auto& arg : argv) {
|
|
|
|
ptrs.push_back(const_cast<char*>(arg.c_str()));
|
|
|
|
}
|
|
|
|
ptrs[0] = const_cast<char*>(executable_name.c_str());
|
|
|
|
int argc = static_cast<int>(argv.size());
|
|
|
|
benchmark::Initialize(&argc, ptrs.data());
|
|
|
|
std::vector<std::string> remaining_argv;
|
|
|
|
remaining_argv.reserve(argc);
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
remaining_argv.emplace_back(ptrs[i]);
|
|
|
|
}
|
|
|
|
return remaining_argv;
|
|
|
|
}
|
|
|
|
|
2020-09-11 17:55:18 +08:00
|
|
|
benchmark::internal::Benchmark* RegisterBenchmark(const char* name,
|
|
|
|
py::function f) {
|
|
|
|
return benchmark::RegisterBenchmark(
|
|
|
|
name, [f](benchmark::State& state) { f(&state); });
|
2020-05-07 00:28:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
PYBIND11_MODULE(_benchmark, m) {
|
2020-09-11 17:55:18 +08:00
|
|
|
using benchmark::TimeUnit;
|
|
|
|
py::enum_<TimeUnit>(m, "TimeUnit")
|
|
|
|
.value("kNanosecond", TimeUnit::kNanosecond)
|
|
|
|
.value("kMicrosecond", TimeUnit::kMicrosecond)
|
|
|
|
.value("kMillisecond", TimeUnit::kMillisecond)
|
2020-12-22 01:15:58 +08:00
|
|
|
.value("kSecond", TimeUnit::kSecond)
|
2020-09-11 17:55:18 +08:00
|
|
|
.export_values();
|
|
|
|
|
|
|
|
using benchmark::BigO;
|
|
|
|
py::enum_<BigO>(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_<Benchmark>(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("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);
|
2020-05-07 00:28:29 +08:00
|
|
|
|
2020-09-10 16:57:30 +08:00
|
|
|
using benchmark::Counter;
|
|
|
|
py::class_<Counter> py_counter(m, "Counter");
|
|
|
|
|
|
|
|
py::enum_<Counter::Flags>(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_<Counter::OneK>(py_counter, "OneK")
|
|
|
|
.value("kIs1000", Counter::OneK::kIs1000)
|
|
|
|
.value("kIs1024", Counter::OneK::kIs1024)
|
|
|
|
.export_values();
|
|
|
|
|
|
|
|
py_counter
|
|
|
|
.def(py::init<double, Counter::Flags, Counter::OneK>(),
|
|
|
|
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::float_, Counter>();
|
|
|
|
py::implicitly_convertible<py::int_, Counter>();
|
|
|
|
|
|
|
|
py::bind_map<benchmark::UserCounters>(m, "UserCounters");
|
|
|
|
|
|
|
|
using benchmark::State;
|
|
|
|
py::class_<State>(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)
|
2021-04-09 20:00:43 +08:00
|
|
|
.def_property_readonly("error_occurred", &State::error_occurred)
|
2020-09-10 16:57:30 +08:00
|
|
|
.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)
|
Introduce accessors for currently public data members (threads and thread_index) (#1208)
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate the direct access to these fields.
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate the direct access to these fields.
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
2021-08-23 16:06:57 +08:00
|
|
|
.def("set_label", (void(State::*)(const char*)) & State::SetLabel)
|
2020-09-10 16:57:30 +08:00
|
|
|
.def("range", &State::range, py::arg("pos") = 0)
|
|
|
|
.def_property_readonly("iterations", &State::iterations)
|
|
|
|
.def_readwrite("counters", &State::counters)
|
Introduce accessors for currently public data members (threads and thread_index) (#1208)
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate the direct access to these fields.
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate the direct access to these fields.
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
* [benchmark] Introduce accessors for currently public data members `threads` and `thread_index`
Also deprecate direct access to `.thread_index` and make threads a private field
Motivations:
Our internal library provides accessors for those fields because the styleguide disalows accessing classes' data members directly (even if they're const).
There has been a discussion to simply move internal library to make its fields public similarly to the OSS version here, however, the concern is that these kinds of direct access would prevent many types of future design changes (eg how/whether the values would be stored in the data member)
I think the concensus in the end is that we'd change the external library for this case.
AFAIK, there are three important third_party users that we'd need to migrate: tcmalloc, abseil and tensorflow.
Please let me know if I'm missing anyone else.
2021-08-23 16:06:57 +08:00
|
|
|
.def_property_readonly("thread_index", &State::thread_index)
|
|
|
|
.def_property_readonly("threads", &State::threads);
|
2020-09-11 17:55:18 +08:00
|
|
|
|
|
|
|
m.def("Initialize", Initialize);
|
|
|
|
m.def("RegisterBenchmark", RegisterBenchmark,
|
|
|
|
py::return_value_policy::reference);
|
|
|
|
m.def("RunSpecifiedBenchmarks",
|
|
|
|
[]() { benchmark::RunSpecifiedBenchmarks(); });
|
2020-05-07 00:28:29 +08:00
|
|
|
};
|
|
|
|
} // namespace
|