Date: Thu, 18 Sep 2025 11:41:31 +0200
On 18/09/2025 08:28, Yongwei Wu via Std-Discussion wrote:
> On Wed, 17 Sept 2025 at 22:59, Nate Eldredge <nate_at_[hidden]
> <mailto:nate_at_[hidden]>> wrote:
>
>
>
>> On Sep 17, 2025, at 00:21, Yongwei Wu <wuyongwei_at_[hidden]
>> <mailto:wuyongwei_at_[hidden]>> wrote:
>>
>> I can hardly imagine it is "common". Who would have written such
>> code, especially when it never terminates? I would argue that an
>> infinite loop is better, in that it would alert the programmer
>> that something is broken. To me (and I believe to most C++
>> programmers), this is a surprising optimization. I really have
>> difficulty imagining it is truly useful.
>
> A very common reason for *humans* to write code to compute a value
> that is then unused: when the code that would use the value has been
> turned off for this build.
>
>
> I'll reply to this post to describe my points.
>
> First, programmers in general do not want to /rely on/ compiler
> optimizations to get rid of unused code. Do not forget it is also
> difficult for compilers to prove something is unused, especially when
> the compiler does not see all code at once.
>
C++ programmers must very much rely on compiler optimisations in order
to get efficient results - including dead code elimination. But often
the dead code in question is not something they have written themselves
- it is part of standard headers or other header code. Templates and
inline functions are regularly full of code that is designed to be used
or eliminated, depending on factors known at compile time (such as
template arguments). When possible, it is good to use things like
"constexpr if" to guarantee compile-time dead-code elimination, but in
other cases, that is not possible.
Yes, identifying dead code can be difficult for compilers - but
top-class compilers are very good at that kind of thing. And yes, it
becomes much more limited if the compiler does not have all the
information it needs. That is why people striving for high efficiency
code (remembering that most code is not performance critical) have
multiple techniques for working with their compilers in order to get the
highest efficiency results. These are, unfortunately, not all entirely
portable - while C23 has "unsequenced" and "reproducible" function
attributes, C++ does not (yet) have these and programmers have to use
compiler-specific features like gcc attributes and link-time optimisation.
> Another thing is unexpected results. It seems to me very often that
> programmers want to measure some calculations. However, when volatile
> variables, locks/atomics, or I/O operations are not involved, the
> calculation /sometimes/ gets eliminated (and sometimes not). It looks to
> me like a burden in education.
If that is a burden in education, then it is a burden educators will
have to bear. C++ should not compromise on efficiency or the
convenience of experienced programmers simply to reduce the learning
effort for newbies. C++ is not a beginners language. I am fully in
favour of trying to avoid surprises and unexpected behaviour in a
language, but the emphasis there has to be on making it harder to make
mistakes for the middle 80% of C++ programmers - not the top 10%
experts, nor the newest and least experienced 10%. And whenever common
compilers and tools can reliably spot problems with static error
checking, that is - IMHO - good enough. There is no benefit in changing
the language to reduce optimisation opportunities or efficiency for good
programmers in order to pander to those that don't care enough about
their code to use basic checking. (Sorry about the slightly off-topic
rant there.)
C and C++ are defined in terms of observable behaviour. Calculations
are not observable. Calculations that do nothing can be eliminated -
and they /will/ be eliminated if the compiler can see that they do
nothing. If you want to benchmark a calculation, you need to understand
how to benchmark calculations - if you are educating programmers, then
you need to teach them how to do this correctly and reliably. It is not
difficult, and is vastly better than making the language poorer for
everyone who is not sitting in a classroom. Start by making sure that
they all understand about observable behaviour. Then if you want to
force the compiler to generate code for a calculation, the answer should
be obvious to the students - make the inputs and outputs of the
calculation observable.
So instead of :
int input = 42;
int start_time = now();
int output = calculate(input);
int end_time = now();
do :
int input = 42;
int start_time = now();
volatile int v_input = input;
volatile int v_output = calculate(v_input);
int end_time = now();
I have seen countless cases of people making a mess of their
benchmarking because they don't understand how this works. Or they
don't understand about dependencies - they think that volatile accesses
force an ordering on calculations or on other accesses. And then they
try benchmarking their code with optimisations disabled - as useful as
testing the top speed of your car in first gear.
The answer here is not a worse language, but better education.
>
> For the sake of programmer friendliness (by the way, UB is /extremely
> programmer-unfriendly/), I would like to see forward progress opt-in,
> instead of the current opposite way. Do not surprise your users
> (programmers here). Do not expect every programmer to read the C++
> standard (just like most humans want to avoid reading user manuals
> wherever possible).
I certainly don't expect C++ programmers in general to read the C++
standards. But I /do/ expect them to be familiar with looking things up
when they are not sure - and to be familiar with resources like
<https://en.cppreference.com>. (Unfortunately, there are also many poor
quality resources out there too.)
And I think programmers should be taught to be /programmers/ before
being taught C++. A key concept in programming is "garbage in, garbage
out" - function pre-conditions and post-conditions have been fundamental
even though C++ has not had contracts until C++26. UB is putting
garbage into your function (or operator, or whatever) and expecting
roses to come out. UB is asking for the square root of a negative
number, or trying to fit 13 eggs into a box sized for 12 eggs. You have
to teach about UB - but you do so by emphasising that when you write
code that calls a function, /you/ are responsible for making sure the
input values are valid. It is not the fault of the language if you get
this wrong. Of course the function specifications or implementation
could be flawed, but then all bets are off. UB is a /good/ thing for
programmers - it is programmer friendly. It is part of making
contracts, specifications and responsibilities clear and well-defined.
(Unexpected UB is bad, but that is typically the result of bad
specifications, bad documentation, bad design or bad explanations - all
of which are, obviously, bad.)
Trying to remove all possibilities of UB in a language is futile and
unhelpful to programmers. Trying to understand UB - where it comes
from, why it exists, and how to avoid hitting it in practice - is the
important thing.
>
> I am in the process of preparing a lecture on UB, and it is really a
> pain to find that there are so many surprises in the C++ language.... I
> concur that UB is unavoidable at present, but I would argue it is better
> to have less UB where possible, especially when making UB defined does
> not bring real harm.
>
There is no doubt that there is a lot of hard stuff in C++. It is not a
beginners' language - it is big, and it has a lot of scary parts. Part
of that is inevitable when making an efficient compiled language. Part
of it is due to historic design decisions. And while it is easy for me
to say "the way to avoid UB is to make C++ education better", I am well
aware that teaching this stuff is far from easy!
Perhaps you can turn this all on its head. Instead of thinking of all
the potential UB situations, put the emphasis on being sure your code
makes sense in the first place. Don't worry about code being UB if it
fails to make progress or has certain kinds of infinite loops - rather,
say that C++ programs should do something useful. Either they do
something useful and terminate, or they keep doing something useful
indefinitely. Don't worry about signed integer arithmetic overflow
being UB - rather, emphasis that it is obvious that you need to use
types that are big enough to handle the numbers you are using.
Encourage people to learn what the language guarantees, and to stick to
that, rather than trying to guess all the unwarranted assumptions or
ways to write nonsensical code. You'll still be left with some cases
where there is surprising UB, but you can eliminate most unsurprising UB
by encouraging people to think about what works, rather than being
concerned about what doesn't work.
> While I agree the optimization like you mentioned may really exist, I
> still can hardly imagine people will /rely/ on it. It is too fragileāa
> single memory allocation can make the optimization impossible at run time.
>
I certainly rely on dead code elimination for some of my programming.
But in my main work (small systems embedded programming), memory
allocation is very tightly controlled (if it is allowed at all), and I
have the advantage of being able to use compiler-specific features as
needed. In general, I expect most C++ programmers don't rely on any
particular optimisations - but they rely on the sum of optimisations in
order to make their code efficient in the end result.
> On Wed, 17 Sept 2025 at 22:59, Nate Eldredge <nate_at_[hidden]
> <mailto:nate_at_[hidden]>> wrote:
>
>
>
>> On Sep 17, 2025, at 00:21, Yongwei Wu <wuyongwei_at_[hidden]
>> <mailto:wuyongwei_at_[hidden]>> wrote:
>>
>> I can hardly imagine it is "common". Who would have written such
>> code, especially when it never terminates? I would argue that an
>> infinite loop is better, in that it would alert the programmer
>> that something is broken. To me (and I believe to most C++
>> programmers), this is a surprising optimization. I really have
>> difficulty imagining it is truly useful.
>
> A very common reason for *humans* to write code to compute a value
> that is then unused: when the code that would use the value has been
> turned off for this build.
>
>
> I'll reply to this post to describe my points.
>
> First, programmers in general do not want to /rely on/ compiler
> optimizations to get rid of unused code. Do not forget it is also
> difficult for compilers to prove something is unused, especially when
> the compiler does not see all code at once.
>
C++ programmers must very much rely on compiler optimisations in order
to get efficient results - including dead code elimination. But often
the dead code in question is not something they have written themselves
- it is part of standard headers or other header code. Templates and
inline functions are regularly full of code that is designed to be used
or eliminated, depending on factors known at compile time (such as
template arguments). When possible, it is good to use things like
"constexpr if" to guarantee compile-time dead-code elimination, but in
other cases, that is not possible.
Yes, identifying dead code can be difficult for compilers - but
top-class compilers are very good at that kind of thing. And yes, it
becomes much more limited if the compiler does not have all the
information it needs. That is why people striving for high efficiency
code (remembering that most code is not performance critical) have
multiple techniques for working with their compilers in order to get the
highest efficiency results. These are, unfortunately, not all entirely
portable - while C23 has "unsequenced" and "reproducible" function
attributes, C++ does not (yet) have these and programmers have to use
compiler-specific features like gcc attributes and link-time optimisation.
> Another thing is unexpected results. It seems to me very often that
> programmers want to measure some calculations. However, when volatile
> variables, locks/atomics, or I/O operations are not involved, the
> calculation /sometimes/ gets eliminated (and sometimes not). It looks to
> me like a burden in education.
If that is a burden in education, then it is a burden educators will
have to bear. C++ should not compromise on efficiency or the
convenience of experienced programmers simply to reduce the learning
effort for newbies. C++ is not a beginners language. I am fully in
favour of trying to avoid surprises and unexpected behaviour in a
language, but the emphasis there has to be on making it harder to make
mistakes for the middle 80% of C++ programmers - not the top 10%
experts, nor the newest and least experienced 10%. And whenever common
compilers and tools can reliably spot problems with static error
checking, that is - IMHO - good enough. There is no benefit in changing
the language to reduce optimisation opportunities or efficiency for good
programmers in order to pander to those that don't care enough about
their code to use basic checking. (Sorry about the slightly off-topic
rant there.)
C and C++ are defined in terms of observable behaviour. Calculations
are not observable. Calculations that do nothing can be eliminated -
and they /will/ be eliminated if the compiler can see that they do
nothing. If you want to benchmark a calculation, you need to understand
how to benchmark calculations - if you are educating programmers, then
you need to teach them how to do this correctly and reliably. It is not
difficult, and is vastly better than making the language poorer for
everyone who is not sitting in a classroom. Start by making sure that
they all understand about observable behaviour. Then if you want to
force the compiler to generate code for a calculation, the answer should
be obvious to the students - make the inputs and outputs of the
calculation observable.
So instead of :
int input = 42;
int start_time = now();
int output = calculate(input);
int end_time = now();
do :
int input = 42;
int start_time = now();
volatile int v_input = input;
volatile int v_output = calculate(v_input);
int end_time = now();
I have seen countless cases of people making a mess of their
benchmarking because they don't understand how this works. Or they
don't understand about dependencies - they think that volatile accesses
force an ordering on calculations or on other accesses. And then they
try benchmarking their code with optimisations disabled - as useful as
testing the top speed of your car in first gear.
The answer here is not a worse language, but better education.
>
> For the sake of programmer friendliness (by the way, UB is /extremely
> programmer-unfriendly/), I would like to see forward progress opt-in,
> instead of the current opposite way. Do not surprise your users
> (programmers here). Do not expect every programmer to read the C++
> standard (just like most humans want to avoid reading user manuals
> wherever possible).
I certainly don't expect C++ programmers in general to read the C++
standards. But I /do/ expect them to be familiar with looking things up
when they are not sure - and to be familiar with resources like
<https://en.cppreference.com>. (Unfortunately, there are also many poor
quality resources out there too.)
And I think programmers should be taught to be /programmers/ before
being taught C++. A key concept in programming is "garbage in, garbage
out" - function pre-conditions and post-conditions have been fundamental
even though C++ has not had contracts until C++26. UB is putting
garbage into your function (or operator, or whatever) and expecting
roses to come out. UB is asking for the square root of a negative
number, or trying to fit 13 eggs into a box sized for 12 eggs. You have
to teach about UB - but you do so by emphasising that when you write
code that calls a function, /you/ are responsible for making sure the
input values are valid. It is not the fault of the language if you get
this wrong. Of course the function specifications or implementation
could be flawed, but then all bets are off. UB is a /good/ thing for
programmers - it is programmer friendly. It is part of making
contracts, specifications and responsibilities clear and well-defined.
(Unexpected UB is bad, but that is typically the result of bad
specifications, bad documentation, bad design or bad explanations - all
of which are, obviously, bad.)
Trying to remove all possibilities of UB in a language is futile and
unhelpful to programmers. Trying to understand UB - where it comes
from, why it exists, and how to avoid hitting it in practice - is the
important thing.
>
> I am in the process of preparing a lecture on UB, and it is really a
> pain to find that there are so many surprises in the C++ language.... I
> concur that UB is unavoidable at present, but I would argue it is better
> to have less UB where possible, especially when making UB defined does
> not bring real harm.
>
There is no doubt that there is a lot of hard stuff in C++. It is not a
beginners' language - it is big, and it has a lot of scary parts. Part
of that is inevitable when making an efficient compiled language. Part
of it is due to historic design decisions. And while it is easy for me
to say "the way to avoid UB is to make C++ education better", I am well
aware that teaching this stuff is far from easy!
Perhaps you can turn this all on its head. Instead of thinking of all
the potential UB situations, put the emphasis on being sure your code
makes sense in the first place. Don't worry about code being UB if it
fails to make progress or has certain kinds of infinite loops - rather,
say that C++ programs should do something useful. Either they do
something useful and terminate, or they keep doing something useful
indefinitely. Don't worry about signed integer arithmetic overflow
being UB - rather, emphasis that it is obvious that you need to use
types that are big enough to handle the numbers you are using.
Encourage people to learn what the language guarantees, and to stick to
that, rather than trying to guess all the unwarranted assumptions or
ways to write nonsensical code. You'll still be left with some cases
where there is surprising UB, but you can eliminate most unsurprising UB
by encouraging people to think about what works, rather than being
concerned about what doesn't work.
> While I agree the optimization like you mentioned may really exist, I
> still can hardly imagine people will /rely/ on it. It is too fragileāa
> single memory allocation can make the optimization impossible at run time.
>
I certainly rely on dead code elimination for some of my programming.
But in my main work (small systems embedded programming), memory
allocation is very tightly controlled (if it is allowed at all), and I
have the advantage of being able to use compiler-specific features as
needed. In general, I expect most C++ programmers don't rely on any
particular optimisations - but they rely on the sum of optimisations in
order to make their code efficient in the end result.
Received on 2025-09-18 09:41:39