Audio DSP (Digital Signal Processing) can be a resource intensive task, especially when working in constrained environments such as embedded systems. At Chirp, we ensure that our core software (written in C) runs as efficiently as possible on each supported platform. This can involve integrating specialised DSP modules designed specifically for each hardware target, such as CMSIS and Neon. Alongside this, we use profilers to assess the performance of code, identify bottlenecks and sometimes even bugs.
Instruments is a profiling tool designed by Apple to provide a friendly graphical user interface for DTrace. It can give you useful information on how much time each of your functions are taking to run. Instruments comes bundled with Xcode command line tools, which can be installed with
The Instruments app will be installed in the following location, so head over to this directory and open up the app.
Once opened you should select the Time Profiler.
Then choose your target application to profile, and hit the record button.
Note: before profiling your code, you must ensure that you have debug symbols enabled. This can be achieved by adding the
-g flag to your compilation process, for example
gcc -Wall -g -O0 main.c -o application
Compiler optimisation should be disabled with
-O0, to prevent functions being unlined, loops being unrolled, and other optimisations. Without this, you may notice some missing symbols and inaccurate results.
So, now we have our application running inside Instruments, we can start to identify some of the areas of code which are taking the most time to run. Functions with the heaviest stack trace will appear in the right hand column. By clicking one of these functions in the side panel, you will notice the function gain focus in the main stack tree window.
The two columns displaying ‘Weight’ and ‘Self Weight’ will give you an idea of the performance of this particular function. The ‘Weight’ parameter denotes the cumulative time taken running this function, and a percentage of time this function takes to run relative to the whole application. The data in the ‘Weight’ column contains statistics for all of the child functions, i.e., the functions that are called by this function, whereas ‘Self Weight’ is just the time spent in this function.
We used Instruments when we began integrating an internal research project in to the Chirp core. When inspecting the new piece of code, it was clear that the most expensive operation was using memcpy to shift a buffer along when new data is processed.
You can see below the memmove taking 113ms, significantly more than the other functions.
By replacing this moving buffer with a circular buffer, we were able to cut the function run time down by half.
With the optimised version of the code, we can just move the pointer along to the most recent data in the circular buffer, as opposed to performing the computationally expensive task of shifting large buffers along in memory.
Instruments is a hidden gem when developing in macOS, so be sure to make use of it when developing C programs to produce the most efficient code you can. You may find some surprising results!