There are a number of things that you could do to make your program run faster. On my machine, all of them together shaved off a little over 8% of the time of your original. Here's what I did:
Use unsigned
where applicable
Your histogram counts can never be less than zero, so it makes sense to use unsigned
instead of int
for their type. This often allows a small but measurable performance increase when doing calculations or comparisons, depending on the particulars of your machine.
Prefer preincrement over postincrement
On my machine it made no difference with this code, but it's often the case that preincrement (++i
) is slightly faster than postincrement (i++
) because the postincrement case has to save the original value and then increment while the preincrement can simply save in place. In some cases (especially if the architecture is register-starved), this results in a performance difference.
Precalculate loop invariants
The value canvasHeight / maxBinValue
doesn't need to be recalculated every loop iteration because the value of it doesn't change within the loop. This kind of calculation is therefore known as a loop invariant and can be precalculated outside the loop for a small but measurable performance increase.
Eliminate unnecessary operations
Your code sets the initial maxBinValue = 0
and then changes it to 1 if the value is 0 after the loop. Why not just start with the value of 1? That way no post-loop adjustment is required.
Use pointers rather than indexing
Pointer arithmetic is often faster than indexing, so rather than having a loop construct for (int index = 0; index < BINS; index++)
it is often faster instead to have something like for (unsigned *bin = histogramBins; bin != lastbin; ++bin)
Measure and verify
All of this is only of theoretical interest until you actually measure the performance on your machine. Your computer, operating system, and compiler are all likely different from mine, so what improves performance on my machine could actually slow things down on yours. The only way to know for sure is to measure.
I did so using the time
command on my Linux machine and found that your original code took an average of 850ms for the program below, while the final optimized version took an average of 779ms. This is the test harness I used:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* test code goes here */
int main()
{
const unsigned height=480;
const unsigned width=640;
unsigned char in[height*width];
unsigned hist[BINS];
for (unsigned i=0; i < height*width; ++i)
in[i] = rand();
for (int i=0; i < 1000; ++i)
calculateHistogram(in, width, height, 1024, hist);
}
This is the optimized version of your code. Note that BINS
is moved outside the function so that it may also be used by the calling code.
const int BINS = 256;
void calculateHistogram(const unsigned char* yuv420sp, const int yuvWidth,
const int yuvHeight, const int canvasHeight, unsigned* histogramBins)
{
// Clear the output
memset(histogramBins, 0, BINS * sizeof(unsigned));
// Get the bin values
const unsigned char *last = &yuv420sp[yuvWidth*yuvHeight];
for ( ; yuv420sp != last; ++yuv420sp)
++histogramBins[*yuv420sp];
// Resolve the maximum count in bins
unsigned maxBinValue = 1;
const unsigned *lastbin = &histogramBins[BINS];
for (unsigned *bin = histogramBins; bin != lastbin; ++bin)
if (*bin > maxBinValue)
maxBinValue = *bin;
// Normalize to fit into the histogram UI canvas height
unsigned scale = canvasHeight / maxBinValue;
for (unsigned *bin = histogramBins; bin != lastbin; ++bin)
*bin *= scale;
}