From 92620aa3ebfd92a805498ce8c37582d9334cd518 Mon Sep 17 00:00:00 2001
From: akuesters <42005107+akuesters@users.noreply.github.com>
Date: Wed, 3 Jul 2019 12:34:57 +0200
Subject: [PATCH] Python wrapper: profiler (#796)

Wraps the `meter_manager` with
- the constructor
- `start(ctx)`
- `checkpoint(name, ctx)`
- `checkpoint_names`
- `times`

and the `meter_report` with
- `make_meter_report(meter_manager, ctx)`

Fixes #765
---
 doc/py_intro.rst       |  2 +-
 python/CMakeLists.txt  |  1 +
 python/example/ring.py | 19 +++++++++++---
 python/profiler.cpp    | 57 ++++++++++++++++++++++++++++++++++++++++++
 python/pyarb.cpp       |  9 ++++---
 5 files changed, 81 insertions(+), 7 deletions(-)
 create mode 100644 python/profiler.cpp

diff --git a/doc/py_intro.rst b/doc/py_intro.rst
index 7d016656..5b020f80 100644
--- a/doc/py_intro.rst
+++ b/doc/py_intro.rst
@@ -2,7 +2,7 @@
 
 Overview
 =========
-The Python frontend for Arbor is interface that the vast majority of users will use to interact with Arbor.
+The Python frontend for Arbor is an interface that the vast majority of users will use to interact with Arbor.
 This section covers how to use the frontend with examples and detailed descriptions of features.
 
 .. _prerequisites:
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index e03878ec..063c5146 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -23,6 +23,7 @@ add_library(pyarb MODULE
     event_generator.cpp
     identifiers.cpp
     mpi.cpp
+    profiler.cpp
     pyarb.cpp
     recipe.cpp
     schedule.cpp
diff --git a/python/example/ring.py b/python/example/ring.py
index 65a230d8..f5ba456e 100644
--- a/python/example/ring.py
+++ b/python/example/ring.py
@@ -48,18 +48,31 @@ class ring_recipe (arbor.recipe):
 context = arbor.context(threads=4, gpu_id=None)
 print(context)
 
+meters = arbor.meter_manager()
+meters.start(context)
+
 recipe = ring_recipe(100)
-print(recipe)
+print(f'{recipe}')
+
+meters.checkpoint('recipe-create', context)
 
 decomp = arbor.partition_load_balance(recipe, context)
-print(decomp)
+print(f'{decomp}')
+
+meters.checkpoint('load-balance', context)
 
 sim = arbor.simulation(recipe, decomp, context)
-print(sim)
+print(f'{sim}')
+
+meters.checkpoint('simulation-init', context)
 
 recorder = arbor.attach_spike_recorder(sim)
 
 sim.run(1000)
 
+meters.checkpoint('simulation-run', context)
+
+print(f'{arbor.meter_report(meters, context)}')
+
 for s in recorder.spikes:
     print(s)
diff --git a/python/profiler.cpp b/python/profiler.cpp
new file mode 100644
index 00000000..b44a58c4
--- /dev/null
+++ b/python/profiler.cpp
@@ -0,0 +1,57 @@
+#include <sstream>
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include <arbor/profile/meter_manager.hpp>
+
+#include "context.hpp"
+#include "strprintf.hpp"
+
+namespace pyarb {
+
+void register_profiler(pybind11::module& m) {
+    using namespace pybind11::literals;
+
+    // meter manager
+    pybind11::class_<arb::profile::meter_manager> meter_manager(m, "meter_manager",
+        "Manage metering by setting checkpoints and starting the timing region.");
+    meter_manager
+        .def(pybind11::init<>())
+        .def("start",
+            [](arb::profile::meter_manager& manager, const context_shim& ctx){
+                manager.start(ctx.context);
+            },
+            "context"_a,
+            "Start the metering. Records a time stamp,\
+             that marks the start of the first checkpoint timing region.")
+        .def("checkpoint",
+            [](arb::profile::meter_manager& manager, std::string name, const context_shim& ctx){
+                manager.checkpoint(name, ctx.context);
+            },
+            "name"_a, "context"_a,
+            "Create a new checkpoint. Records the time since the last checkpoint\
+             (or the call to start if no previous checkpoints),\
+             and restarts the timer for the next checkpoint.")
+        .def_property_readonly("checkpoint_names", &arb::profile::meter_manager::checkpoint_names,
+            "A list of all metering checkpoint names.")
+        .def_property_readonly("times", &arb::profile::meter_manager::times,
+            "A list of all metering times.")
+        .def("__str__",  [](const arb::profile::meter_manager&){return "<arbor.meter_manager>";})
+        .def("__repr__", [](const arb::profile::meter_manager&){return "<arbor.meter_manager>";});
+
+    // meter report
+    pybind11::class_<arb::profile::meter_report> meter_report(m, "meter_report",
+        "Summarises the performance meter results, used to print a report to screen or file.\n"
+        "If a distributed context is used, the report will contain a summary of results from all MPI ranks.");
+    meter_report
+        .def(pybind11::init<>(
+        [](const arb::profile::meter_manager& manager, const context_shim& ctx){
+            return arb::profile::make_meter_report(manager, ctx.context);
+            }),
+            "manager"_a, "context"_a)
+        .def("__str__",  [](arb::profile::meter_report& r){return util::pprintf("{}", r);})
+        .def("__repr__", [](arb::profile::meter_report& r){return "<arbor.meter_report>";});
+}
+
+} // namespace pyarb
diff --git a/python/pyarb.cpp b/python/pyarb.cpp
index c369c2ab..7f5bdd71 100644
--- a/python/pyarb.cpp
+++ b/python/pyarb.cpp
@@ -12,6 +12,7 @@ void register_contexts(pybind11::module& m);
 void register_domain_decomposition(pybind11::module& m);
 void register_event_generators(pybind11::module& m);
 void register_identifiers(pybind11::module& m);
+void register_profiler(pybind11::module& m);
 void register_recipe(pybind11::module& m);
 void register_schedules(pybind11::module& m);
 void register_simulation(pybind11::module& m);
@@ -32,11 +33,13 @@ PYBIND11_MODULE(arbor, m) {
     pyarb::register_domain_decomposition(m);
     pyarb::register_event_generators(m);
     pyarb::register_identifiers(m);
-    #ifdef ARB_MPI_ENABLED
-    pyarb::register_mpi(m);
-    #endif
+    pyarb::register_profiler(m);
     pyarb::register_recipe(m);
     pyarb::register_schedules(m);
     pyarb::register_simulation(m);
     pyarb::register_spike_handling(m);
+    
+    #ifdef ARB_MPI_ENABLED
+    pyarb::register_mpi(m);
+    #endif
 }
-- 
GitLab