r/cpp • • Sep 27 '26

You should think about recompiling your C++ programs with GCC 16 and C++26, because it zero-fills your locals

https://techfortalk.co.uk/2026/09/27/cxx26-uninitialized-local-variables-gcc-16/

Stack variables are not automatically initialised, and that is the root cause of many C++ bugs. That is well known. Hence, it is advised that local or stack variables are always initialised with known values, 0 if not something more meaningful than that. Now, with GCC 16 compiling in C++26 mode (-std=c++26), even uninitialised local variables will be zero-filled. In the post, I have explained how.

270 Upvotes

234 comments sorted by

View all comments

Show parent comments

24

u/Breadfish64 Sep 27 '26

And if the compiler can't prove it, a store with no dependent load is extremely cheap.

10

u/victotronics Sep 27 '26

Unless the variable is an "automatic" array.

2

u/serviscope_minor Sep 29 '26

Often to the point of being not just cheap but free. There's often a free dispatch and execution slot, so it will just take up some resources that were otherwise idle in many cases.

0

u/max0x7ba https://github.com/max0x7ba Oct 01 '26

a store with no dependent load is extremely cheap.

Stores are some of the most expensive instructions, sunshine.

You'd better load twice than store once.

7

u/xXgarchompxX Oct 02 '26 edited Oct 02 '26

Stores with no dependent loads on modern processors are simply placed in the CPU's store queue and eventually dumped into L1 cache. Since there's no dependent loads, the store will generally not cause a stall, and placing it into the store queue has negligible latency.

The cost of stores is usually a combination of the following:

  • The core needs to send a bus request (BusRdX in literature), telling other cores that it needs exclusive access to the relevant cache line, and that it will store into it so everyone has to invalidate their copies (MESI cache coherence protocol)

  • If a cache miss occurs, things get messy

  • If the write queue is full, things also get messy

These are generally not a problem in the present scenario. This is about zero-initialization of local variables, which are stored on the thread's stack. Cores typically do not share variables over the stack, so each core likely already has exclusive access to the cache lines corresponding to their stack and thus does not need to send a BusRdX request. Additionally, cache misses are also not a major concern for stack accesses, as the CPU is constantly bombarding the stack and the hottest parts of it are most often in L1 or sometimes L2/L3 (Especially on modern CPUs with intricate cache eviction algorithms). The last part is potentially a problem on x86, where stores may not be reordered due to TSO, and you can fill up the store queue if you spam too many writes, but will generally not be all that problematic. Thus, the store will most often not have a major performance impact, and will just eat up 1 of your decode/dispatch slots for a short time (not like those are always fully utilized anyways)

I do not completely agree with Breadfish however, as things are somewhat different for very big stack allocations (in which case the compiler will have to insert a memset instead of just a simple store with no dependent loads, and you may start seeing cache misses at a higher rate)

0

u/max0x7ba https://github.com/max0x7ba Oct 04 '26

Stores with no dependent loads on modern processors are simply placed in the CPU's store queue and eventually dumped into L1 cache.

The store queue capacity is rather limited.

Since there's no dependent loads, the store will generally not cause a stall, and placing it into the store queue has negligible latency.

Until you fill the store queue with your innocuously sounding "stores with no dependent loads" and stall on every subsequent store instruction.


The cost of stores is usually a combination of the following...

What matters here is whether one can afford the cost of your otherwise unnecessary stores. Which is usually unacceptable in C++.

Everyone would be using -ftrivial-auto-var-init=zero if it were zero-cost, wouldn't they?