#include <sstream>
#include <tuple>
+#include <nntrainer_error.h>
#include <nntrainer_log.h>
#include <profiler.h>
if (iter == time_taken.end() ||
std::get<GenericProfileListener::CNT>(iter->second) == 0) {
std::stringstream ss;
- ss << "event has never recorded" << event_to_str(event);
+ ss << "event has never recorded" << profiler->eventToStr(event);
throw std::invalid_argument("event has never recorded");
}
}
void GenericProfileListener::report(std::ostream &out) const {
- const std::vector<unsigned int> column_size = {10, 23, 23, 23};
+ std::vector<unsigned int> column_size = {10, 23, 23, 23};
+
+ for (auto &entry : time_taken) {
+ auto title = profiler->eventToStr(entry.first);
+ column_size[0] =
+ std::max(column_size[0], static_cast<unsigned int>(title.length()));
+ }
auto total_col_size =
std::accumulate(column_size.begin(), column_size.end(), 0);
auto &cnt_ = std::get<GenericProfileListener::CNT>(entry.second);
auto &min_ = std::get<GenericProfileListener::MIN>(entry.second);
auto &max_ = std::get<GenericProfileListener::MAX>(entry.second);
- auto &sum_ = std::get<GenericProfileListener::SUM>(time_iter->second);
+ auto &sum_ = std::get<GenericProfileListener::SUM>(entry.second);
+
+ auto title = profiler->eventToStr(entry.first);
if (warmups >= cnt_) {
- out << std::left << std::setw(total_col_size) << event_to_str(entry.first)
+ out << std::left << std::setw(total_col_size) << title
<< "less data then warmup\n";
continue;
}
// clang-format off
- out << std::setw(column_size[0]) << event_to_str(entry.first)
+ out << std::setw(column_size[0]) << title
<< std::setw(column_size[1]) << sum_.count() / (cnt_ - warmups)
<< std::setw(column_size[2]) << min_.count()
<< std::setw(column_size[3]) << max_.count() << '\n';
return instance;
}
-std::string event_to_str(const int event) {
- switch (event) {
- case EVENT::NN_FORWARD:
- return "nn_forward";
- case EVENT::TEMP:
- return "temp";
- }
-
- std::stringstream ss;
- ss << "undef(" << event << ')';
- return ss.str();
-}
-
void Profiler::start(const int &event) {
#ifdef DEBUG
/// @todo: consider race condition
}
}
+std::string Profiler::eventToStr(const int event) {
+
+ /// reserved event
+ if (event >= 0) {
+ switch (event) {
+ case EVENT::NN_FORWARD:
+ return "nn_forward";
+ }
+ }
+ /// custom events
+ else if (event < 0) {
+ std::lock_guard<std::mutex> lk(event_registration_mutex);
+ int actual_idx = -event + 1;
+
+ if (actual_idx > static_cast<int>(custom_events.size()) - 1) {
+ std::stringstream ss;
+ ss << "undef(" << event << ')';
+ return ss.str();
+ }
+ return custom_events[actual_idx];
+ }
+
+ throw std::logic_error("control should not reach here");
+}
+
+int Profiler::registerEvent(const std::string &name) {
+ std::lock_guard<std::mutex> lk(event_registration_mutex);
+ custom_events.push_back(name);
+
+ return -custom_events.size();
+}
+
} // namespace profile
} // namespace nntrainer
#define START_PROFILE(event_key)
#define END_PROFILE(event_key)
+#define REGISTER_EVENT(event_key, ret)
#else
prof.end(event_key); \
} while (0);
+#define REGISTER_EVENT(event_str, ret) \
+ do { \
+ auto &prof = nntrainer::profile::Profiler::Global(); \
+ ret = prof.registerEvent(event_str); \
+ } while (0);
+
#endif /** PROFILE */
#include <chrono>
namespace profile {
typedef enum {
- NN_FORWARD = 0 /**< Neuralnet single inference without loss calculation */,
- TEMP = 999 /**< Temporary event */
+ NN_FORWARD = 1 /**< Neuralnet single inference without loss calculation */,
} EVENT;
-/**
- * @brief get string representation of event
- *
- * @return std::string name
- */
-std::string event_to_str(const int event);
-
class Profiler;
/**
* @brief Generic profile listener class to attach to a profiler,
*/
virtual void report(std::ostream &out) const = 0;
-private:
+protected:
Profiler *profiler;
};
*/
void unsubscribe(ProfileListener *listener);
+ /**
+ * @brief registerEvent to record.
+ * @note Call to the function shouldn't be inside a critical path
+ *
+ * @return int return NEGATIVE integer to distinguish from reserved events
+ */
+ int registerEvent(const std::string &name);
+
+ /**
+ * @brief get string representation of an event
+ *
+ * @return std::string name
+ */
+ std::string eventToStr(const int event);
+
private:
/**
* @brief notify the result
std::unordered_map<int, std::chrono::time_point<std::chrono::steady_clock>>
start_time; /**< start_time of the clock */
+ std::vector<std::string>
+ custom_events; /**< integer indexed custom events. Actual key is negative to
+ avoid clashing */
+
+ std::mutex event_registration_mutex; /**< protect custom event registration */
+
std::mutex subscription_mutex; /**< protect sub/unsub routine to
gaurantee invarient */
};
void reset(const int event) override { hit = false; }
- void report(std::ostream &out) const override {
+ virtual void report(std::ostream &out) const override {
out << (hit ? "hit" : "no hit");
}
/// measure that are not registered for event listener
all_listener->reset(EVENT::NN_FORWARD);
event_listener->reset(EVENT::NN_FORWARD);
- prof.start(EVENT::TEMP);
- prof.end(EVENT::TEMP);
+ prof.start(-1);
+ prof.end(-1);
/// Check if notified on event profiler does not hit
{
EXPECT_EQ(ss.str(), "no hit");
}
+ /// register a event key to profiler and check event to str
+ {
+ int key = prof.registerEvent("hello_world");
+ EXPECT_LT(key, 0);
+ EXPECT_EQ(prof.eventToStr(key), "hello_world");
+ }
+
/// unsubscribe event_listener
event_listener->reset(EVENT::NN_FORWARD);
prof.unsubscribe(event_listener.get());