Code, 3D, Games, Linux and much more...
I have dedicated the last few days of work to face a problem that is getting bigger and bigger. Although we have a quite modular codebase, use interfaces whenever possible to hide implementation details and apply most of the recipes recommended in classic books, our linking times have started to be a problem in some of our libraries. Linking times is not the only problem. Our binary sizes are becoming really fat.
After a thoroughly study with the help of tools like Sizer, Symbol Sort (I strongly recommend reading the articles associated to this tool: 1, 2, 3, 4, 5, 6 and passing all them to your co-workers) we discovered lot of code bloating generated by improper template usage. The template is a powerful tool that can be easily misused although I am not against using it. We use template metaprogramming in lots of places like reflection, annotations, uri-like dependency properties, serialization and more places where it is worth it. The problem with templates is that normally declarations and definitions go in the same header file. With that structure a compiler is normally forced (although some compilers allow customization) to generate template instances in each obj file. Redundancy is eliminated in the linking phase, of course taking time. This is one of the faces of template bloating. The other face appears when you templatize functions when it is not strictly necessary. And you pay for it in the final size of your binaries.
Trying to solve all this, we applied a solution that although yet not standard seems to work in all compilers we tested: exporting explicit template instantiations. This technique applies when you have a template that is to be instantiated for only a few known types. Those instantiations are exported from a dll. This allows even hiding big functions in the .cpp without having to show them in the header. With this solution you have the best of both worlds: short functions can be inlined by the compiler and large functions instead of being instantiated in each compilation unit are referenced as an exported symbol. By the way, a special behavior of Visual Studio (to me, it is a bug) has the effect of non inlining functions being defined outside the class when exporting the template. We had to move our definitions (that we usually have pseudo hidden in a .inl file) to inside the class declaration.
We applied this strategy in several places reducing the bloatage by a considerable factor. For example, the vector library seems an ideal target because you normally only instantiate that library for a few types (float, double, int) and “big” functions like Invert() for 4×4 matrices can be hidden and exported from the dll. Another good candidate is std::string (instances for chat and wchar_t). Although, at least in MSVC, this job is already being done by the crt (not tested in other platforms).
I hope you find this useful. Thanks for reading and welcome to all the new subscribers since my last post!