Skip to content

Usage: A coding tutorial

Nils Brinkmann edited this page May 7, 2014 · 3 revisions

This tutorial should show how you can use CxxProf in your code. If you want to learn how you can integrate CxxProf into your project in general, see our Integration guide for more information.

In this tutorial we're using a made up example code as base and enhance it step by step with CxxProf macros. There also is a usage example available with cxxprof_howto. It is rather longer than our example here, but shows the features in more variety and depth.

##The base program So let's begin with a very simple application:

    #include <iostream>

    void printSomething(unsigned int numberOfPrints)
    {
        for(unsigned int index = 0; index < numberOfPrints; ++index)
        {
            std::cout << "Hello World #" << index << std::endl;
        }
    }

    void someLongFunction()
    {
        std::cout << "Some long function, what a ressource hogger!" << std::endl;
    }

    int main()
    {
        printSomething(1);
        printSomething(5);
        someLongFunction();
        
        return 0;
    }

This application does not do much, it contains two functions which in reality could take a lot of ressources and time. For simplicity's sake they're just printing something onto the screen. The printSomething allows the user to control how many times something should be printed.

##Add and initialize CxxProf To add CxxProf we just need to include the cxxprof_static/CxxProf.h header. It defines the macros which we'll use to do all the magic.

We should also initialize CxxProf, this is done by calling the CXXPROF_INIT macro. This macro should be called at the very first opportunity, as it takes some time to load the dynamic plugin and get everything up and running. You do not want to let the initialization of CxxProf sabotage your measurements.

In order to ease up the analysis of our recorded data we can define our own process and thread aliases. This is done by calling CXXPROF_PROCESS_ALIAS and CXXPROF_THREAD_ALIAS. We do not have to call them, CxxProf will find names for them automatically. But often it's easier to have our own names for those things. Calling these macros will change the alias of the scope they're called from. It is important to call them before you start measuring anything, so I'd suggest to use them directly after the initialization.

After everything is initialized our code looks like the following:

    #include <iostream>
    #include <cxxprof_static/CxxProf.h>

    void printSomething(unsigned int numberOfPrints)
    {
        for(unsigned int index = 0; index < numberOfPrints; ++index)
        {
            std::cout << "Hello World #" << index << std::endl;
        }
    }

    void someLongFunction()
    {
        std::cout << "Some long function, what a ressource hogger!" << std::endl;
    }

    int main()
    {
        CXXPROF_INIT();
        CXXPROF_PROCESS_ALIAS("TutorialApp");
        CXXPROF_THREAD_ALIAS("MainThread");
        
        printSomething(1);
        printSomething(5);
        someLongFunction();
        
        return 0;
    }

##Activities Activities are the heart of CxxProf. They measure the time it takes until their scope is left. So if you're adding one Activity to a function it measures how long it takes until the function is done. You can also use any other scope or create scopes by yourself.

When we add Activities to our sample code it looks like the following:

    #include <iostream>
    #include <cxxprof_static/CxxProf.h>

    void printSomething(unsigned int numberOfPrints)
    {
        CXXPROF_ACTIVITY(__FUNCTION__);
        for(unsigned int index = 0; index < numberOfPrints; ++index)
        {
            std::cout << "Hello World #" << index << std::endl;
        }
    }

    void someLongFunction()
    {
        std::cout << "Some long function, what a ressource hogger!" << std::endl;
    }

    int main()
    {
        CXXPROF_INIT();
        CXXPROF_PROCESS_ALIAS("TutorialApp");
        CXXPROF_THREAD_ALIAS("MainThread");
        
        CXXPROF_ACTIVITY("This is the main function");
        printSomething(1);
        printSomething(5);
        
        { //let's create our own scope here
            CXXPROF_ACTIVITY("someLongFunction");
            someLongFunction();
        }
        
        return 0;
    }

We added 3 Activities:

  • CXXPROF_ACTIVITY("This is the main function"); will measure how long the entire main takes
  • CXXPROF_ACTIVITY(__FUNCTION__); in printSomething just takes the __FUNCTION__ keyword, which means that the compiler will later fill in the correct function name by itself. It measures how long it takes to execute the printSomething function
  • The CXXPROF_ACTIVITY("someLongFunction"); got its own scope around the call to someLongFunction. By doing this we can measure how long the function takes without changing it internally. This is useful if we want to measure Thirdparty calls where we don't have the rights to change sources.

Note: It is also possible to create conditional Activities by using CXXPROF_ACTIVITY_COND(NAME, COND). You can put any bool expression as COND. These can be useful to limit the amount of data that is generated and just profile the important parts of your application.

Example:

CXXPROF_ACTIVITY_COND("MyActivity", units.size() > 10);

##Marks and Plots By adding Marks and Plots to your code, you add context that later helps you analyze the recorded data.

Marks define specific points during your application where the state changes. This could perhaps mean that your initialization is finished and your application starts running its main task. Or it could mean that something bad happened and you need to switch to an alternative execution path.

Plots show how values changed over time. It's for example useful to track the number of entities you're dealing with in a game engine to see how certain Activities change their execution time. So if your updating function messes up as soon as you have more than 50 entities, you can easily see this.

Here is the new code:

    #include <iostream>
    #include <cxxprof_static/CxxProf.h>

    void printSomething(unsigned int numberOfPrints)
    {
        CXXPROF_PLOT("numberOfPrints", numberOfPrints);
        CXXPROF_ACTIVITY(__FUNCTION__);
        for(unsigned int index = 0; index < numberOfPrints; ++index)
        {
            std::cout << "Hello World #" << index << std::endl;
        }
    }

    void someLongFunction()
    {
        std::cout << "Some long function, what a ressource hogger!" << std::endl;
    }

    int main()
    {
        CXXPROF_INIT();
        CXXPROF_PROCESS_ALIAS("TutorialApp");
        CXXPROF_THREAD_ALIAS("MainThread");
        
        CXXPROF_ACTIVITY("This is the main function");
        CXXPROF_MARK("PrintSomething tests");
        printSomething(1);
        printSomething(5);
        
        CXXPROF_MARK("someLongFunction tests");
        { //let's create our own scope here
            CXXPROF_ACTIVITY("someLongFunction");
            someLongFunction();
        }
        
        return 0;
    }

Two Marks have been added before the different parts of our tests. There is also 1 Plot added in the printSomething method. It later will show how many times something should be printed, there we'll see why the second call will take 5 times longer than the first.

Note: As with Activities it is possible to define Marks and Plots with a Condition. It helps handling the amount of data being generated:

CXXPROF_MARK_COND("MyMark", isDataReceived);
CXXPROF_PLOT_COND("PlayerCounter", session.isLive() && playercount() > 2);

##Clean Shutdown To ensure that everything shuts down gracefully, we have to call CXXPROF_SHUTDOWN(); at the very end of our application. This cancels all Activities that are still running and sends the data to the network. The final version looks like the following:

    #include <iostream>
    #include <cxxprof_static/CxxProf.h>

    void printSomething(unsigned int numberOfPrints)
    {
        CXXPROF_PLOT("numberOfPrints", numberOfPrints);
        CXXPROF_ACTIVITY(__FUNCTION__);
        for(unsigned int index = 0; index < numberOfPrints; ++index)
        {
            std::cout << "Hello World #" << index << std::endl;
        }
    }

    void someLongFunction()
    {
        std::cout << "Some long function, what a ressource hogger!" << std::endl;
    }

    int main()
    {
        CXXPROF_INIT();
        CXXPROF_PROCESS_ALIAS("TutorialApp");
        CXXPROF_THREAD_ALIAS("MainThread");
        
        CXXPROF_ACTIVITY("This is the main function");
        CXXPROF_MARK("PrintSomething tests");
        printSomething(1);
        printSomething(5);
        
        CXXPROF_MARK("someLongFunction tests");
        { //let's create our own scope here
            CXXPROF_ACTIVITY("someLongFunction");
            someLongFunction();
        }
        
        CXXPROF_SHUTDOWN();
        
        return 0;
    }

##Results The final tutorial application is in the source repository. It will compile alongside the other components if you call build.py and provides a start-script to easily get it running.

Here is how the output with Chromes trace-viewer looks like: Turorial_Output Link to image

You can get the data in SQLite and JSON format to play around with it.

##Removing CxxProf Keep in mind that in order to remove CxxProf from your code you do not need to do something more than undefine USECXXPROF from your project. This will void the macros and your compiler removes them automatically.

##What's next?

If there is something unclear to you or you have some interesting data give us notice. Feel free to open issues for everything you find useful.

Clone this wiki locally