qsort vs. std::sort

When you have an array with some data structure that you would like to sort, you are looking for a sorting algorithm in the standard library.

In C, you have the qsort function which has a pretty cumbersome declaration:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

So you have to pass a pointer to your container, pass the number of elements in the container, pass the size of each element and a comparator function.

Bjarne Stroustrup asked the questions which seem natural when you are used to C++ in a talk about C++11:

  • Why doesn't the container know its size?
  • Why doesn't the container know the size of its elements?
  • Why don't the elements know their order, the way to compare them to each other?

Additionally, the void * in the comparator function mean that type safety cannot be enforced by the compiler, which is bad.

Since the function is passed as a pointer, and the elements are passed as pointers as well, it means that each comparison costs three indirections and a function call. In C++, the std::sort is a template algorithm, so that it can be compiled once for each type. The operator< of the type is usually baked into the sort algorithm as well (inlining), reducing the cost of the comparison significantly.

As Stroustrup pointed out in his talk, people sometimes think that qsort must be faster, since it is C, which is supposed to be the fastest language you can get. The fact that the declaration of qsort is cumbersome tells those people that it is even faster.

Test programs

I want to sort a container of simple floating points numbers. That way, the actual comparison is a really fast operation and the overhead will be seen dramatically.

In C, this is implemented using a dynamic array, a comparison function and the qsort function. I only show the relevant parts here, you can find the full code for this example on GitHub.

int compare(void const *this, void const *other) {
    double const a = *((const double *)this);
    double const b = *((const double *)other);
    if (a < b)
        return -1;
    else if (a == b)
        return 0;
    else
        return 1;
}

The sorting is then done using the qsort function:

qsort(data, size, sizeof(*data), compare);

I have generated files with random numbers in them such that the sorting would have some actual work to do.

You can see that the information regarding the container and its contents are split across several blocks. You have the size, the data and the compare function.

In C++, I use that there exists an operator< for int and the std::sort function. So we only need to actually use it:

std::sort(std::begin(data), std::end(data));

I find that the C++ version is very clear on intent. This is a thing where I prefer C++ over C.

This is compiled with a sufficiently new standard as well as full optimization:

-Wall -Wpedantic -O3 -march=native

Results

The sorting is repeated from 10 numbers to 10 million numbers. The C and C++ programs translated with the GCC and LLVM compilers scale like the following:

It is very curious to see that for small lengths, the GCC versions are faster then the LLVM versions; the language makes the smaller difference! For more items, the C++ version is faster with both compilers.

Taking the appropriate ratios, one can compare GCC with LLVM and also C with C++ here:

For small data sets, one cannot really say anything, the statistical fluctuations are just too large. For large data sets, there is not much of a difference between GCC and LLVM. There is, however, a difference of a factor 1.9 between C and C++, the C version is slower than the C++ version.

Conclusion

The main point is that the C++ version is way faster than the C version, although the C++ version is more elegant. With C++, you can express your intentions more clearly, helping the compiler to optimize your program. Trust your optimizer!