manager.setGradientMemoryOptimization(opt);
}
-/// @todo Make a more common class have this
-/// Maybe appcontext can have this?
-#ifdef PROFILE
- class Profiler {
- public:
- // Designated key.
- enum { FORWARD = 0 };
-
- /**
- * @brief query profile result
- *
- * @return std::chrono::time_point<std::chrono::steady_clock> profile result
- */
- std::chrono::milliseconds result(const int &key) { return time_taken[key]; }
-
- /**
- * @brief start profile
- *
- * @param key to record the profile result. Either designated key from enum
- * or arbitrary key can be used
- */
- void start(const int &key) {
- /// @todo: check if key is being reused with time_taken
- start_time[key] = std::chrono::steady_clock::now();
- }
-
- /**
- * @brief end profile
- *
- * @param key to record the profile result. Either designated key from enum
- * or arbitrary key can be used
- */
- void end(const int &key) {
- auto end = std::chrono::steady_clock::now();
- auto iter = start_time.find(key);
-
- if (iter == start_time.end()) {
- throw std::invalid_argument("profiler hasn't started with the key");
- }
-
- time_taken[key] = std::chrono::duration_cast<std::chrono::milliseconds>(
- end - iter->second);
- }
-
- private:
- std::unordered_map<int, std::chrono::time_point<std::chrono::steady_clock>>
- start_time; /**< start_time of the clock */
- std::unordered_map<int, std::chrono::milliseconds> time_taken;
- };
-
-public:
- /**
- * @brief Get the Profile Result
- *
- * @param key key to recorder the profile
- * @return std::chrono::time_point<std::chrono::steady_clock>
- */
- std::chrono::milliseconds getProfileResult(const int &key) {
- return profiler.result(key);
- }
-
-private:
- Profiler profiler;
-#endif
-
private:
/**
* @brief Print Options when printing layer info
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2020 Jihoon Lee <jhoon.it.lee@samsung.com>
+ *
+ * @file profiler.cpp
+ * @date 09 December 2020
+ * @brief Profiler related codes to be used to benchmark things
+ * @see https://github.com/nnstreamer/nntrainer
+ * @author Jihoon Lee <jhoon.it.lee@samsung.com>
+ * @bug No known bugs except for NYI items
+ *
+ */
+#include <sstream>
+
+#include <profiler.h>
+
+namespace nntrainer {
+namespace profile {
+
+Profiler &Profiler::Global() {
+ static Profiler instance;
+ return instance;
+}
+
+std::string event_to_str(const EVENT event) {
+ switch (event) {
+ case EVENT::NN_FORWARD:
+ return "nn_forward";
+ case EVENT::TEMP:
+ return "temp";
+ }
+
+ std::stringstream ss;
+ ss << "undefined key - " << event;
+ return ss.str();
+}
+
+void Profiler::start(const EVENT &event) {
+ /// @todo: consider race condition
+ auto iter = start_time.find(event);
+
+ if (iter != start_time.end()) {
+ throw std::invalid_argument("profiler has already started");
+ }
+
+ start_time[event] = std::chrono::steady_clock::now();
+}
+
+void Profiler::end(const EVENT &event) {
+ /// @todo: consider race condition
+ auto end = std::chrono::steady_clock::now();
+ auto iter = start_time.find(event);
+
+ if (iter == start_time.end()) {
+ throw std::invalid_argument("profiler hasn't started with the event");
+ }
+
+ auto duration =
+ std::chrono::duration_cast<std::chrono::milliseconds>(end - iter->second);
+ notify(event, duration);
+
+ start_time.erase(iter);
+}
+
+void Profiler::notify(const EVENT &event,
+ const std::chrono::milliseconds &value) {
+ for (auto listener : all_event_listeners) {
+ listener->onNotify(event, value);
+ }
+ auto items = event_listeners[event];
+
+ for (auto listner : items) {
+ listner->onNotify(event, value);
+ }
+}
+
+void Profiler::subscribe(ProfileListener *listener,
+ const std::vector<EVENT> &events) {
+
+ if (listener == nullptr) {
+ throw std::invalid_argument("listener is null!");
+ }
+
+ if (events.empty()) {
+ all_event_listeners.push_back(listener);
+ return;
+ }
+
+ for (auto event : events) {
+ auto iter = event_listeners.find(event);
+ if (iter == event_listeners.end()) {
+ event_listeners[event] = std::vector<ProfileListener *>{};
+ }
+ event_listeners[event].push_back(listener);
+ }
+}
+
+} // namespace profile
+
+} // namespace nntrainer
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2020 Jihoon Lee <jhoon.it.lee@samsung.com>
+ *
+ * @file profiler.h
+ * @date 09 December 2020
+ * @brief Profiler related codes to be used to benchmark things
+ * @see https://github.com/nnstreamer/nntrainer
+ * @author Jihoon Lee <jhoon.it.lee@samsung.com>
+ * @bug No known bugs except for NYI items
+ *
+ */
+
+#ifndef __PROFILER_H__
+#define __PROFILER_H__
+
+#ifndef PROFILE
+
+#define PROFILE_START(event_key)
+#define PROFILE_END(event_key)
+
+#else
+
+#define PROFILE_START(event_key) \
+ do { \
+ auto &prof = nntrainer::profile::Profiler::Global(); \
+ prof.start(event_key); \
+ } while (0);
+
+#define PROFILE_END(event_key) \
+ do { \
+ auto &prof = nntrainer::profile::Profiler::Global(); \
+ prof.end(event_key); \
+ } while (0);
+
+#endif /** PROFILE */
+
+#include <chrono>
+#include <iostream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace nntrainer {
+namespace profile {
+
+typedef enum {
+ NN_FORWARD = 0 /**< Neuralnet single inference without loss calculation */,
+ TEMP = 999 /**< Temporary event */
+} EVENT;
+
+/**
+ * @brief get string representation of event
+ *
+ * @return std::string name
+ */
+std::string event_to_str(const EVENT event);
+
+/**
+ * @brief Generic profile listener class to attach to a profiler,
+ * this can be inherited to create a custom profile listener
+ */
+class ProfileListener {
+public:
+ /**
+ * @brief Destroy the Base Profile Listener object
+ *
+ */
+ virtual ~ProfileListener() = default;
+
+ /**
+ * @brief A callback function to be called from a profiler
+ *
+ * @param event event key to store the result
+ * @param value time value from the profiler
+ */
+ virtual void onNotify(const EVENT event,
+ const std::chrono::milliseconds &value) = 0;
+
+ /**
+ * @brief resets the listener to the inital state for a particular key
+ *
+ * @param event event which profiler should notice
+ */
+ virtual void reset(const EVENT event) = 0;
+
+ /**
+ * @brief get the latest result of a event
+ *
+ * @param event event to query the result
+ * @return const std::chrono::milliseconds
+ */
+ virtual const std::chrono::milliseconds result(const EVENT event) = 0;
+
+ /**
+ * @brief report the result
+ *
+ * @param out outstream object to make a report
+ */
+ virtual void report(std::ostream &out) const = 0;
+};
+
+class GenericProfileListener : public ProfileListener {
+public:
+ /**
+ * @brief Destroy the Generic Profile Listener object
+ *
+ */
+ virtual ~GenericProfileListener() = default;
+
+ /**
+ * @copydoc ProfileListener::onNotify(const int event, const
+ * std::chrono::milliseconds &value)
+ */
+ virtual void onNotify(const int event,
+ const std::chrono::milliseconds &value);
+
+ /**
+ * @copydoc ProfileListener::reset(const int event)
+ */
+ virtual void reset(const int event);
+
+ /**
+ * @copydoc ProfileListener::result(const int event)
+ */
+ virtual const std::chrono::milliseconds result(const int event);
+
+ /**
+ * @copydoc ProfileListener::report(std::ostream &out)
+ */
+ virtual void report(std::ostream &out) const;
+
+private:
+ std::unordered_map<int, std::chrono::milliseconds> time_taken;
+};
+
+/**
+ * @brief Overriding output stream for layers and it's derived class
+ */
+template <typename T,
+ typename std::enable_if_t<std::is_base_of<ProfileListener, T>::value,
+ T> * = nullptr>
+std::ostream &operator<<(std::ostream &out, T &l) {
+ l.report(out);
+ return out;
+}
+
+class Profiler {
+public:
+ Profiler() {}
+
+ Profiler(const Profiler &) = delete;
+
+ Profiler &operator=(const Profiler &) = delete;
+
+ /**
+ *
+ * @brief Get Global app context.
+ *
+ * @return AppContext&
+ */
+ static Profiler &Global();
+
+ /**
+ * @brief start profile
+ *
+ * @param key to record the profile result. Either designated key from enum
+ * or arbitrary key can be used
+ */
+ void start(const EVENT &key);
+
+ /**
+ * @brief end profile and notify to the listeners
+ *
+ * @param key to record the profile result. Either designated key from enum
+ * or arbitrary key can be used
+ */
+ void end(const EVENT &key);
+
+ /**
+ * @brief subscribe a listner to the profiler
+ *
+ * @param listener listener to register, listener must outlive lifetime of
+ * profiler
+ * @param events event listeners are subscribing, if empty listener subscribes
+ * to all events
+ */
+ void subscribe(ProfileListener *listener,
+ const std::vector<EVENT> &events = {});
+
+private:
+ /**
+ * @brief notify the result
+ *
+ * @param event event to notify
+ * @param value measured value from the profiler
+ */
+ void notify(const EVENT &event, const std::chrono::milliseconds &value);
+
+ std::vector<ProfileListener *>
+ all_event_listeners; /**< listeners subscribed to all events */
+
+ std::unordered_map<EVENT, std::vector<ProfileListener *>>
+ event_listeners; /**< listeners for an events */
+
+ std::unordered_map<EVENT, std::chrono::time_point<std::chrono::steady_clock>>
+ start_time; /**< start_time of the clock */
+};
+
+} // namespace profile
+} // namespace nntrainer
+
+#endif /** __PROFILER_H__ */