Live data from Hacker News

Why Const Doesn't Make C Code Faster

theartofmachinery.com

111–120 of 197 posts

Re: Why Const Doesn't Make C Code Faster

#111
post #109

Surprised no one has mentioned what the author of C, Dennis Ritchie, said about the addition of "const" to the language. https://www.lysator.liu.se/c/dmr-on-noalias.html

> Assigning an ordinary pointer to a pointer to a `noalias' object is a license for the compiler to undertake aggressive optimizations that are completely legal by the committee's rules, but make hash of apparently safe programs

I can only imagine what he thought about aggressively optimizing C-family compilers in his later years.

Re: Why Const Doesn't Make C Code Faster

#112
post #88

Earlier quoted context omitted.

Even excluding casting evils, calling a method with a const pointer only means that the method isn't supposed to change the value. It does not mean that the caller isn't going to change things (particularly from another thread). This is incorrect. Actually, you promise not to change things from another thread, DMA, interrupt handler, signal, etc, with any non-volatile reference passed, let alone a const! The compiler…

Your comment is incorrect as well. The C specification describes semantics in terms of an abstract machine which is actually quite different from real hardware (most notably in terms of how memory works!). It then goes on to say that the compiler may choose to implement it radically differently, so long as the observable semantics (I/O calls and volatile accesses) are preserved. I'd have to double check whether it wa…

??

You say that it is only thing outside of the execution model that require volatile, but you explain that a signal handler requires volatile and to be the signal-atomic type. ;) In practice, volatile with lock-free types (that now stdatomic has a way to query whether types are lock-free) are correctly used all the time to pass data between threads.

A volatile declaration may be used to describe an object corresponding to a... an object accessed by an asynchronously interrupting function.

Like, execution in other thread contexts.

Or, from the C99 Rationale:

The translator may assume, for an unqualified lvalue, that it may read or write the referenced object, that the value of this object cannot be changed except by explicitly programmed actions in the current thread of control, but that other lvalue expressions could reference the same object.

vs volatile:

No cacheing through this lvalue: each operation in the abstract semantics must be performed (that is, no cacheing assumptions may be made, since the location is not guaranteed to contain any previous value). In the absence of this qualifier, the contents of the designated location may be assumed to be unchanged except for possible aliasing.

> Double-checked locking is problematic in large part because of hardware reordering of loads and stores.

This was unsafe even on in-order uniprocessors, because the compiler was free to reorder loads and non-aliased stores.

Re: Why Const Doesn't Make C Code Faster

#113
post #8

I've worked with JavaScript weenies who are in love with the const declaration. They don't have an answer, though, when I ask, "Shouldn't the compiler be able to notice that a variable isn't changed in it's lifetime?" Most of the const values are just local and never leave the function but the weenies continue to stomp their feet when I use "var". The compilers are smart. Let them do their thing.

You use const because you want a runtime error if someone attempts to change it.

It's also documentation of intent.

Some code, for better or worse has very long functions, and you might use the variable 100 lines down from where it is declared.

Is a runtime error better than letting it silently change? Debatable, but the error is more likely to blow up a unit test, so you know there is a problem before you ship.

And needless to say: in Typescript you'll get a compile time error. In Typescript it's a no-brainer to use const when appropriate.

Re: Why Const Doesn't Make C Code Faster

#114
The most succinct way I've heard this explained is: "Const means 'I won't mutate this'. It doesn't mean 'nobody will mutate this'."

To do optimizations, compilers really need to know that nobody will mutate a value; just knowing that a particular function won't mutate a value isn't that helpful.

Re: Why Const Doesn't Make C Code Faster

#115

> This is true in practice because enough real-world C code has “I know what I’m doing” casting away of const. I found this out the hard way when I implemented OptimumC, the first data flow analysis optimizer for DOS C compilers back in the 1980's. I had to undo doing optimizations on const. Anecdote: Optimum C blew away all the other C compilers on a magazine benchmark article because it figured out that the benchma…

>The author didn't ask me about it, he just assumed the compiler was buggy and gave us a bad review. Sigh.

Did you write a letter complaining? Any response? Do you have a link to the review or copy of it? (Sorry, I just find this history fascinating).

Re: Why Const Doesn't Make C Code Faster

#116
I used to strive for "const correctness" in my game engine code, and wasted a fair amount of time fighting the compiler and editing function signatures as requirements changed. Mostly eliminating const from the codebase simplified things a lot.

Didn't do any comparison studies of performance, but the productivity boost from not having to think about it was nice, and I suspect any performance difference is margin-of-error stuff like the sqlite results in this article.

Re: Why Const Doesn't Make C Code Faster

#117

The most succinct way I've heard this explained is: "Const means ' I won't mutate this'. It doesn't mean ' nobody will mutate this'." To do optimizations, compilers really need to know that nobody will mutate a value; just knowing that a particular function won't mutate a value isn't that helpful.

True to form, there's multiple forms of const in C++.

const on a methond says "I won't mutate the state of this class (except for members explicitly marked as mutable)

A const reference says I won't mutate this (unless I const_cast away the constness first)

But a const variable actually is a promise to C++, saying "This object will never mutate after initialization ever forever I promise for real this time " (except in the destructor, then it's OK).

Confusingly, all together this means you're only allowed to cast away constness if the object wasn't const to start with.

___

Some of the above may be wrong, not a language expert.

Re: Why Const Doesn't Make C Code Faster

#118
post #107

Earlier quoted context omitted.

Exactly. const int* is a non-const pointer to a const int; int* const is a const pointer to a non-const int. So I’m pretty confused by the claims. Maybe the compiler will happily let you modify the dereferenced const int* (undefined behavior), wouldn’t try it now, but that’s not what the signature promises. Edit: Thought about it more and read some other comments. Now it makes sense.

If the initial object itself isn't const, then merely declaring the function parameter as a pointer to const won't guarantee that some other thread of execution won't change the value under your nose. Or if you have multiple pointer parameters, they can alias each other. Indirection in C and C++ is a mess, but at least C has the "restrict" keyword. Best to program with value types whenever you can, and use pointers a…

> won't guarantee that some other thread of execution won't change the value under your nose

My understanding is that compilers can always assume that there is no other thread involved, which (part of) why C11 atomics are necessary. Is that not the case?

Re: Why Const Doesn't Make C Code Faster

#119
post #112

Earlier quoted context omitted.

Your comment is incorrect as well. The C specification describes semantics in terms of an abstract machine which is actually quite different from real hardware (most notably in terms of how memory works!). It then goes on to say that the compiler may choose to implement it radically differently, so long as the observable semantics (I/O calls and volatile accesses) are preserved. I'd have to double check whether it wa…

?? You say that it is only thing outside of the execution model that require volatile, but you explain that a signal handler requires volatile and to be the signal-atomic type. ;) In practice, volatile with lock-free types (that now stdatomic has a way to query whether types are lock-free) are correctly used all the time to pass data between threads. A volatile declaration may be used to describe an object correspond…

> You say that it is only thing outside of the execution model that require volatile

No, I'm saying that the statement that volatile is only necessary and sufficient for outside the execution model. For signal handlers, it is necessary but not sufficient; for threads, it is neither necessary nor sufficient.

> Or, from the C99 Rationale:

C99 does not consider threading at all. C11 and C++11 do, and any compiler written in the past decade is going to be obeying the rules for the C++11 memory model (which C11 adopted as its memory model). The committees explicitly considered whether it would make sense to imbue volatile with any special threading semantics, and they explicitly rejected doing so.

A volatile read or write is not guaranteed to be converted into a single hardware load or store, even for lock-free types. There are situations where the compiler will narrow or widen the load/store, or even insert extraneous loads and stores to the value.

> This was unsafe even on in-order uniprocessors, because the compiler was free to reorder loads and non-aliased stores.

The compiler is free to reorder non-volatile loads and stores with volatile loads and stores. Only reordering volatile loads and stores with respect to other volatile loads and stores is prohibited. Volatile is not sufficient to make double-checked locking safe.

Re: Why Const Doesn't Make C Code Faster

#120

I have done a lot of profiling work and I have observed similar things. One thing is const, another is virtual functions. A lot of people think they add overhead and avoid them for performance reasons but my profiling almost never showed them as a problem. Same for const and inline. it’s really hard to predict what the optimizer will do. Obviously there are stupid things that can be avoided from the start but in gene…

Inlining especially is call-site dependent. Little of the benefit of inlining comes from eliminating the call overhead on a modern processor. But quite often, functions are called with one or more parameters constant. So that enables a bunch of constant folding for that call site -- the compiler is good at noticing that if(1<7) is always going to be true, and dropping out the whole else branch, which can enable code…

"Inlining is a gateway drug^H^H^H^H optimization"
Post reply on HN