16 ms·
Why Const Doesn't Make C Code Faster
- foxhill 7y agooverlooks the value of const qualification for the caller - if the argument is marked as const, the caller might reasonably assume the data won’t be changed.
- deleted 7y ago[deleted]
- vortico 7y agoYes, there are many cases where the caller can optimize the dependency chain / order of constFoo(x); constBar(x); where it couldn't make that assumption if one function wasn't const.
- mlyle 7y agoUnfortunately, C++ const functions are not necessarily pure functions and cannot be freely reordered.
- vortico 7y agoYou're right, I was mistaken. And if the function is in the same compilation unit so the compiler is able to prove that it's pure, it would also be able to prove that a non-const pointer is actually const.
- commandlinefan 7y agoI don't think he's saying that you shouldn't use const (if he is, I vehemently disagree!) just that using it won't make the code faster. It will definitely prevent you, or the next maintenance programmer, from making a lot of dumb mistakes.
- Ididntdothis 7y agoI 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 general I prefer clean code where people write for readability and simplicity and not speed.
- EpicEng 7y agoYes, profile. Anecdotally, I once removed a number of virtual function calls as they were imposing significant overhead in a tight loop (image processing). Would that be the case today? No idea; I'd have to measure it again.
- Ididntdothis 7y agoImage processing is a different beast. A lot of subtle things are in play here. I remember getting big improvements by iterating through the images buffer either line vs row or even arranging memory in 64 byte squares instead of linear. I was often surprised when I looked at the optimized assembly. Optimizer are really clever and do a lot of surprising things....
- EpicEng 7y agoYes: row traversal (no hopping around in memory and causing cache flushes) and block size reads/writes are a big deal. Luckily they're easy enough to do right every time once you know. I used to take some flak from the "premature optimization blah blah" side of the house on occasion.
- dbcurtis 7y agoInlining 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 motion and more common subexpressions... so inlining is often a win but often people misunderstand exactly why.
- SomaticPirate 7y agoIs the same true for other compiled languages? Like Golang?
- xhkkffbf 7y agoI'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.
- kgwxd 7y agoIt tells the reader something too. There's no excuse for var anymore.
- wnevets 7y agoconst doesn't actually stop data from changing, just reassignment. const user = {id: 1, status: 'active'}; user.status = 'disabled'; is valid javascript
- echelon 7y agoWhich is to say that the const pointer itself doesn't change. It doesn't impose any constraints on the values of any underlying data structures. Javascript could use some proper immutable types.
- mywittyname 7y agoYou're talking about two different things. In this case, you'd use... const user = Object.freeze({id: 1, status: 'active'}); But const is immaterial to this topic, as it only prevents you from being able to reassign user to some other value, i.e., user = 7; While... let user = Object.freeze({id: 1, status: 'active'}); user.status = 'disabled'; // throws an error. Provides the behavior that you're seeking to guard against, regardless of let/var/const usage.
- wnevets 7y ago>You're talking about two different things. In this case, you'd use... No, I'm talking about the behavior of const.
- not2b 7y agoUnfortunately in C++, the compiler is not allowed to assume that if it passes a const reference to a function, that function will not change the object. The function is allowed to cast away const and modify the object. I'm not happy that they did it that way; I would have preferred it if cast-away-const were more restricted (for example, allowed when calling a child function that takes a char* but doesn't modify the pointed-to C string), with the idea being that if a function has only a pointer to const or a reference for a const object, it has read permission on the object and lacks write permission. But that isn't how the language works.
- saagarjha 7y agoThe fact that std::launder (https://en.cppreference.com/w/cpp/utility/launder https://en.cppreference.com/w/cpp/utility/launder) exists blows my mind. Like, why is this a thing that the standard allows?
- jcranmer 7y agohttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0532r0.pdf http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p053... has an explanation. In short, placement new (or some scenarios involving unions) technically cause undefined behavior if you try to use the object after the call to placement new, since the pre-existing object there has had its lifetime expire. std::launder lets you use a pre-existing pointer to the memory at the same location to access the data there.
- saagarjha 7y agoYes, I know what std::launder does, and that document contains some of my own thoughts: namely, placement new (the only use I've seen suggested) should automatically launder, and std::launder should just not exist.
- gpderetta 7y agoAFAIK placement new does launder. But both new and launder have no effect on their argument (well, placement new of course constructs an object there), they only 'bless' pointer returned from it. The use case is if you placement new on some byte storage. Now you want to access the object stored there, and of course you do not want to placement new the storage again, nor you have cached the result of the previous placement new (it would be suboptimal), instead you want to get a pointer to T from the storage address itself.
- badsectoracula 7y ago> So most of the time the compiler sees const, it has to assume that someone, somewhere could cast it away, which means the compiler can’t use it for optimisation. This is true in practice because enough real-world C code has “I know what I’m doing” casting away of const. Some years ago i'd agree, but experience has shown that C/C++ compiler writers would rather win benchmark games than keep working code working. So it'd be nice if there was a better reason than "well, a lot of code would break if they did that".
- aflag 7y agoI think the point is that correct code could break.
- mafuyu 7y agoThat's typically in the case of UB, where compilers can make whatever assumptions they want. In this case, even if the code is completely within the C spec, the compiler cannot guarantee that the data is unmodified.
- BeeOnRope 7y agoIt's not even that: it's that if it sees a pointer declared const, it doesn't know if it was actually defined const. I may have a non-const object that I pass to a function that takes it by const pointer: that doesn't magically make it const! The object can still be modified, e.g., by an opaque function in the caller which modifies the object not necessarily by casting away cost, but because the object isn't const at all (e.g., it may have a non-const reference to the object). Const definitely helps optimizations, but only when the object is actually cost. References or pointers to const objects can't tell you that.
- kmadento 7y agoShouldn't the test instead be: const int * const <variable name> Haven't profiled it but would make more sense to have the pointer also be const instead of having a non const pointer as input?
- jforberg 7y agoIt rarely makes sense. Pointer to const is a contact between caller and callee. A signature like char <star>strdup(const char <star>) says "I take a pointer to memory that I promise not to modify, and you get a pointer to memory that you may modify". Const pointer is a statement about the internal variables of a function definition, usually not of any interest outside the function itself and therefore rarely used.
- MauranKilom 7y ago> Const pointer is a statement about the internal variables of a function definition, usually not of any interest outside the function itself and therefore rarely used. ...and in fact not even part of the name mangling (for the exact reason you mentioned): https://godbolt.org/z/1pjecq https://godbolt.org/z/1pjecq
- dmitrygr 7y agogcc has some attributes to help with that, for example try __attribute__((pure)) on funcs that will always return the same thing for the same inputs (given the same global state) and do not modify said input or state (and thus can be called more or fewer times than programmer wrote). Usually gcc will be quite aggressive with optimizations if you use that. For even more aggressiveness, try __attribute__((const)) which tells it that the func accesses NOTHING but the params, not even global vars. Of course this is quite limiting. You can also use this as a trick when YOU know that the only accessed global state is itself constant but gcc does not know that. This can produce substantial savings in number of calls of funcs that, for example, do lookups in large constant data tables, like table-based versions of sin() and cos(). In the example the article gives, adding __attribute__((pure)) to constFunc() does produce the behaviour author wanted and then some LWN has more: https://lwn.net/Articles/285332/ https://lwn.net/Articles/285332/
- Ididntdothis 7y agoDoes it enforce these attributes or are they just a promise by the code author?
- dmitrygr 7y agoThey are a promise and that is precisely what makes them powerful. It gives GCC info it might not be able to derive, but you know to be true. You can even promise a func is pure without even implementing it in the same translation unit! That produces lots of improvement. See these two godbolt links: https://godbolt.org/z/v2zqJI https://godbolt.org/z/v2zqJI https://godbolt.org/z/kk3Q3T https://godbolt.org/z/kk3Q3T
- einpoklum 7y ago> You can even promise a func is pure without even implementing it in the same translation unit! Actually that's the main point, as if it's in the same TU, the compiler can probably figure out a function is pure.
- 7y ago
- ljackman 7y agoThe key difference between `const` and the `register` and `inline` keywords is that, despite all of them often flagging optimisations that the compiler can often work out anyway, `const` still aids human comprehension by declaring constraints, whereas the latter two do not. It seems that interprocedure optimisations in modern compilers make a lot of const-by-reference optimisations apply even when the code is mutable-by-reference in the parameter list but the function body doesn't modify it in practice. This would only work if it could deterministically work out which function is called. Local constant stack values surely can be completely deterministically verified as such by the optimiser even without the `const` modifier. They could be overwritten without a C assignment to it, via the stack from a buffer overflow of an array next to it, but that's undefined behaviour so a compiler is presumably free to assume it is not modified, eliminate unnecessary register/memory loading code, and let the developers deal with the consequences. As trailing `const` on member functions outlaws modifications via `this`, it would follow that the same optimisation-even-without-modifier process would apply as to `const` local stack values. As constancy is a constraint that aids human comprehension, there's a good reason for choosing a keyword just as short as the mutable equivalent, such as Swift's `let` vs `var`; if the more constrained equivalent is equally or more convenient, more constrained and thus easy-to-reason-about code becomes more common.
- caf 7y agoLocal constant stack values surely can be completely deterministically verified as such by the optimiser even without the `const` modifier. Only if their address doesn't escape.
- AstralStorm 7y agoThe compiler will detect any attempt at taking the address of the variable (including fancy things like most inline assembly) and skip the optimization, marking it as requiring a memory location. However, it can still cheat by rearranging the memory write or even letting linker initialize the address.
- caf 7y ago
- klingonopera 7y agoI hardly ever bother optimizing my code anymore, with -O3 nothing I do ever really seems to make a difference. The real reason to use "const" is to show intent. See "Const and Rigid Parameters" in this wonderful article about the Doom 3 source code: https://kotaku.com/the-exceptional-beauty-of-doom-3s-source-code-5975610 https://kotaku.com/the-exceptional-beauty-of-doom-3s-source-...
- saagarjha 7y ago> I hardly ever bother optimizing my code anymore, with -O3 nothing I do ever really seems to make a difference. Your choice of data structure or algorithm would…
- klingonopera 7y agoYes, true, though I consider that "structural/procedural" optimization as opposed to "technical" optimization. This means, I won't bother using "inline" anymore, but I will consider how many subfunctions in total I'd call.
- planteen 7y agoI find that I can sometimes get significant speedups by changing data access patterns when considering cache. I have found this to be the first and most valuable thing to tune, before attempting SIMD.
- klingonopera 7y agoInteresting! Any rules-of-thumb you can share about that process?
- ridiculous_fish 7y agoconst_cast risk is not why the compiler emits a reload. Indeed we don't even need to pass x to constFunc to get the reload: see line 7 in https://godbolt.org/z/TjmWxL https://godbolt.org/z/TjmWxL Consider that `x` may point to a global variable which `something` may modify. That is why the reload is necessary. edit: I just realized that the reload would be necessary even without an intervening function call. https://godbolt.org/z/QhVzlV https://godbolt.org/z/QhVzlV Consider that `x` may point to errno; then printf would modify it!
- hermitdev 7y agoThe reload shouldnt be necessary unless there is an appropriate memory barrier, though. Seems like the compiler is being pessimistic in the examples.
- einpoklum 7y agoYou're mistaken. What you're thinking of is a pointer to a _volatile_ ; for a regular pointer, you only need one load - even if it's not a pointer-to-const. See: https://godbolt.org/z/XWW9Nq https://godbolt.org/z/XWW9Nq
- amalcon 7y agoI think you're talking past each other. Grandparent is examining the case where there is a function call (the printf), but you do not pass anything relevant into it. You're looking at the case where there is no function call, so peephole optimization can observe that the memory at that location can't change. "volatile" basically tells the compiler "Hey, wait, this might be modified by DMA, another thread, or another process. Assume nothing."
- mrpippy 7y agoOn some microcontrollers, marking variables/arrays as const allows the compiler to access them directly from flash rather than having to copy them into RAM at startup. I used this to great effect on a PIC24 with 256KB flash/16KB RAM.
- blackflame7000 7y agoGood point! On Arduino sometimes your program won't fit unless you store some parts in flash.
- jsd1982 7y agoThough this becomes incredibly annoying using const pointers when you didn't mean to refer to ROM but instead meant a read-only pointer to RAM.
- chmaynard 7y agoMany laws exist that express the intent of a law-making body but are either unenforceable or too expensive to enforce in any meaningful way. Perhaps the const declaration in C is an example of this type of law. In contrast, a Standard ML compiler can enforce this law because the language itself insists that all bindings are immutable.
- mlyle 7y agoThis assumes that the compiler can actually infer that a const-by-reference isn't modified. The second we have indirection (function pointers), or a different source file without whole-program-optimization, or a library, these assumptions break down. Further, the author assumes that the compiler can't benefit from the knowledge that something is const because the const can be cached away. This isn't true, e.g. per ISO9899 6.7.3.5: If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
- WalterBright 7y ago> 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 benchmark code did nothing and deleted it. The author didn't ask me about it, he just assumed the compiler was buggy and gave us a bad review. Sigh. When other compilers did DFA, the benchmarks got changed.
- cheez 7y agoNice
- 420codebro 7y agoSounds like you tried to game the system and got bitchslapped.
- Diederich 7y ago> Anecdote: I'd love to hear more about any aspect of this if you have time. (:
- WalterBright 7y agoHere's another failure of marketing on my part. We included full library source with the compiler, for free. No reviewer ever noticed that. One day, Borland decided to make source code for their library, sans the floating point stuff, available for an extra charge. This generated headlines in the next compiler roundup article. No mention that complete library source code, including floating point, came with my compiler. So we decided to split off the library source code into a separate package, and charged for it. This solved the marketing problem, and doubled the money we were making. This was how I learned about the common practice of upselling. I am a slow learner. Of course, these days we give away everything for free!
- MaximumYComb 7y ago
- WalterBright 7y agoThis issue is why D has both const and immutable qualifiers. const can't be optimized because there may be other mutable references to the same memory object. But for immutable references, there cannot be. It is possible to cast away const and immutable in D, but these are only allowed in system code, presumably where the programmer actually does know what he's doing.
- gavanwoolery 7y agoConst won't necessarily make things faster but constant values will (whether or not explicitly inferred). I noticed, for example, a 2x+ speedup in one of my ray tracers by downgrading my dynamic 2d/3d/4d vector class* to always have a constant size of 3. * (poor choice, I know)
- sdinsn 7y agoHaving a constant size probably made it easy for the compiler to properly generate bytecode that uses vectorized instructions
- BubRoss 7y agoI would say that's unlikely. SIMD instructions don't usually get used by normal compilers without very specific loops that make sure there are no obstacles to vectorization. Also the best way to use SIMD is to loop through a large array of two and do very simple operations with them. Modern CPUs actually have three (I think) floating point slots so their total floating point throughput isn't simply a fraction of the SIMD size.
- sdinsn 7y ago> SIMD instructions don't usually get used by normal compilers without very specific loops Compilers are smart. Even Java's JIT will generate SIMD instructions pretty well
- BubRoss 7y agoI would have to see this to believe it. Even Intel's own compiler is extremely sensitive to small changes turning off SIMD use in a loop. You can try out compilers and see their asm at godbolt.org
- deleted 7y ago[deleted]
- BubRoss 7y ago
- BeeOnRope 7y agoConst does make C go faster, just not in most of the places you see it used. For const to help, the object itself needs to be defined const. Just taking a `const object * ` (or `const object&` in C++) doesn't help you determine the constness of the underlying object, and that usually accounts for the majority of const usage by volume. Limiting the scope to of actual const definitions, it can help a lot, but only in cases where the compiler couldn't move that anyways. So local variable const definitions rarely help, because the compiler can often already prove they are const by inspection (but they can help if the variable escapes). They are useful especially for global variables (and moral equivalents, like static class members in C++), since the compiler cannot prove by examining only the current TU whether the variable is unmodified (and it is a hard problem even if the whole program can be inspected), so const is a useful promise there.
- taylodl 7y agoBINGO!!! The pointer is constant, not the item being pointed to. Since you’re dereferencing the pointer, i.e. accessing the non-const bits, the compiler must reload.
- bstamour 7y agoThe examples in the articles are non-constant pointers to constant data. If you want to declare the pointer itself const, you need to do it after the asterisk. const int* const instead of const int*
- oefrha 7y agoExactly. 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.
- 7y ago
- haberman 7y ago> So, what’s const for? For all its flaws, C/C++ const is still useful for type safety. Const variables can also be mapped read-only. This gives you hardware protection against modifications, and also uses less RAM if the const variable is in a shared library (multiple processes can share the same mappings).
- jasonzemos 7y agoI wish there was a C++ compiler flag which made everything const by default and required the mutable keyword otherwise. This is obviously non-standard and would break included headers, but alternatively a #pragma could scope that option for project code coming after non-project includes.
- Someone 7y ago// x is just a read-only pointer to something that may or may not be a constant void constFunc(const int *x) Is that correct? I read that as “x is a pointer to a const int” If you want “x is a read-only pointer to an int”, you would need void constFunc(int * const x) The article also doesn’t mention the case “x is a read-only pointer to a constant int”. To state that, you would use void constFunc(const int * const x) (https://stackoverflow.com/a/1143272 https://stackoverflow.com/a/1143272)
- mikeash 7y agoI believe the distinction they’re making is that there is actually no guarantee that the value pointed to by x will remain constant. All this tells you is that you’re not allowed to use x to make the modification. Consider: int foo = 42; const int *x = &foo; foo = 43;
- dmitrygr 7y agoRestrict keyword does do that if you wish to say that to the compiler. It tells it that no parameters point to the same memory. So if you won't change things via x, *x won't change.
- cogman10 7y agoI think the big problem here is that const doesn't really truly say anything about aliasing. 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). The languages are simply too permissible when it comes to what a pointer means and how it can be used. For const to be optimizable, you'd need to take Rust's approach and make language level guarantees that "You can't do bad things with this". C and C++ missed that boat.
- mlyle 7y agoEven 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 loads things into registers and has no way to know if memory in a passed reference changes underneath the hood-- it freely generates code that assumes that the things it has pointers do, do not change. It can freely make optimizations that lead to incorrect computation, infinite loops, segmentation faults if this is not obeyed. If you've ever head about how "double check locking" is an antipattern, this is a big part of why. e.g. from ISO 9899: Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. In such an implementation, at the time of each function entry and function return where the calling function and the called function are in different translation units, the values of all externally linked objects and of all objects accessible via pointers therein would agree with the abstract semantics. Furthermore, at the time of each such function entry the values of the parameters of the called function and of all objects accessible via pointers therein would agree with the abstract semantics. In this type of implementation, objects referred to by interrupt service routines activated by the signal function would require explicit specification of volatile storage, as well as other implementation-defined restrictions. This is the model used by pretty much every C compiler you'll encounter. When you e.g. acquire a lock, you call something in a different linkage unit so multithreaded stuff behaves properly. For const, the guarantees go further: you promise it won't change elsewhere (relevant standards text quoted in my other comment).
- kazinator 7y agoWell, of course: void constByArg(const int *x) { printf("%d\n", *x); constFunc(x); printf("%d\n", *x); } Here, the object referenced by pointer x is non-local, and so is constFunc. The object could be modified in legal ways nothing to do with constFunc stripping away the qualifier. Also, if the object is not defined const, then constFunc is allowed to do that, too. Then the next, correct, example with a const protected local shows that two instructions are shaved off. That's a wortwhile saving that could be leveraged to get faster code. If you're passing local variables into helper functions which are not supposed to change them, you can shave off some cycles with const. > I mean, I removed const from the entire program Maybe sqLite doesn't use const specifically with a view toward optimization. To see an overall performance impact, there would have to be some case in a "hot spot" of the program, where const is used with some local variables being passed into functions. (Or whatever other case we can ferret out where const happens to help.) There are some widely applicable optimizations which scoop up all the "low hanging fruit" improvements, but after that, optimization is a game of eking out small gains with specialized cases.
- ma2rten 7y agoIt seems to me that compilers could benefit from doing analysis of functions and providing that information to callers.
- gpderetta 7y agothey do.
- stephc_int13 7y agoI once had a very stubborn intern who decided to add as much const as he could in our codebase. What followed was a heated discussion and I did a global replace of const by nothing. The main problem with const is that it's ugly, it's viral and it does not add any value to the code. And I know all the supposedly good things about this, but I never found any real usage in practice.
- will4274 7y agoYou should fire yourself. Seriously. Understanding which object parameters to a function are inputs and which ones are modified is key information about the behavior of a function. It's omission requires devs to divine the intent from the name and hope that every previous dev was a good citizen w.r.t keeping function names accurate to intent. Finding and replace const with empty is the moral equivalent of replacing all the types of parameters with void*. After all, types are viral and ugly too. I really hope that intern found a better place to work than your company.
- stephc_int13 7y agoI strongly disagree, obviously. Using const to document code might seems like a good idea in some organizations, with extremely large codebase, I don't know. But first, when you're an intern, you have to follow the local codestyle, even if you think it's not ideal. Second, I think there are much better rules than spamming const to attain the same objective, but you might need more experience to really appreciate that.
- will4274 7y ago> Second, I think there are much better rules than spamming const to attain the same objective Like? > you might need more experience to really appreciate that. Practically a picture perfect appeal to authority.
- icedchai 7y agoWith "const" you still need to rely on devs being good citizens since const can be trivially casted away. I've been programming in C for 25+ years. const is clutter.
- fallingfrog 7y agoI use const all over the place, but no so much to make the code faster as to eliminate potential stupid mistakes.
- caf 7y agoIt's true that, as demonstrated, const-qualifying your pointer arguments is very unlikely to allow any optimisations on its own. However, const-qualifying your pointer arguments where the pointed-to object isn't changed is what allows you to make more liberal use of const-qualified declarations, and as also demonstrated by the article, those in turn do allow some optimisations. Additionally, there is some safety on the table: if your C program makes use of pointers to structures full of function pointers to implement polymorphism, making those pointers const-qualified allows your underlying structures full of function pointers to be declared const, which in turn allows them to be stored in hardware-enforced read-only memory. As a side note, I've often thought that block-scope variables declared const and whose address is never taken should be automatically made static.
- 3xblah 7y agoSurprised 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 https://www.lysator.liu.se/c/dmr-on-noalias.html
- favorited 7y ago> 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.
- tutfbhuf 7y agoYou should also test it with older compiler versions from the 80s/90s. There are some falsehood believes about programming now that were true some decades ago. I'm not saying that this is the case here, but it's worth to keep that in mind.
- sclangdon 7y agoC++, rather than C, but there was a GOTW post about this years ago, which explains the situation in detail. http://www.gotw.ca/gotw/081.htm http://www.gotw.ca/gotw/081.htm
- pcwalton 7y agoThe 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.
- missblit 7y agoTrue 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.
- nikbackm 7y agoYou are allowed to cast away const-ness of an actual const object, just as long as you don't modify it.
- QuadrupleA 7y agoI 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.
- account42 7y agoProductivity boost from not writing tests or thinking about function preconditions is also nice ... until it isn't.
- ndesaulniers 7y agoIf clang can see the definition of `constFunc` and deduce that its parameters are `noescape`, then I think it can avoid reloading `x`. A recent optimization in clang (not sure if it's in clang-9 or clang-10) will remove memsets to variables declared const, which usually come from assigning through a pointer that's had const (of the pointed to type) casted away. The MIPS Linux kernel won't boot when built with clang due to the above (I sent a patch 2 weeks ago)
- rurban 7y agoOverly broad statement. const for global or static variables do make code faster, as they are placed in the .rodata segment, which allows swapping them out for free. This can be a huge win. He only talks about const args, where the compiler does not try yet to check for casts which violate the constness guarantees. (DFA, Escape Analysis). And thus misses all important optimizations. You cannot rely on that never being implemented in the future. With LTO they already do I think.
- numlock86 7y agoI just see too many programmers that right from the start try to micro-optimize their code with things like this. You'll usually see very well written code but with all sorts of hacks, taken from blogs and StackOverflow answers and whatnot all across the web, but in the end they fail to optimize their actual algorithm and end up with some ultra-high memory footprints or O(n^3) stuff instead of some nice O(log(n)) for example. And they'll argue that they already "optimized the sh*t out of it", because they saved a couple of hundred assembly calls in a binary file around the size of a few megabytes ...
- known 7y agoExtra protection of data costs speed;
- mcv 7y agoNot quite the same issue, but this reminds me of my very first job, back in 2001, where we used a homebrew C++ framework that separated lots of concerns, and then used a single massive .h file with 10,000 const uints to tie everything together. Compiling took over 2 hours. The header was generated automatically, but after I added a macro that replaced those const uints with #defines, compile time dropped to 30 minutes, which was quickly voted the biggest productivity enhancement in that project. More relevant to the topic, in javascript programming I recently switched to using const instead of let wherever possible. Most variables don't actually vary, so let's make that explicit. That can prevent some unexpected surprises (though not all, as objects and arrays in constants are not immutable).
- craftoman 7y agoWe're talking about billions of calls that have milliseconds in difference. That's irrelevant when building a basic app that sits on a 10K worth of hardware machine with hundreds of CPUs.