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