ArmNN
 20.08
ProfilingService.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "ProfilingService.hpp"
7 
8 #include <armnn/BackendId.hpp>
9 #include <armnn/Logging.hpp>
10 #include <common/include/SocketConnectionException.hpp>
11 
12 #include <boost/format.hpp>
13 
14 namespace armnn
15 {
16 
17 namespace profiling
18 {
19 
20 ProfilingGuidGenerator ProfilingService::m_GuidGenerator;
21 
23 {
24  return m_GuidGenerator.NextGuid();
25 }
26 
28 {
29  return m_GuidGenerator.GenerateStaticId(str);
30 }
31 
33 {
34  m_GuidGenerator.Reset();
35 }
36 
38  bool resetProfilingService)
39 {
40  // Update the profiling options
41  m_Options = options;
42  m_TimelineReporting = options.m_TimelineEnabled;
43  m_ConnectionAcknowledgedCommandHandler.setTimelineEnabled(options.m_TimelineEnabled);
44 
45  // Check if the profiling service needs to be reset
46  if (resetProfilingService)
47  {
48  // Reset the profiling service
49  Reset();
50  }
51 }
52 
54 {
55  return m_Options.m_EnableProfiling;
56 }
57 
59  const ExternalProfilingOptions& options,
60  bool resetProfilingService)
61 {
62  ResetExternalProfilingOptions(options, resetProfilingService);
63  ProfilingState currentState = m_StateMachine.GetCurrentState();
64  if (options.m_EnableProfiling)
65  {
66  switch (currentState)
67  {
69  Update(); // should transition to NotConnected
70  Update(); // will either stay in NotConnected because there is no server
71  // or will enter WaitingForAck.
72  currentState = m_StateMachine.GetCurrentState();
73  if (currentState == ProfilingState::WaitingForAck)
74  {
75  Update(); // poke it again to send out the metadata packet
76  }
77  currentState = m_StateMachine.GetCurrentState();
78  return currentState;
80  Update(); // will either stay in NotConnected because there is no server
81  // or will enter WaitingForAck
82  currentState = m_StateMachine.GetCurrentState();
83  if (currentState == ProfilingState::WaitingForAck)
84  {
85  Update(); // poke it again to send out the metadata packet
86  }
87  currentState = m_StateMachine.GetCurrentState();
88  return currentState;
89  default:
90  return currentState;
91  }
92  }
93  else
94  {
95  // Make sure profiling is shutdown
96  switch (currentState)
97  {
100  return currentState;
101  default:
102  Stop();
103  return m_StateMachine.GetCurrentState();
104  }
105  }
106 }
107 
109 {
110  if (!m_Options.m_EnableProfiling)
111  {
112  // Don't run if profiling is disabled
113  return;
114  }
115 
116  ProfilingState currentState = m_StateMachine.GetCurrentState();
117  switch (currentState)
118  {
120 
121  // Initialize the profiling service
122  Initialize();
123 
124  // Move to the next state
126  break;
128  // Stop the command thread (if running)
129  m_CommandHandler.Stop();
130 
131  // Stop the send thread (if running)
132  m_SendThread.Stop(false);
133 
134  // Stop the periodic counter capture thread (if running)
135  m_PeriodicCounterCapture.Stop();
136 
137  // Reset any existing profiling connection
138  m_ProfilingConnection.reset();
139 
140  try
141  {
142  // Setup the profiling connection
143  ARMNN_ASSERT(m_ProfilingConnectionFactory);
144  m_ProfilingConnection = m_ProfilingConnectionFactory->GetProfilingConnection(m_Options);
145  }
146  catch (const Exception& e)
147  {
148  ARMNN_LOG(warning) << "An error has occurred when creating the profiling connection: "
149  << e.what();
150  }
151  catch (const arm::pipe::SocketConnectionException& e)
152  {
153  ARMNN_LOG(warning) << "An error has occurred when creating the profiling connection ["
154  << e.what() << "] on socket [" << e.GetSocketFd() << "].";
155  }
156 
157  // Move to the next state
158  m_StateMachine.TransitionToState(m_ProfilingConnection
159  ? ProfilingState::WaitingForAck // Profiling connection obtained, wait for ack
160  : ProfilingState::NotConnected); // Profiling connection failed, stay in the
161  // "NotConnected" state
162  break;
164  ARMNN_ASSERT(m_ProfilingConnection);
165 
166  // Start the command thread
167  m_CommandHandler.Start(*m_ProfilingConnection);
168 
169  // Start the send thread, while in "WaitingForAck" state it'll send out a "Stream MetaData" packet waiting for
170  // a valid "Connection Acknowledged" packet confirming the connection
171  m_SendThread.Start(*m_ProfilingConnection);
172 
173  // The connection acknowledged command handler will automatically transition the state to "Active" once a
174  // valid "Connection Acknowledged" packet has been received
175 
176  break;
178 
179  // The period counter capture thread is started by the Periodic Counter Selection command handler upon
180  // request by an external profiling service
181 
182  break;
183  default:
184  throw RuntimeException(boost::str(boost::format("Unknown profiling service state: %1")
185  % static_cast<int>(currentState)));
186  }
187 }
188 
190 {
191  ProfilingState currentState = m_StateMachine.GetCurrentState();
192  switch (currentState)
193  {
197  return; // NOP
199  // Stop the command thread (if running)
200  Stop();
201 
202  break;
203  default:
204  throw RuntimeException(boost::str(boost::format("Unknown profiling service state: %1")
205  % static_cast<int>(currentState)));
206  }
207 }
208 
209 // Store a profiling context returned from a backend that support profiling, and register its counters
211  std::shared_ptr<armnn::profiling::IBackendProfilingContext> profilingContext)
212 {
213  ARMNN_ASSERT(profilingContext != nullptr);
214  // Register the backend counters
215  m_MaxGlobalCounterId = profilingContext->RegisterCounters(m_MaxGlobalCounterId);
216  m_BackendProfilingContexts.emplace(backendId, std::move(profilingContext));
217 }
219 {
220  return m_CounterDirectory;
221 }
222 
224 {
225  return m_CounterDirectory;
226 }
227 
229 {
230  return m_StateMachine.GetCurrentState();
231 }
232 
234 {
235  return m_CounterDirectory.GetCounterCount();
236 }
237 
238 bool ProfilingService::IsCounterRegistered(uint16_t counterUid) const
239 {
240  return m_CounterDirectory.IsCounterRegistered(counterUid);
241 }
242 
243 uint32_t ProfilingService::GetAbsoluteCounterValue(uint16_t counterUid) const
244 {
245  CheckCounterUid(counterUid);
246  std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
247  ARMNN_ASSERT(counterValuePtr);
248  return counterValuePtr->load(std::memory_order::memory_order_relaxed);
249 }
250 
251 uint32_t ProfilingService::GetDeltaCounterValue(uint16_t counterUid)
252 {
253  CheckCounterUid(counterUid);
254  std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
255  ARMNN_ASSERT(counterValuePtr);
256  const uint32_t counterValue = counterValuePtr->load(std::memory_order::memory_order_relaxed);
257  SubtractCounterValue(counterUid, counterValue);
258  return counterValue;
259 }
260 
262 {
263  return m_CounterIdMap;
264 }
265 
267 {
268  return m_CounterIdMap;
269 }
270 
272 {
273  return m_Holder.GetCaptureData();
274 }
275 
276 void ProfilingService::SetCaptureData(uint32_t capturePeriod,
277  const std::vector<uint16_t>& counterIds,
278  const std::set<BackendId>& activeBackends)
279 {
280  m_Holder.SetCaptureData(capturePeriod, counterIds, activeBackends);
281 }
282 
283 void ProfilingService::SetCounterValue(uint16_t counterUid, uint32_t value)
284 {
285  CheckCounterUid(counterUid);
286  std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
287  ARMNN_ASSERT(counterValuePtr);
288  counterValuePtr->store(value, std::memory_order::memory_order_relaxed);
289 }
290 
291 uint32_t ProfilingService::AddCounterValue(uint16_t counterUid, uint32_t value)
292 {
293  CheckCounterUid(counterUid);
294  std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
295  ARMNN_ASSERT(counterValuePtr);
296  return counterValuePtr->fetch_add(value, std::memory_order::memory_order_relaxed);
297 }
298 
299 uint32_t ProfilingService::SubtractCounterValue(uint16_t counterUid, uint32_t value)
300 {
301  CheckCounterUid(counterUid);
302  std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
303  ARMNN_ASSERT(counterValuePtr);
304  return counterValuePtr->fetch_sub(value, std::memory_order::memory_order_relaxed);
305 }
306 
307 uint32_t ProfilingService::IncrementCounterValue(uint16_t counterUid)
308 {
309  CheckCounterUid(counterUid);
310  std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
311  ARMNN_ASSERT(counterValuePtr);
312  return counterValuePtr->operator++(std::memory_order::memory_order_relaxed);
313 }
314 
316 {
318 }
319 
321 {
322  return ProfilingService::GetStaticId(str);
323 }
324 
325 std::unique_ptr<ISendTimelinePacket> ProfilingService::GetSendTimelinePacket() const
326 {
327  return m_TimelinePacketWriterFactory.GetSendTimelinePacket();
328 }
329 
330 void ProfilingService::Initialize()
331 {
332  // Register a category for the basic runtime counters
333  if (!m_CounterDirectory.IsCategoryRegistered("ArmNN_Runtime"))
334  {
335  m_CounterDirectory.RegisterCategory("ArmNN_Runtime");
336  }
337 
338  // Register a counter for the number of Network loads
339  if (!m_CounterDirectory.IsCounterRegistered("Network loads"))
340  {
341  const Counter* loadedNetworksCounter =
342  m_CounterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
343  armnn::profiling::NETWORK_LOADS,
344  "ArmNN_Runtime",
345  0,
346  0,
347  1.f,
348  "Network loads",
349  "The number of networks loaded at runtime",
350  std::string("networks"));
351  ARMNN_ASSERT(loadedNetworksCounter);
352  InitializeCounterValue(loadedNetworksCounter->m_Uid);
353  }
354  // Register a counter for the number of unloaded networks
355  if (!m_CounterDirectory.IsCounterRegistered("Network unloads"))
356  {
357  const Counter* unloadedNetworksCounter =
358  m_CounterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
359  armnn::profiling::NETWORK_UNLOADS,
360  "ArmNN_Runtime",
361  0,
362  0,
363  1.f,
364  "Network unloads",
365  "The number of networks unloaded at runtime",
366  std::string("networks"));
367  ARMNN_ASSERT(unloadedNetworksCounter);
368  InitializeCounterValue(unloadedNetworksCounter->m_Uid);
369  }
370  // Register a counter for the number of registered backends
371  if (!m_CounterDirectory.IsCounterRegistered("Backends registered"))
372  {
373  const Counter* registeredBackendsCounter =
374  m_CounterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
375  armnn::profiling::REGISTERED_BACKENDS,
376  "ArmNN_Runtime",
377  0,
378  0,
379  1.f,
380  "Backends registered",
381  "The number of registered backends",
382  std::string("backends"));
383  ARMNN_ASSERT(registeredBackendsCounter);
384  InitializeCounterValue(registeredBackendsCounter->m_Uid);
385 
386  // Due to backends being registered before the profiling service becomes active,
387  // we need to set the counter to the correct value here
388  SetCounterValue(armnn::profiling::REGISTERED_BACKENDS, static_cast<uint32_t>(BackendRegistryInstance().Size()));
389  }
390  // Register a counter for the number of registered backends
391  if (!m_CounterDirectory.IsCounterRegistered("Backends unregistered"))
392  {
393  const Counter* unregisteredBackendsCounter =
394  m_CounterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
395  armnn::profiling::UNREGISTERED_BACKENDS,
396  "ArmNN_Runtime",
397  0,
398  0,
399  1.f,
400  "Backends unregistered",
401  "The number of unregistered backends",
402  std::string("backends"));
403  ARMNN_ASSERT(unregisteredBackendsCounter);
404  InitializeCounterValue(unregisteredBackendsCounter->m_Uid);
405  }
406  // Register a counter for the number of inferences run
407  if (!m_CounterDirectory.IsCounterRegistered("Inferences run"))
408  {
409  const Counter* inferencesRunCounter =
410  m_CounterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
411  armnn::profiling::INFERENCES_RUN,
412  "ArmNN_Runtime",
413  0,
414  0,
415  1.f,
416  "Inferences run",
417  "The number of inferences run",
418  std::string("inferences"));
419  ARMNN_ASSERT(inferencesRunCounter);
420  InitializeCounterValue(inferencesRunCounter->m_Uid);
421  }
422 }
423 
424 void ProfilingService::InitializeCounterValue(uint16_t counterUid)
425 {
426  // Increase the size of the counter index if necessary
427  if (counterUid >= m_CounterIndex.size())
428  {
429  m_CounterIndex.resize(boost::numeric_cast<size_t>(counterUid) + 1);
430  }
431 
432  // Create a new atomic counter and add it to the list
433  m_CounterValues.emplace_back(0);
434 
435  // Register the new counter to the counter index for quick access
436  std::atomic<uint32_t>* counterValuePtr = &(m_CounterValues.back());
437  m_CounterIndex.at(counterUid) = counterValuePtr;
438 }
439 
440 void ProfilingService::Reset()
441 {
442  // Stop the profiling service...
443  Stop();
444 
445  // ...then delete all the counter data and configuration...
446  m_CounterIndex.clear();
447  m_CounterValues.clear();
448  m_CounterDirectory.Clear();
449  m_CounterIdMap.Reset();
450  m_BufferManager.Reset();
451 
452  // ...finally reset the profiling state machine
453  m_StateMachine.Reset();
454  m_BackendProfilingContexts.clear();
455  m_MaxGlobalCounterId = armnn::profiling::MAX_ARMNN_COUNTER;
456 }
457 
458 void ProfilingService::Stop()
459 {
460  { // only lock when we are updating the inference completed variable
461  std::unique_lock<std::mutex> lck(m_ServiceActiveMutex);
462  m_ServiceActive = false;
463  }
464  // The order in which we reset/stop the components is not trivial!
465  // First stop the producing threads
466  // Command Handler first as it is responsible for launching then Periodic Counter capture thread
467  m_CommandHandler.Stop();
468  m_PeriodicCounterCapture.Stop();
469  // The the consuming thread
470  m_SendThread.Stop(false);
471 
472  // ...then close and destroy the profiling connection...
473  if (m_ProfilingConnection != nullptr && m_ProfilingConnection->IsOpen())
474  {
475  m_ProfilingConnection->Close();
476  }
477  m_ProfilingConnection.reset();
478 
479  // ...then move to the "NotConnected" state
481 }
482 
483 inline void ProfilingService::CheckCounterUid(uint16_t counterUid) const
484 {
485  if (!IsCounterRegistered(counterUid))
486  {
487  throw InvalidArgumentException(boost::str(boost::format("Counter UID %1% is not registered") % counterUid));
488  }
489 }
490 
492 {
493  BackendProfilingContext::iterator it = m_BackendProfilingContexts.begin();
494  while (it != m_BackendProfilingContexts.end())
495  {
496  auto& backendProfilingContext = it->second;
497  backendProfilingContext->EnableTimelineReporting(m_TimelineReporting);
498  // Increment the Iterator to point to next entry
499  it++;
500  }
501 }
502 
504 {
505  { // only lock when we are updating the inference completed variable
506  std::unique_lock<std::mutex> lck(m_ServiceActiveMutex);
507  m_ServiceActive = true;
508  }
509  m_ServiceActiveConditionVariable.notify_one();
510 }
511 
513 {
514  std::unique_lock<std::mutex> lck(m_ServiceActiveMutex);
515 
516  auto start = std::chrono::high_resolution_clock::now();
517  // Here we we will go back to sleep after a spurious wake up if
518  // m_InferenceCompleted is not yet true.
519  if (!m_ServiceActiveConditionVariable.wait_for(lck,
520  std::chrono::milliseconds(timeout),
521  [&]{return m_ServiceActive == true;}))
522  {
523  if (m_ServiceActive == true)
524  {
525  return;
526  }
527  auto finish = std::chrono::high_resolution_clock::now();
528  std::chrono::duration<double, std::milli> elapsed = finish - start;
529  std::stringstream ss;
530  ss << "Timed out waiting on profiling service activation for " << elapsed.count() << " ms";
531  ARMNN_LOG(warning) << ss.str();
532  }
533  return;
534 }
535 
537 {
538  Stop();
539 }
540 } // namespace profiling
541 
542 } // namespace armnn
bool IsCounterRegistered(uint16_t counterUid) const override
bool IsCategoryRegistered(const std::string &categoryName) const
void WaitForProfilingServiceActivation(unsigned int timeout) override
const Category * RegisterCategory(const std::string &categoryName) override
const Counter * RegisterCounter(const BackendId &backendId, const uint16_t uid, const std::string &parentCategoryName, uint16_t counterClass, uint16_t interpolation, double multiplier, const std::string &name, const std::string &description, const Optional< std::string > &units=EmptyOptional(), const Optional< uint16_t > &numberOfCores=EmptyOptional(), const Optional< uint16_t > &deviceUid=EmptyOptional(), const Optional< uint16_t > &counterSetUid=EmptyOptional()) override
uint32_t GetAbsoluteCounterValue(uint16_t counterUid) const override
ProfilingState GetCurrentState() const
void Start(IProfilingConnection &profilingConnection)
void Reset()
Reset the generator back to zero. Used mainly for test.
Strongly typed guids to distinguish between those generated at runtime, and those that are statically...
Definition: Types.hpp:319
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:163
BackendRegistry & BackendRegistryInstance()
Copyright (c) 2020 ARM Limited.
ProfilingStaticGuid GenerateStaticId(const std::string &str) override
Create a ProfilingStaticGuid based on a hash of the string.
ProfilingDynamicGuid NextGuid() override
Return the next random Guid in the sequence.
uint32_t IncrementCounterValue(uint16_t counterUid) override
uint32_t GetDeltaCounterValue(uint16_t counterUid) override
uint32_t SubtractCounterValue(uint16_t counterUid, uint32_t value) override
static ProfilingStaticGuid GetStaticId(const std::string &str)
CaptureData GetCaptureData() const
Definition: Holder.cpp:54
void SetCaptureData(uint32_t capturePeriod, const std::vector< uint16_t > &counterIds, const std::set< BackendId > &activeBackends)
void SetCaptureData(uint32_t capturePeriod, const std::vector< uint16_t > &counterIds, const std::set< armnn::BackendId > &activeBackends)
Definition: Holder.cpp:74
std::unique_ptr< ISendTimelinePacket > GetSendTimelinePacket() const override
const ICounterMappings & GetCounterMappings() const override
void ResetExternalProfilingOptions(const ExternalProfilingOptions &options, bool resetProfilingService=false)
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
uint32_t AddCounterValue(uint16_t counterUid, uint32_t value) override
ProfilingDynamicGuid NextGuid() override
Return the next random Guid in the sequence.
IRegisterCounterMapping & GetCounterMappingRegistry()
void SetCounterValue(uint16_t counterUid, uint32_t value) override
bool IsProfilingEnabled() const override
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
bool IsCounterRegistered(uint16_t counterUid) const
static ProfilingDynamicGuid GetNextGuid()
uint16_t GetCounterCount() const override
void TransitionToState(ProfilingState newState)
void AddBackendProfilingContext(const BackendId backendId, std::shared_ptr< armnn::profiling::IBackendProfilingContext > profilingContext)
std::unique_ptr< ISendTimelinePacket > GetSendTimelinePacket() const
const ICounterDirectory & GetCounterDirectory() const
ProfilingState ConfigureProfilingService(const ExternalProfilingOptions &options, bool resetProfilingService=false)
void Stop(bool rethrowSendThreadExceptions=true) override
Stop the thread.
Definition: SendThread.cpp:82
uint16_t GetCounterCount() const override
void Start(IProfilingConnection &profilingConnection) override
Start the thread.
Definition: SendThread.cpp:52
ProfilingStaticGuid GenerateStaticId(const std::string &str) override
Create a ProfilingStaticGuid based on a hash of the string.