5 ms·
In general I've long been very skeptical of removing optimizations that rely on undefined behavior. People say "I'd happily sacrifice 1% for better theoretical
by pcwalton 5y ago
In general I've long been very skeptical of removing optimizations that rely on undefined behavior. People say "I'd happily sacrifice 1% for better theoretical semantics", but theoretical semantics don't pay the bills of compiler writers. Instead, compiler developers are employed by the largest companies, where a 1% win is massive amounts of dollars saved. Any complaint about undefined behavior in C must acknowledge the underlying economics to have relevance to the real world.
As the paper notes, there are plenty of alternative C compilers available to choose from. The reason why GCC and LLVM ended up attaining overwhelming market share is simply that they produce the fastest possible code, because, at the end of the day, that is what users want.
If you want to blame someone, blame the designers of the C language for doing things like making int the natural idiom to iterate over arrays even when size_t would be better. The fact that C programmers continue to write "for (int i = 0; i < n; i++)" to iterate over an array is why signed overflow is undefined, and it is absolutely a critical optimization in practice.
- userbinator 5y agoThe reason why GCC and LLVM ended up attaining overwhelming market is simply that they produce the fastest possible code No, I think it's more because they are free. In my experience, ICC can be much better at instruction selection while also not being so crazy with exploiting UB.
- Someone 5y agoICC nowadays is based on LLVM (https://software.intel.com/content/www/us/en/develop/blogs/adoption-of-llvm-complete-icx.html https://software.intel.com/content/www/us/en/develop/blogs/a...). That might mean the differences have mostly disappeared, but that may depend on what the front end (icx vs clang) does.
- pcwalton 5y agoI'm also skeptical about the often-claimed superiority of ICC. The numbers I've seen are very equivocal. Besides, it's irrelevant: there are lots of free C compilers that don't exploit UB, and also rarely get used.
- rurban 5y agoLots? None are usable to replace our current insane compilers, removing code without warnings. None of pgcc, sdcc, tcc, lcc and friends work with current code bases, plus they have severe bugs.
- vyodaiken 5y agoIt is because they are "free" and also because they attract massive investment from FAANG and National Labs etc.
- Arnt 5y agoUh, massive investment? The commit logs reveals how many people work on them, and while it's massive compared to single-pizza teams I've worked in buildings where all of them could get a desk each.
- gHosts 5y agoThey also support the widest range of CPU's
- astrange 5y ago> If you want to blame someone, blame the designers of the C language for doing things like making int the natural idiom to iterate over arrays even when size_t would be better. The fact that C programmers continue to write "for (int i = 0; i < n; i++)" to iterate over an array is why signed overflow is undefined, and it is absolutely a critical optimization in practice. Well, size_t is unsigned and has defined overflow, so you'd lose the optimization if you switched to it. (Specifically, there's cases where defining overflow means a loop is possibly infinite, which blocks all kinds of optimizations.) Many languages try to fix this by defaulting to wrap on overflow, but that was a mistake because you rarely actually want that. A better solution is to have a loop iteration statement that doesn't have an explicit "int i" or "i++" written out.
- pcwalton 5y agoThe optimization I'm referring to is widening a 32-bit loop IV to 64-bit so it can stay in a register: https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7 https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759... size_t obviates the need for this optimization.
- astrange 5y agoThat's a strange example since it doesn't prove their point. "int count; … for (int i = 0; i < count; ++i) {}" can't overflow so the optimization is always valid. "int count; … for (int i = 0; i < count; i += 2) {}" is more of a problem. I don't think "have a 64-bit int type" is the right approach for a new language either… we should be aiming for safety. If a variable's valid values are 0-50 then its type should be "integer between 0 and 50", not "integer between 0 and UINT64_MAX". Storage size should be an implementation detail.
- plq 5y agoADA has this exact functionality: https://www.adaic.org/resources/add_content/standards/05aarm/html/AA-3-5-4.html https://www.adaic.org/resources/add_content/standards/05aarm... Look for the "range" keyword
- tsukikage 5y agoWe make a distinction between undefined and implementation defined behaviour for a reason. Saying that certain runtime behaviours result in malformed programs while being impractical to generate explicit checks for is not entirely crazy. That said, I believe the set of undefined behaviours in our current standards is much, much too large - most of these should rightly be filed in the implementation-defined category instead. It is no longer the 70s and the very same modern compilers that perform more and more extreme optimisations year on year really do not need to account for a giant zoo of quirky experimental architectures; over the decades we've basically settled on a consensus on how pointers, integers, floating-point numbers etc ought to work.
- mwcampbell 5y agoPriorities change though; in particular, security is a rising priority. So it's not inevitable that funded compiler development must focus on wringing every last optimization out of software that might perform well enough if it were simpler. Organizations like NLnet and ISRG, for example, could fund work on getting to a usable Unix-like system that's entirely compiled with one of the simpler C compilers that don't exploit undefined behavior so much. They could justify it with the argument that security is best achieved through simplicity at all levels of the stack, including a simple build toolchain.