Look at This Modern Sausage

If you think adopting the latest-and-greatest C++11/14/17 will guarantee your firm the latest technology, please follow me looking at how this sausage is made. We will be looking at gcc-7-20161204 and clang/libc++ 3.9 (stable) sources fresh from last month releases.

std::string::to_string( T )

This one is a crowd favorite. to_string resides in libc++/src/string.cpp. Let’s assume T=int. The implementation calls

and then

Performance warning: clang’s libc++ is running a loop, increasing the size of the string in the expectation that it does not need to allocate more memory.

Same thing with gcc-7/libstdc++-v3

and __to_xstring is defined in include/ext/string_conversions.h as

The big problem here is that gcc/libstdc++ is always allocating memory (exactly __n = 16 bytes) for the biggest string the conversion can yield. So even if you are printing “0” it will always allocate 16 characters. Not counting the time for std::string creation itself.

So BOTH implementations of std::to_string are calling vsnprintf() which has been part of the c library for what, 30 years already? I have to look it up, perhaps more.

The sausage: std::to_string() is just a wrapper, with an expensive hack job inside. Just avoid it.

std::chrono::now()

std::chrono is one of the biggest stars of C++11. It promised to standardize all the clock mechanisms once and for all. Let’s look at the implementation? This is libstdc++-v3/src/c++11/chrono.cc line 53

Surprise! It is calling clock_gettime(), which has been in the libc since SUSv2, POSIX.1-2001. On clang 3.9.0/src/chrono.cpp it looks like a straight copy of it:

Also, I have seen folks using and boosting std::high_resolution_clock as a new feature. A closer look reveals:

What a disappointment.

std::random_device

So what about <random> that allegedly got rid of all the libc rand() calls? Here we see gcc is shamelessly opening /dev/urandom, a Linux kernel facility

On clang 3.9.0/src/random.cpp we see basically the same thing

std::thread

On libstdc++-v3 we can see on include/std/thread:

Then __gthread is defined in gcc-7/libgcc/gthr-posix.h:

Bingo! Here it is C++ leaning on its poor cousin. std::threads is in fact, a gift-wrapped pthreads which was defined in POSIX.1c, Threads extensions (IEEE Std 1003.1c-1995). We know gcc it is using gthr-posix.h because you can see in the command line:

On clang/libc++ the same story

Again std::thread is pthreads, wrapped. Oh you C++, can’t you do anything by yourself?

No need to add, std::shared_mutex, std::shared_timed_mutex, shared_lock also are wrapping pthreads on posix platforms.

std::regex

I got to give this one: both implementations are native C++. Well done.

Coroutines

Let’s talk about something that has not even made into the standard yet? Well coroutines are still a proposal but the C library has had it since 2001 with makecontext/setcontext.

Here I had a surprise: even though there is no mention of coroutines in both the libc++ or the stdlibc++, boost::context DOES have a very comprehensive implementation of the execution stack swap by Oliver Kowalke (see boost/libs/context/src/asm/ontop_xxx) where XXX is the platform eg x86_64_sysv_elf_gas. That is beautiful engineering, the type I would like to see all over the current C++ implementations.

However, I really doubt this will make into the standard the way it is on boost so I still stand correct.

Conclusion

I could go on for hours perusing both implementations of the standard library, even going into supc++ as well, showing that the entire “Modern C++” infrastructure is just wrappers around our old friend POSIX & WIN32 C-library.

Worse, usually these wrappers will impose significant performance penalties to your programs in order to stay consistent with the ISO C++ standard requirements. I strongly encourage you to glance over and have both the gcc/stdlibc++ and the libc++ source trees unzipped somewhere in your hard drive. The gcc/stdlibc++ because it is the de facto standard – it is even used by clang by default. The libc++, because its implementation is really 10x less cluttered than that of GCC (as the clang compiler sources are also much easier to read than gcc itself).

The libstdc++-v3 sources are found inside the GCC source tree, which can be downloaded from https://gcc.gnu.org/releases.html

The libc++ is provided as a module that can be inserted into the LLVM project tree. It is optional. It can be downloaded from http://libcxx.llvm.org/.

I want to show the new standards are all wrappers. Some lazy wrappers, some better implemented. I wanted to destroy the myth some MDs have that adherence to the latest standards is necessary for having the best technology onboard. The latest technology you will find on places like github, boost, the Linux kernel, vendor white papers and most importantly, in software as configurable hardware.

2 thoughts on “Look at This Modern Sausage

  1. Not sure I get it. malloc is a thin wrapper too. So is fopen or any language standard library. However, passing a lamba to a thread or making time units comparison safer is what you get with an update of your cpp compiler. And this is just about the language lib, not the language itself.

    • Sure, I agree. That is exactly the point of the article, to show that the new standards are about to make safer comparisons etc, not create new features, which is a very common misconception, believe me, especially within the management circles.

Leave a Reply

Your email address will not be published. Required fields are marked *

*