Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Macros
Zakero_Profiler.h File Reference

Zakero Profiler. More...

Go to the source code of this file.

Macros

#define ZAKERO_PROFILER_INIT(output_)
 Initialize the profiler. More...
 
#define ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_)
 Initialize the profiler. More...
 
#define ZAKERO_PROFILER_ACTIVATE
 Activate Profiling. More...
 
#define ZAKERO_PROFILER_DEACTIVATE
 Deactivate Profiling. More...
 
#define ZAKERO_PROFILER_DURATION(category_, name_)
 Generate profiler data. More...
 
#define ZAKERO_PROFILER_INSTANT(category_, name_)
 Generate profiler data. More...
 
#define ZAKERO_PROFILER_ENABLE
 Enable the profiler. More...
 
#define ZAKERO_PROFILER_IMPLEMENTATION
 Activate the implementation code. More...
 

Detailed Description

TL;DR:

This library will generate profiling data while your application is running.
To use, add the implementation to a source code file:

#define ZAKERO_PROFILER_IMPLEMENTATION
#include "path/to/Zakero_Profiler.h"

Next, add the library to the source code where it is "always enabled"

#define ZAKERO_PROFILER_ENABLE
#include "path/to/Zakero_Profiler.h"

Or. enable the profiler at compile time. Only include the library in the source code:

#include "path/to/Zakero_Profiler.h"

Then use a compiler flag when building:

-DZAKERO_PROFILER_ENABLE

What Is It?

Profiling a program helps you determine how much time is being used by different sections of code. This information can be used to determine where code optimizations will have the most pay-off or help locate an area that does not perform consistently.

If your program is crashing or has some other catastrophic bug, profiling will not help you.

The Zakero Profiler is an "internal" profiler that can be added to your C++ code. An "internal" profiler has a few advantages over "external" profilers, such as "gprof". These advantages include:

  • You have full control of what is profiled: Add profiling where it matters.
  • Profiling as it happens, instead of runtime snapshots which can miss details.
  • This implementation has Zero-Cost if not enabled.

The disadvantage of "internal" profilers is code-clutter. You must litter your code with profiler stuff which can make code harder to read. This implementation is no different, but it does try to keep the mess to a minimum by using a few macros.

Why Use It?

The first benefit is the generated profiler output is JSON formatted. There are many JSON parsers available allowing you to parse and use the data easily. Which leads into the main benefit...

The main benefit of using the Zakero Profiler is that you can visualize the profiler data in Google Chrome and Chromium. No need to write your own visualization code.

And the final reason to use the Zakero Profiler is that it is a "Single Header Library". Just include the header file where ever you need or want to add profiling.

How To Use It?

Step 0

Your compiler must support at least the C++20 standard.

Step 1

The first step is to select which C++ source code file will contain the Zakero Profiler implementation. Once the location has been determined, add the following to that file:

#define ZAKERO_PROFILER_IMPLEMENTATION
#include "path/to/Zakero_Profiler.h"

The macro ZAKERO_PROFILER_IMPLEMENTATION tells the header file to include the implementation of the profiler.

In all other files that will use the Zakero Profiler, they need to include the header. In addition to including the header, the profiler must be enabled by defining the ZAKERO_PROFILER_ENABLE macro.

The easiest way to do this is to always have the Zakero_Profiler enabled:

#define ZAKERO_PROFILER_ENABLE
#include "path/to/Zakero_Profiler.h"

This, however, is not very realistic. A better solution is to configure your build system to define ZAKERO_PROFILER_ENABLE when needed and add the Zakero Profiler header and macros where desired.

#include "path/to/Zakero_Profiler.h"

And if your "build system" is the command-line, the following will turn on profiling:

> g++ ... -DZAKERO_PROFILER_ENABLE ...

If the ZAKERO_PROFILER_ENABLE macro is not defined, all the other marcos in this library will be disabled. With compiler optimizations turned on, -O2, the unused profiler code should be removed from the resulting executable.

Step 2

Next, is to initialize the Zakero Profiler. Somewhere in your code, before any profiling is done (such as in main()) add one of the initializer macros (ZAKERO_PROFILER_INIT(), ZAKERO_PROFILER_INIT_METADATA()).

The following example will have the Zakero Profiler write to a file and include information about the application.

zakero::Profiler::MetaData meta_data =
{ { "application", "MyApp" }
, { "version", "1.0.0" }
, { "intent", "=Locate Allocation Delays=" }
};
ZAKERO_PROFILER_INIT_METADATA("MyApp.profiler_json", meta_data);

Step 3

All the prep work is done, now it is time to generate some profiling data.
To do this use one of these macros:

The ZAKERO_PROFILER_DURATION() macro is best used at the start of a code block. Remember you do not have to put this macro after every '{', only add the macros where you need to capture data.

For example:

void func()
{
ZAKERO_PROFILER_DURATION("cache availability", "linear defrag")
do_stuff();
if(!done)
{
ZAKERO_PROFILER_DURATION("cache availability", "extra
defrag-ing")
do_more_stuff();
}
}

Step 4

The program ran and you have profile data. It's time to look at it. Fire up one of the following browsers and...

Google Chrome

Open a new tab and enter chrome://tracing for the URL.

Chromium

Open a new tab enter about:tracing for the URL.

Next, drag the Zakero Profiler output file into the tab to see your data.

Version
0.8.2
  • Bug fixes
0.8.1
  • Bug fixes
  • Macro name changes
0.8.0
  • Profile time duration in C++ code blocks
  • Add "instant" markers to the timeline
Author
Andrew "Zakero" Moore
  • Original Author
Todo:

Add meta data to ZAKERO_PROFILER_DURATION

Add meta data to ZAKERO_PROFILER_INSTANT

Add support for std::filesystem

Add error handling

Look into converting Duration to use a "Complete" event (phase = 'X').

Macro Definition Documentation

◆ ZAKERO_PROFILER_INIT

#define ZAKERO_PROFILER_INIT (   output_)

The profiler must be initialized before any of the other macros are used.

The output_ can be a file name or a std::ostream object. If a std::ostream object is used, the profiler will use a pointer to that stream. This means the provided output_ stream must remain valid for the life of the profiler.

However, if a file name is given, then the profiler will maintain the life-span of the file. Behavior is undefined (i.e. expect a crash) if the provided file name is not writable.

Example
// --- or --- //
ZAKERO_PROFILER_INIT("profile.log");
Note
The Zakero Profiler does not flush after writing data to the output using the stream operator (<<). The reason for this is to allow the operating system to decide the best time to write the data, reducing the number of I/O requests the profiler makes. As a result of this, profiling data will be incomplete if the application does not cleanly exit.
See also
ZAKERO_PROFILER_INIT_METADATA()
Parameters
output_Where to stream the profile data.

◆ ZAKERO_PROFILER_INIT_METADATA

#define ZAKERO_PROFILER_INIT_METADATA (   output_,
  meta_data_ 
)

The profiler must be initialized before any of the other macros are used.

The output_ can be a file name or a std::ostream object. If a std::ostream object is used, the profiler will use a pointer to that stream. This means the provided output_ stream must remain valid for the life of the profiler.

However, if a file name is given, then the profiler will maintain the life-span of the file. Behavior is undefined (i.e. expect a crash) if the provided file name is not writable.

Any extra data that should appear in the profiler output can be added using the meta_data_. meta_data_ is a map of std::string/std::string key/value pairs. The "date" key will be automatically added if not already present. The following keys will be over written if used:

  • displayTimeUnit
  • traceEvents
Example
zakero::Profiler::MetaData meta_data =
{ { "application", "MyApp" }
, { "version", "1.2.3" }
};
ZAKERO_PROFILER_INIT_METADATA("MyApp.profile_json", meta_data);
Note
The Zakero Profiler does not flush after writing data to the output using the stream operator (<<). The reason for this is to allow the operating system to decide the best time to write the data, reducing the number of I/O requests the profiler makes. As a result of this, profiling data will be incomplete if the application does not cleanly exit.
See also
ZAKERO_PROFILER_INIT()
Parameters
output_Where to stream the profile data
meta_data_Extra data

◆ ZAKERO_PROFILER_ACTIVATE

#define ZAKERO_PROFILER_ACTIVATE

This macro will reactivate the generation of profiling data.

The use of ZAKERO_PROFILER_ACTIVATE and ZAKERO_PROFILER_DEACTIVATE macros allows the control of when profiling happens. Instead of having profiling data being generated for the entire runtime of an application, an event can be used to call these macros so that only the profiling data of the area of interest is generated.

If Zakero Profiler has been enabled at compile-time, by defining the ZAKERO_PROFILER_ENABLE macro, then profiling is automatically active. To have the profiler manually/programmatically activated, call ZAKERO_PROFILER_DEACTIVATE immediately after initialization.

Example
int main()
{
ZAKERO_PROFILER_INIT("profiler.json")
eventLoop();
return 0;
}
void onEsc()
{
stopEventLoop();
}
void onF12()
{
}
void onShiftF12()
{
}
See also
ZAKERO_PROFILER_DEACTIVATE

◆ ZAKERO_PROFILER_DEACTIVATE

#define ZAKERO_PROFILER_DEACTIVATE

The generation of profiling data can be temporarily stopped with this macro.

See also
ZAKERO_PROFILER_ACTIVATE

◆ ZAKERO_PROFILER_DURATION

#define ZAKERO_PROFILER_DURATION (   category_,
  name_ 
)

This macro will generate a "duration" profiler event, meaning that the time when the macro was executed is recorded and the time when current code block goes out-of-scope is also recorded.

The category_ can be used to group together related name_ data. For example, profiling a C++ class could use the class name for the category and the name_ would be the part of the class being profiled.

Note
The file name and function name are automatically recorded.
Example
void func()
{
ZAKERO_PROFILER_DURATION("busy", "doing stuff")
// Doing stuff
for(int i = 0; i < max_i; i++)
{
ZAKERO_PROFILER_DURATION("busy", "gone loopy")
// Doing more stuff
if(i % magic)
{
ZAKERO_PROFILER_INSTANT("busy", "shhh, it's magic")
// Doing magic stuff
}
}
}
Parameters
category_The category of the data
name_The name of the data

◆ ZAKERO_PROFILER_INSTANT

#define ZAKERO_PROFILER_INSTANT (   category_,
  name_ 
)

This macro will generate an "instant" event. These events are useful to mark something in the timeline.

The category_ and name_ serve the same function as in ZAKERO_PROFILER_DURATION().

Parameters
category_The category of the data
name_The name of the data

◆ ZAKERO_PROFILER_ENABLE

#define ZAKERO_PROFILER_ENABLE

The Zakero Profiler is a macro based system. When this macro is defined, the macros will be replaced with code. If this macro is not defined, the macros will be removed at compile time.

Note
It does not matter if the macro is given a value or not, only its existence is checked.

◆ ZAKERO_PROFILER_IMPLEMENTATION

#define ZAKERO_PROFILER_IMPLEMENTATION

Defining this macro will cause the Zakero Profiler implementation to be included. This should only be done once, since compiler and/or linker errors will typically be generated if more than a single implementation is found.

Note
It does not matter if the macro is given a value or not, only its existence is checked.
ZAKERO_PROFILER_ACTIVATE
#define ZAKERO_PROFILER_ACTIVATE
Activate Profiling.
Definition: Zakero_Profiler.h:359
ZAKERO_PROFILER_DEACTIVATE
#define ZAKERO_PROFILER_DEACTIVATE
Deactivate Profiling.
Definition: Zakero_Profiler.h:369
ZAKERO_PROFILER_INIT
#define ZAKERO_PROFILER_INIT(output_)
Initialize the profiler.
Definition: Zakero_Profiler.h:265
ZAKERO_PROFILER_INSTANT
#define ZAKERO_PROFILER_INSTANT(category_, name_)
Generate profiler data.
Definition: Zakero_Profiler.h:482
ZAKERO_PROFILER_DURATION
#define ZAKERO_PROFILER_DURATION(category_, name_)
Generate profiler data.
Definition: Zakero_Profiler.h:463
ZAKERO_PROFILER_INIT_METADATA
#define ZAKERO_PROFILER_INIT_METADATA(output_, meta_data_)
Initialize the profiler.
Definition: Zakero_Profiler.h:310