Over the holidays there was a big debate on compilation time in the C++ community. It started with a blog post by Aras Pranckevičius: "Modern" C++ Lamentations
. The post picks on the Standard Ranges
by Eric Niebler, that shows how you would implement Pythagorean Triples with Ranges, a new addition to C++20 standard.
At the heart of Aras' argument was the following two facts:
- the range-based Pythagorean Triples is much longer and more complex than a simple C-style implementation
- compilation time increases from 0.071s to 3.02s (when going from C-style to range-based implementation -- that is 42 times slower
The "modern" C++ tends to be more complex and less usable.
As Sparrow has for some time ranges as a core usability feature, and compilation time is not necessarily a strong point of Sparrow, I feel obliged to state my position on this topic; it's been a bit more than a month since the heat started, so this is a good opportunity to look at the problem calmly.
Here is the summary:
- as always, I tend to find a balance between the two extremes
- the balance is more shifted towards Aras' side
First, I think most of us agree that Eric's example is a bad one. That doesn't mean that there aren't a lot of good examples in which Ranges can help a lot. And yes, the programmer's mind is still the bottleneck, not the compilation time. That's why we are not programming in assembly anymore. Within reasonable limits, we should focus on improving the developer's productivity; that is, as long as the improvements we make are greater compared to the problems we introduce/aggravate.
And, that is exactly what Sparrow aims to do: without sacrificing performance and, to some extent, compilation time, improve the "naturalness" of programming---make it easier for programmers to write quality code and enjoy programming.
So far, in terms of compilation-time, Sparrow is not doing that great. Here are a few drawbacks:
- current compilation times are not great
- Sparrow relies on a lot of generics
- Sparrow relies on hyper-metaprogramming for some of its features
On the first point, things are excusable: Sparrow did not yet reach maturity; very little effort spent in making the compiler faster.
The current work on moving towards value semantics will make things faster to compile. Previously, most of the values were held by reference. As a direct consequence, each time the program needs to access a compile-time value, the compiler would have to reach the compile-time execution engine and dereference a compile-time pointer. Moving towards value semantics, we reduce the need to call the compile-time execution engine.
Using C++-style generics is also a potential problem for compilation-time. On this topic, the long-term goal is to switch to "one compilation" of a generic. That is, the generic is compiled only once, regardless of how many times the generic is actually instantiated. The tricky part is to keep the same code specialization characteristic the C++ has, that makes generics fast. I have some ideas on how to make this possible; describing them here would be beyond the scope of this article.
Finally, there is the problem of hyper-metaprogramming. The more the users will use it, the slower the compilation-time will get. But here, we have another trick that we can apply: external compile-time modules. Once a programmer creates a compile-time functionality, it should be able to instruct the compiler to compile everything to a shared library and make sure that the library is loaded into the compiler. This way, even if the user writes metaprograms in Sparrow, they can be optimized out just like any other compiler module. It's basically extending the compiler from within Sparrow. This is again not yet implemented.
So yes, Sparrow allows one to shoot oneself in the foot but provides means to avoid these types of issues (well, at least that's the intention).
And since we are discussing compilation-time, there is also one thing on Sparrow's roadmap that is worth noticing. Sparrow aims to stay away from the "include" model of C and C++. Instead, it aims at the following behavior:
- the content of a file is compiled only once
- dependencies between files are controlled at a more detailed level
- changing something in a file, only leads to the recompilation of the declarations that actually depend on the change
- if one changes the body of a function, without affecting the interface, no recompilation is needed for other modules
- if one changes the signature of a function, only the declarations that depend on that function would be recompiled
- if one changes the size of a datatype, only the declarations dependent on that size would be recompiled
- linking would, of course, have to be done for every change, just like in C++
With that, I would say the Sparrow has a good plan of making the compile-time problem go away. If all these are implemented, then Sparrow would allow to raise the level of abstraction, make programming easier, without compromising compilation time. All that, of course, if we don't write over-complex code, just for the sake of showing off some features :p.