thread_stats_manager.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #include "thread_stats_manager.h"
  2. #include "simulator.h"
  3. #include "core_manager.h"
  4. #include "performance_model.h"
  5. #include "thread.h"
  6. #include "stats.h"
  7. #include <cstring>
  8. ThreadStatsManager::ThreadStatsManager()
  9. : m_threads_stats(MAX_THREADS)
  10. , m_thread_stat_types()
  11. , m_thread_stat_callbacks()
  12. , m_next_dynamic_type(DYNAMIC)
  13. , m_bottlegraphs(MAX_THREADS)
  14. , m_waiting_time_last(SubsecondTime::Zero())
  15. {
  16. // Order our hooks to occur before possible reschedulings (which are done with ORDER_ACTION), so the scheduler can use up-to-date information
  17. Sim()->getHooksManager()->registerHook(HookType::HOOK_PRE_STAT_WRITE, hook_pre_stat_write, (UInt64)this, HooksManager::ORDER_NOTIFY_PRE);
  18. Sim()->getHooksManager()->registerHook(HookType::HOOK_THREAD_CREATE, hook_thread_create, (UInt64)this, HooksManager::ORDER_NOTIFY_PRE);
  19. Sim()->getHooksManager()->registerHook(HookType::HOOK_THREAD_START, hook_thread_start, (UInt64)this, HooksManager::ORDER_NOTIFY_PRE);
  20. Sim()->getHooksManager()->registerHook(HookType::HOOK_THREAD_STALL, hook_thread_stall, (UInt64)this, HooksManager::ORDER_NOTIFY_PRE);
  21. Sim()->getHooksManager()->registerHook(HookType::HOOK_THREAD_RESUME, hook_thread_resume, (UInt64)this, HooksManager::ORDER_NOTIFY_PRE);
  22. Sim()->getHooksManager()->registerHook(HookType::HOOK_THREAD_EXIT, hook_thread_exit, (UInt64)this, HooksManager::ORDER_NOTIFY_PRE);
  23. registerThreadStatMetric(INSTRUCTIONS, "instruction_count", metricCallback, 0);
  24. registerThreadStatMetric(ELAPSED_NONIDLE_TIME, "nonidle_elapsed_time", metricCallback, 0);
  25. registerThreadStatMetric(WAITING_COST, "waiting_cost", metricCallback, 0);
  26. }
  27. ThreadStatsManager::~ThreadStatsManager()
  28. {
  29. for(std::vector<ThreadStats*>::iterator it = m_threads_stats.begin(); it != m_threads_stats.end(); ++it)
  30. if (*it)
  31. delete *it;
  32. }
  33. ThreadStatsManager::ThreadStatType ThreadStatsManager::registerThreadStatMetric(ThreadStatType type, const char* name, ThreadStatCallback func, UInt64 user)
  34. {
  35. if (type == DYNAMIC)
  36. {
  37. type = m_next_dynamic_type;
  38. ++m_next_dynamic_type;
  39. }
  40. m_thread_stat_types.push_back(type);
  41. m_thread_stat_callbacks[type] = StatCallback(name, func, user);
  42. return type;
  43. }
  44. UInt64 ThreadStatsManager::callThreadStatCallback(ThreadStatType type, thread_id_t thread_id, Core *core)
  45. {
  46. return m_thread_stat_callbacks[type].call(type, thread_id, core);
  47. }
  48. void ThreadStatsManager::update(thread_id_t thread_id, SubsecondTime time)
  49. {
  50. if (time == SubsecondTime::MaxTime())
  51. time = Sim()->getClockSkewMinimizationServer()->getGlobalTime();
  52. calculateWaitingCosts(time);
  53. if (thread_id == INVALID_THREAD_ID)
  54. {
  55. for(thread_id = 0; thread_id < (thread_id_t)Sim()->getThreadManager()->getNumThreads(); ++thread_id)
  56. if (m_threads_stats[thread_id])
  57. m_threads_stats[thread_id]->update(time);
  58. }
  59. else
  60. {
  61. m_threads_stats[thread_id]->update(time);
  62. }
  63. }
  64. void ThreadStatsManager::calculateWaitingCosts(SubsecondTime time)
  65. {
  66. // Calculate a waiting cost for all threads.
  67. // For fully-subscribed systems, the cost is equal to each thread's waiting time.
  68. // On over-subscribed systems, waiting is Ok as long as there are other threads that can execute,
  69. // so the cost becomes equal to the number of unused core*cycles, which is spread out over all waiting threads.
  70. if (time > m_waiting_time_last)
  71. {
  72. SubsecondTime time_delta = time - m_waiting_time_last;
  73. UInt32 n_running = 0, n_stalled = 0, n_total = Sim()->getConfig()->getApplicationCores();
  74. for(thread_id_t thread_id = 0; thread_id < (thread_id_t)Sim()->getThreadManager()->getNumThreads(); ++thread_id)
  75. {
  76. if (Sim()->getThreadManager()->isThreadRunning(thread_id))
  77. ++n_running;
  78. else if (Sim()->getThreadManager()->getThreadStallReason(thread_id) == ThreadManager::STALL_UNSCHEDULED)
  79. ;
  80. else
  81. ++n_stalled;
  82. }
  83. if (n_stalled && n_running <= n_total)
  84. {
  85. SubsecondTime cost = (n_total - n_running) * time_delta / n_stalled;
  86. for(thread_id_t thread_id = 0; thread_id < (thread_id_t)Sim()->getThreadManager()->getNumThreads(); ++thread_id)
  87. {
  88. if (!Sim()->getThreadManager()->isThreadRunning(thread_id)
  89. && Sim()->getThreadManager()->getThreadStallReason(thread_id) != ThreadManager::STALL_UNSCHEDULED)
  90. {
  91. m_threads_stats[thread_id]->m_counts[WAITING_COST] += cost.getFS();
  92. }
  93. }
  94. }
  95. m_waiting_time_last = time;
  96. }
  97. }
  98. UInt64 ThreadStatsManager::metricCallback(ThreadStatType type, thread_id_t thread_id, Core *core, UInt64 user)
  99. {
  100. switch(type)
  101. {
  102. case INSTRUCTIONS:
  103. return core->getPerformanceModel()->getInstructionCount();
  104. case ELAPSED_NONIDLE_TIME:
  105. return core->getPerformanceModel()->getNonIdleElapsedTime().getFS();
  106. case WAITING_COST:
  107. // Waiting cost is added directly to m_counts[], as it needs to be applied even (especially!) when the thread is not on a core
  108. return 0;
  109. default:
  110. LOG_PRINT_ERROR("Invalid ThreadStatType(%d) for this callback", type);
  111. }
  112. }
  113. void ThreadStatsManager::pre_stat_write()
  114. {
  115. SubsecondTime time = Sim()->getClockSkewMinimizationServer()->getGlobalTime();
  116. calculateWaitingCosts(time);
  117. m_bottlegraphs.update(time, INVALID_THREAD_ID, false);
  118. update();
  119. }
  120. void ThreadStatsManager::threadCreate(thread_id_t thread_id)
  121. {
  122. LOG_ASSERT_ERROR(thread_id < MAX_THREADS, "Too many application threads, increase MAX_THREADS");
  123. m_threads_stats[thread_id] = new ThreadStats(thread_id);
  124. }
  125. void ThreadStatsManager::threadStart(thread_id_t thread_id, SubsecondTime time)
  126. {
  127. m_threads_stats[thread_id]->update(time, true);
  128. m_bottlegraphs.threadStart(thread_id);
  129. m_bottlegraphs.update(time, thread_id, true);
  130. }
  131. void ThreadStatsManager::threadStall(thread_id_t thread_id, ThreadManager::stall_type_t reason, SubsecondTime time)
  132. {
  133. calculateWaitingCosts(time);
  134. m_threads_stats[thread_id]->update(time);
  135. if (reason != ThreadManager::STALL_UNSCHEDULED)
  136. m_bottlegraphs.update(time, thread_id, false);
  137. }
  138. void ThreadStatsManager::threadResume(thread_id_t thread_id, thread_id_t thread_by, SubsecondTime time)
  139. {
  140. calculateWaitingCosts(time);
  141. m_threads_stats[thread_id]->update(time);
  142. m_bottlegraphs.update(time, thread_id, true);
  143. }
  144. void ThreadStatsManager::threadExit(thread_id_t thread_id, SubsecondTime time)
  145. {
  146. calculateWaitingCosts(time);
  147. m_threads_stats[thread_id]->update(time);
  148. m_bottlegraphs.update(time, thread_id, false);
  149. }
  150. ThreadStatsManager::ThreadStats::ThreadStats(thread_id_t thread_id)
  151. : m_thread(Sim()->getThreadManager()->getThreadFromID(thread_id))
  152. , m_core_id(INVALID_CORE_ID)
  153. , time_by_core(Sim()->getConfig()->getApplicationCores())
  154. , insn_by_core(Sim()->getConfig()->getApplicationCores())
  155. , m_elapsed_time(SubsecondTime::Zero())
  156. , m_unscheduled_time(SubsecondTime::Zero())
  157. , m_time_last(SubsecondTime::Zero())
  158. , m_counts()
  159. , m_last()
  160. {
  161. registerStatsMetric("thread", thread_id, "elapsed_time", &m_elapsed_time);
  162. registerStatsMetric("thread", thread_id, "unscheduled_time", &m_unscheduled_time);
  163. for (core_id_t core_id = 0; core_id < (core_id_t)Sim()->getConfig()->getApplicationCores(); core_id++)
  164. {
  165. registerStatsMetric("thread", thread_id, "time_by_core[" + itostr(core_id) + "]", &time_by_core[core_id]);
  166. registerStatsMetric("thread", thread_id, "instructions_by_core[" + itostr(core_id) + "]", &insn_by_core[core_id]);
  167. }
  168. ThreadStatsManager *tsm = Sim()->getThreadStatsManager();
  169. for(std::vector<ThreadStatType>::const_iterator it = tsm->getThreadStatTypes().begin(); it != tsm->getThreadStatTypes().end(); ++it)
  170. {
  171. m_counts[*it] = 0;
  172. m_last[*it] = 0;
  173. registerStatsMetric("thread", thread_id, tsm->getThreadStatName(*it), &m_counts[*it]);
  174. }
  175. }
  176. void ThreadStatsManager::ThreadStats::update(SubsecondTime time, bool init)
  177. {
  178. if (Sim()->getThreadManager()->getThreadState(m_thread->getId()) == Core::IDLE
  179. || Sim()->getThreadManager()->getThreadState(m_thread->getId()) == Core::INITIALIZING)
  180. return;
  181. // Increment per-thread statistics based on the progress our core has made since last time
  182. SubsecondTime time_delta = init || m_time_last > time ? SubsecondTime::Zero() : time - m_time_last;
  183. if (m_core_id == INVALID_CORE_ID)
  184. {
  185. m_elapsed_time += time_delta;
  186. m_unscheduled_time += time_delta;
  187. }
  188. else
  189. {
  190. Core *core = Sim()->getCoreManager()->getCoreFromID(m_core_id);
  191. m_elapsed_time += time_delta;
  192. time_by_core[core->getId()] += core->getPerformanceModel()->getNonIdleElapsedTime().getFS() - m_last[ELAPSED_NONIDLE_TIME];
  193. insn_by_core[core->getId()] += core->getPerformanceModel()->getInstructionCount() - m_last[INSTRUCTIONS];
  194. for(std::unordered_map<ThreadStatType, UInt64>::iterator it = m_counts.begin(); it != m_counts.end(); ++it)
  195. {
  196. m_counts[it->first] += Sim()->getThreadStatsManager()->callThreadStatCallback(it->first, m_thread->getId(), core) - m_last[it->first];
  197. }
  198. }
  199. // Take a snapshot of our current core's statistics for later comparison
  200. Core *core = m_thread->getCore();
  201. if (core)
  202. {
  203. m_core_id = core->getId();
  204. for(std::unordered_map<ThreadStatType, UInt64>::iterator it = m_counts.begin(); it != m_counts.end(); ++it)
  205. {
  206. m_last[it->first] = Sim()->getThreadStatsManager()->callThreadStatCallback(it->first, m_thread->getId(), core);
  207. }
  208. }
  209. else
  210. m_core_id = INVALID_CORE_ID;
  211. if (time > m_time_last)
  212. m_time_last = time;
  213. }
  214. ThreadStatsManager::ThreadStatType ThreadStatNamedStat::registerStat(const char* name, String objectName, String metricName)
  215. {
  216. if (Sim()->getStatsManager()->getMetricObject(objectName, 0, metricName))
  217. {
  218. ThreadStatNamedStat *tsns = new ThreadStatNamedStat(objectName, metricName);
  219. return Sim()->getThreadStatsManager()->registerThreadStatMetric(ThreadStatsManager::DYNAMIC, name, callback, (UInt64)tsns);
  220. }
  221. else
  222. {
  223. return ThreadStatsManager::INVALID;
  224. }
  225. }
  226. ThreadStatNamedStat::ThreadStatNamedStat(String objectName, String metricName)
  227. {
  228. for(core_id_t core_id = 0; core_id < (core_id_t)Sim()->getConfig()->getApplicationCores(); ++core_id)
  229. {
  230. StatsMetricBase *m = Sim()->getStatsManager()->getMetricObject(objectName, core_id, metricName);
  231. m_stats.push_back(m);
  232. }
  233. }
  234. UInt64 ThreadStatNamedStat::callback(ThreadStatsManager::ThreadStatType type, thread_id_t thread_id, Core *core, UInt64 user)
  235. {
  236. StatsMetricBase *m = ((ThreadStatNamedStat*)user)->m_stats[core->getId()];
  237. if (m)
  238. return m->recordMetric();
  239. else
  240. return 0;
  241. }