In 30 years of using C++ this is the first time I've ever come across "translation unit" being abbreviated to TU and it took a bit of effort to figure out what the author was trying to say. Not sure why they felt the need to abbreviate this when they explain PCH for instance, which is a far more commonly used term.
Thought I'd add the context here to help anyone else out.
Compilation speed is a huge part of productivity and enjoying writing C++
I first created the module via:
g++ -std=c++26 -fmodules -freflection -fsearch-include-path -fmodule-only -c bits/std.cc
And then benchmarked with: hyperfine "g++ -std=c++26 -fmodules -freflection ./main.cpp"
The only "include" was import std;, nothing else.These are the results:
- Basic struct reflection: 352.8 ms
- Barry's AoS -> SoA example: 1.077 s
Compare that with PCH:
- Basic struct reflection: 208.7 ms
- Barry's AoS -> SoA example: 1.261 s
So PCH actually wins for just <meta>, and modules are not that much better than PCH for the larger example. Very disappointing.
<meta> is another question, it depends on string_view, vector, and possibly other parts. Maybe it's possible to make it leaner with more selective internal deps.
I was somewhat horrified to discover that the STL ended up with a special role in the language spec. (IIRC, one of the tie-ins is initializer lists.)
IMHO it's far wiser to leave the standard library as something that isn't needed by the core language, and where users can (at least in principle) provide their own alternative implementation without needing compiler hacks.
I.e., those details are inherent in the definition of "library" in the C/C++ world.
I am very excited about C++26 reflection.
I am also obsessed by having my code compile as quickly as possible. Fast compilation times are extremely valuable to keep iteration times low, productivity and motivation high, and to quickly see the impact of your changes. 1
With time and experience, I’ve realized that C++ can be an extremely fast-to-compile language. Language features like templates are not the issue – the Standard Library is.
My fork of SFML uses almost no Standard Library at all, and I can recompile the entire thing from scratch in ~4.3s. That’s around ~900 TUs, including external dependencies, tests, and examples.2 Incremental builds are, for all intents and purposes, instantaneous. I love it.
I would love to live in a world where C++26 reflection is purely a lightweight language feature, however that ship has sailed (thank you, Jonathan Müller, for trying).
In this article, I’ll try to provide some early expectations about the compile-time impact of C++26 reflection.
I found a nice Docker image containing GCC 16, the first version that supports reflection, and got to work. To get reasonably stable measurements, I used the hyperfine command-line benchmarking tool.
These are my specs:
sourcemation/gcc-16)-std=c++26 -freflectionMy test scenarios were as follows:
Baseline test. Just a int main() { }.
Header inclusion test. Same as above, but with #include <meta>.
Basic reflection over a struct’s fields:
template <typename T> void reflect_struct(const T& obj) {
template for (constexpr std::meta::info field :
std::define_static_array(std::meta::nonstatic_data_members_of(
^^T, std::meta::access_context::current()))) {
use(std::meta::identifier_of(field));
use(obj.[:field:]);
}
}
struct User {
std::string_view name;
int age;
bool active;
};
int main() {
reflect_struct(User{.name = "Alice", .age = 30, .active = true});
}
Barry Revzin’s AoS to SoA transformation example, from his blog post.3
I’ve also tested using precompiled headers (PCHs) for <meta> and other large dependencies.
⚠️ DISCLAIMER: please take these benchmark results with a grain of salt. ⚠️
| # | Scenario | Code | Precompiled Header (PCH) | Compile Time (Mean) |
|---|---|---|---|---|
| 1 | Baseline (No Reflection Flag) | int main() only |
None | 43.9 ms |
| 2 | Baseline + -freflection |
int main() only |
None | 43.1 ms |
| 3 | <meta> Header Inclusion |
int main() + #include <meta> |
None | 310.4 ms |
| 4 | Basic Struct Reflection (1 type) | reflect_struct with User |
None | 331.2 ms |
| 5 | Basic Struct Reflection (10 types) | reflect_struct with User<N> |
None | 388.6 ms |
| 6 | Basic Struct Reflection (20 types) | reflect_struct with User<N> |
None | 410.9 ms |
| 7 | AoS to SoA (Original) | Barry Revzin’s Unedited Code | None | 1,622.0 ms |
| 8 | AoS to SoA (No Print) | Removed <print> |
None | 540.1 ms |
| 9 | AoS to SoA (No Print/Ranges) | Removed <print>, <ranges> |
None | 391.4 ms |
| 10 | AoS to SoA (Original + PCH) | Barry Revzin’s Unedited Code | <meta>, <ranges>, <print> |
1,265.0 ms |
| 11 | AoS to SoA (No Print + PCH) | Removed <print> |
<meta>, <ranges> |
229.7 ms |
| 12 | AoS to SoA (No Print/Ranges + PCH) | Removed <print>, <ranges> |
<meta> |
181.9 ms |
A few clarifications:
The number of types in the “Basic Struct Reflection” example was adjusted by instantiating unique “clones” of the test struct type:
template <int>
struct User {
std::string_view name;
int age;
bool active;
};
reflect_struct(User<0>{.name = "Alice", .age = 30, .active = true});
reflect_struct(User<1>{.name = "Alice", .age = 30, .active = true});
reflect_struct(User<2>{.name = "Alice", .age = 30, .active = true});
// ...
“Removed <print>” implies removing all of the formatting/printing code from Barry’s example.
“Removed <ranges>” implies rewriting range-based code like std::views::transform or std::views::iota to good old boomer loops™.
-freflection adds 0 ms of overhead.<meta>).<meta> adds ~149 ms of pure parsing time.<ranges> adds ~440 ms.<print> adds an astronomical ~1,082 ms.<meta> and avoiding heavy dependencies cuts compile time down to 181.9 ms (scenario 12).<meta> + <ranges> drops the time from 540ms to 229ms (scenario 8 vs 11).<print> helps a bit, it still leaves the compile time uncomfortably high.<meta> is not part of import std yet, and even with import std Barry’s example took a whopping 1.346s to compile.I ran some more measurements using import std; with a properly-built std module that includes reflection.
Firstly, I created the module via:
g++ -std=c++26 -fmodules -freflection -fsearch-include-path -fmodule-only -c bits/std.cc
And then benchmarked with:
hyperfine "g++ -std=c++26 -fmodules -freflection ./main.cpp"
No #include was used – only import std.
These are the results (mean compilation time):
Compare that with PCH:
So… PCH actually wins compared to modules for just <meta>, and modules are not that much better than PCH for the larger example. Quite disappointing.
Reflection is going to bring a lot of power to C++26. New libraries that heavily rely on reflection are going to become widespread. Every single TU including one of those libraries will virally include <meta> and any other used dependencies.
Assuming the usage of <meta> + <ranges> becomes widespread, we’re looking at a bare minimum of ~540ms compilation overhead per TU. Using PCHs (or modules) will become pretty much mandatory, especially in large projects.
I really, really wish that Jonathan Müller’s paper (P3429: <meta> should minimize standard library dependencies) was given more thought and support.
I also really wish that a game-changing feature such as reflection wasn’t so closely tied to the Standard Library. The less often I use the Standard Library, the more enjoyable and productive I find C++ as a language – insanely fast compilation times are a large part of that.
Hopefully, as reflection implementations are relatively new, things will only get better from here.
I offer training, mentoring, and consulting services. If you are interested, check out romeo.training, alternatively you can reach out at mail (at) vittorioromeo (dot) com or on Twitter.
Check out my newly-released game on Steam: BubbleByte – it’s only $3.99 and one of those games that you can either play actively as a timewaster or more passively to keep you company in the background while you do something else.
My book “Embracing Modern C++ Safely” is available from all major resellers.
If you enjoy fast-paced open-source arcade games with user-created content, check out Open Hexagon, my VRSFML-powered game available on Steam and on itch.io.
The current bottlenecks are (by far) cpptrace and nlohmann::json, used only in a few TUs. Without those, I could easily get a sub 1s build. I could get even lower with unity builds and perhaps some well-chosen PCHs.↩︎
If you haven’t already, check out his great CppCon 2025 talk: “Practical Reflection With C++26”, and perhaps my “More Speed & Simplicity: Practical Data-Oriented Design in C++” keynote as well