SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
Profiler Class Reference

Utility for profiling functions/scopes and writing the results to a file. More...

#include <Profiler.h>

Public Member Functions

void beginSession (std::string filePath)
 
std::string filePath ()
 
void endSession ()
 
void recordResult (ProfilingResult result)
 
void profileThread (const std::string &name)
 

Static Public Member Functions

static Profilerinstance ()
 

Static Private Member Functions

static void writeString (const char *s, std::ofstream &stream)
 Writes the length (32-bit) and the string (non-null-terminated) itself to the file stream. More...
 

Private Attributes

std::string _filePath
 Future path of the trace file. More...
 
uint64_t _sessionStart = 0
 Start timestamp of the session in microseconds. More...
 
std::vector< ProfilingResult_results
 List of profiling results (of all threads) More...
 
std::vector< std::string > _threadNames
 List of thread names (the thread ID is the index) More...
 
std::mutex _mutex
 Mutex for accessing profiling results and thread names. More...
 

Detailed Description

Utility for profiling functions/scopes and writing the results to a file.

To start the profiling, call BEGIN_PROFILING_SESSION(filePath) with the path to the trace file. After that you can place "PROFILE_FUNCTION();" or "PROFILE_SCOPE(name);" at the start of every function or scope you want to measure. The profiler supports multithreading. To add a new thread, call "PROFILE_THREAD(name)" at the start of the thread. Threads with the same name will appear merged in the trace file. To end the session and write the result to the trace file, call END_PROFILING_SESSION().

The resulting trace gets written into the data folder of SLProject and can be opened using the trace viewer located at /externals/trace-viewer/trace-viewer.jar. Note that a Java Runtime Environment is required to launch this JAR archive.

Definition at line 67 of file Profiler.h.

Member Function Documentation

◆ beginSession()

void Profiler::beginSession ( std::string  filePath)

Starts a profiling session by saving the session start timestamp so it can later be subtracted from the individual result timestamps to get the time points relative to the start of the session.

Parameters
filePathThe path where the trace file should be written to

Definition at line 25 of file Profiler.cpp.

26 {
27  _filePath = std::move(filePath);
28 
29  auto startPoint = std::chrono::high_resolution_clock::now();
30  _sessionStart = std::chrono::time_point_cast<std::chrono::microseconds>(startPoint).time_since_epoch().count();
31 }
std::string _filePath
Future path of the trace file.
Definition: Profiler.h:87
uint64_t _sessionStart
Start timestamp of the session in microseconds.
Definition: Profiler.h:88
std::string filePath()
Definition: Profiler.h:77

◆ endSession()

void Profiler::endSession ( )

Ends the profiling session and writes the result to a trace file. A trace file (.slt) has the following layout: Number of scopes: int32 Scope 1 name: (length: int32, name: non-null-terminated char array) Scope 2 name: (length: int32, name: non-null-terminated char array) ... Number of threads: int32 Thread 1 name: (length: int32, name: non-null-terminated char array) Number of scopes entered in thread 1: int32 Scope 1 in thread 1 (name index: int32, depth: int32, start time: int64, end time: int64) Scope 2 in thread 1 (name index: int32, depth: int32, start time: int64, end time: int64) ... Thread 2 name: (length: int32, name: non-null-terminated char array) Number of scopes entered in thread 2: int32 Scope 1 in thread 2 (name index: int32, depth: int32, start time: int64, end time: int64) Scope 2 in thread 2 (name index: int32, depth: int32, start time: int64, end time: int64) ... ...

All data is written in the big-endian format because that's the endianness that Java uses to read the data later on in the trace viewer. This means that the function has to check the endianness of the system and convert all integers to big endian if we're on a little-endian system.

Definition at line 57 of file Profiler.cpp.

58 {
59  std::ofstream fileStream(_filePath, std::ios::binary);
60 
61  ////////////////////////////////////////
62  // Collect scope names and thread IDs //
63  ////////////////////////////////////////
64 
65  std::vector<const char*> scopeNames;
66  std::vector<uint32_t> threadIds;
67 
68  for (ProfilingResult& result : _results)
69  {
70  if (std::find(scopeNames.begin(), scopeNames.end(), result.name) == scopeNames.end())
71  scopeNames.push_back(result.name);
72 
73  if (std::find(threadIds.begin(), threadIds.end(), result.threadId) == threadIds.end())
74  threadIds.push_back(result.threadId);
75  }
76 
77  /////////////////////////
78  // Write scope section //
79  /////////////////////////
80 
81  // Write the number of scope names
82  auto numScopeNames = (uint32_t)scopeNames.size();
83  ByteOrder::writeBigEndian32(numScopeNames, fileStream);
84 
85  // Write each scope name
86  for (const char* scopeName : scopeNames)
87  {
88  writeString(scopeName, fileStream);
89  }
90 
91  /////////////////////////
92  // Write trace section //
93  /////////////////////////
94 
95  // Write number of threads
96  auto numThreads = (uint32_t)threadIds.size();
97  ByteOrder::writeBigEndian32(numThreads, fileStream);
98 
99  for (uint32_t threadId : threadIds)
100  {
101  // Write thread name
102  writeString(_threadNames[threadId].c_str(), fileStream);
103 
104  // Count and write number of scopes in thread
105  uint32_t numScopes = 0;
106  for (ProfilingResult& result : _results)
107  {
108  if (result.threadId == threadId) numScopes++;
109  }
110  ByteOrder::writeBigEndian32(numScopes, fileStream);
111 
112  // Write results of thread
113  for (ProfilingResult& result : _results)
114  {
115  if (result.threadId != threadId) continue;
116 
117  auto nameIndex = (uint32_t)(std::find(scopeNames.begin(), scopeNames.end(), result.name) - scopeNames.begin());
118  auto depth = result.depth;
119  auto start = result.start - _sessionStart;
120  auto end = result.end - _sessionStart;
121 
122  ByteOrder::writeBigEndian32(nameIndex, fileStream);
123  ByteOrder::writeBigEndian32(depth, fileStream);
124  ByteOrder::writeBigEndian64(start, fileStream);
125  ByteOrder::writeBigEndian64(end, fileStream);
126  }
127  }
128 }
std::vector< std::string > _threadNames
List of thread names (the thread ID is the index)
Definition: Profiler.h:90
std::vector< ProfilingResult > _results
List of profiling results (of all threads)
Definition: Profiler.h:89
static void writeString(const char *s, std::ofstream &stream)
Writes the length (32-bit) and the string (non-null-terminated) itself to the file stream.
Definition: Profiler.cpp:170
void writeBigEndian32(uint32_t number, std::ostream &stream)
Definition: ByteOrder.cpp:118
void writeBigEndian64(uint64_t number, std::ostream &stream)
Definition: ByteOrder.cpp:129

◆ filePath()

std::string Profiler::filePath ( )
inline

Definition at line 77 of file Profiler.h.

77 { return _filePath; }

◆ instance()

static Profiler& Profiler::instance ( )
inlinestatic

Definition at line 70 of file Profiler.h.

71  {
72  static Profiler instance;
73  return instance;
74  }
Utility for profiling functions/scopes and writing the results to a file.
Definition: Profiler.h:68
static Profiler & instance()
Definition: Profiler.h:70

◆ profileThread()

void Profiler::profileThread ( const std::string &  name)

Associates the thread in which the function was called with the name provided. This function must be called at the start of every profiled thread. It is sensibly also thread-safe.

Parameters
name

Definition at line 148 of file Profiler.cpp.

149 {
150  _mutex.lock();
151 
152  for (uint32_t i = 0; i < _threadNames.size(); i++)
153  {
154  if (_threadNames[i] == name)
155  {
157  _mutex.unlock();
158  return;
159  }
160  }
161 
162  uint32_t threadId = (uint32_t)_threadNames.size();
163  _threadNames.push_back(name);
164  ProfilerTimer::threadId = threadId;
165 
166  _mutex.unlock();
167 }
std::mutex _mutex
Mutex for accessing profiling results and thread names.
Definition: Profiler.h:91
static thread_local uint32_t threadId
Definition: Profiler.h:111

◆ recordResult()

void Profiler::recordResult ( ProfilingResult  result)

Stores a result thread-safely in a vector so it can be written to a trace file at the end of the session.

Parameters
result

Definition at line 135 of file Profiler.cpp.

136 {
137  _mutex.lock();
138  _results.push_back(result);
139  _mutex.unlock();
140 }

◆ writeString()

void Profiler::writeString ( const char *  s,
std::ofstream &  stream 
)
staticprivate

Writes the length (32-bit) and the string (non-null-terminated) itself to the file stream.

Definition at line 170 of file Profiler.cpp.

171 {
172  ByteOrder::writeBigEndian32((uint32_t)std::strlen(s), stream);
173  stream << s;
174 }
SLScene * s
Definition: SLScene.h:31
The SLScene class represents the top level instance holding the scene structure.
Definition: SLScene.h:47

Member Data Documentation

◆ _filePath

std::string Profiler::_filePath
private

Future path of the trace file.

Definition at line 87 of file Profiler.h.

◆ _mutex

std::mutex Profiler::_mutex
private

Mutex for accessing profiling results and thread names.

Definition at line 91 of file Profiler.h.

◆ _results

std::vector<ProfilingResult> Profiler::_results
private

List of profiling results (of all threads)

Definition at line 89 of file Profiler.h.

◆ _sessionStart

uint64_t Profiler::_sessionStart = 0
private

Start timestamp of the session in microseconds.

Definition at line 88 of file Profiler.h.

◆ _threadNames

std::vector<std::string> Profiler::_threadNames
private

List of thread names (the thread ID is the index)

Definition at line 90 of file Profiler.h.


The documentation for this class was generated from the following files: