4 ms·
Instructions: 1. Install Gentoo. 2. Compile with global optimization flags on for a week. 3. Compile with global optimization flags off for a week. 4. Decid
by cjhanks 10y ago
Instructions:
1. Install Gentoo.
2. Compile with global optimization flags on for a week.
3. Compile with global optimization flags off for a week.
4. Decide.
Unless you're doing numerical work (encoding/decoding/3D geometry/computer vision/...), you probably won't notice. And some desktop applications which used to work, might not work right anymore.
- CJefferson 10y agoNo, that's not the right check at all. The majority of those optimizations aren't taking advantage of undefined behaviour. Neither GCC nor clang provide a simple way of getting "no undefined behaviour" optimisations. Unfortunately, in practice it's often hard to define what that would actually mean. For example, fairly simple optimiations like optimising: int silly(int i) { int y = 3; int x[4]; x[i] = 4; return y; } To just: return 3; Technically relies on undefined behaviour -- if you compile this without optimisation then on my machine passing silly(7) returns 4 (as x[7] happens to write over y). So the question becomes, where is the "line" we want to draw on not allowing undefined behaviour optimisations? It turns out unfortunately in practice everyone seems to have a different line on what should be allowed.
- gpm 10y agoOverflow checks. Passing silly[7] should do something predictable, i.e. abort the program. This is, for example, what rust does. Of course studies have been done on how expensive overflow checking is. IIRC around 5% overhead in the most extreme examples.
- millstone 10y agoThis is an interesting example. The store to x is dead and ought to be totally elided, but Rust will still emit an "overflow check" for an array that does not exist. This overflow check dominates the cost of the function. If the optimizer determines a store is dead and can elide it, should it also elide the overflow checks? If "no," then it seems we've unnecessarily hamstrung the optimizer. If "yes," then whether a program panics is optimization-level dependent.
- petertodd 10y agoedit: re-reading your comment I suspect we're in violent agreement. :) > The store to x is dead and ought to be totally elided Yet, it's a clear sign of a bug in the program; the fact it happened to be elided in that case doesn't change the fact that it's almost certainly a bug that I want to know about. Heck, earlier today I wrote a Rust function along these lines: fn foo<A, B>(a: &A) -> &B { assert_eq!(size_of::<A>() == size_of::<B>()); unsafe { // do horrible low-level things } } The assertion is there to make sure that two generic type parameters that should have an identical size actually do. If they do in optimized builds the compiler knows the assertion is true and completely elides the assertion itself. If the sizes aren't identical, the assertion isn't elided and I'll quickly find the bug when the program crashes(1). I definitely wouldn't want that assertion to get elided just because the "horrible low-level things" happened to be categorized as undefined behavior! 1) Of course, a sufficiently smart compiler could detect that the assertion was false at compile time in this simplified example.
- nwmcsween 10y agoHow does bounds checking do with vectorization? I bet it's not 5%.
- Ded7xSEoPKYNsDd 10y agoThese people have done this work (and it's great!), and in their self-selected benchmarks programs they claim about 16% overhead: https://www.cis.upenn.edu/acg/softbound/ https://www.cis.upenn.edu/acg/softbound/ This is only memory safety, not using other optimizations related to undefined behavior will add a bunch more overhead.
- pizlonator 10y agoGlobal optimizations mostly do not rely on UB in order to get speed-ups, so this is not an A:B test of the value of UB.
- dbaupp 10y agoI think the parent meant (global (optimisation flags)), not ((global optimisation) flags). Of course, some optimisations don't rely on UB, the difference between the unoptimised and optimised code won't be entirely based on UB (a more reasonable comparison for one single type of UB would be passing -fwrapv vs. not, to stop the compiler thinking signed integers never wrap).
- DannyBee 10y ago"Global optimizations mostly do not rely on UB in order to get speed-ups, so this is not an A:B test of the value of UB. " [Citation needed] They most certainly do rely on UB to move statements past other statements, to delete dead code, etc. If you want a source: I've written all or parts of gcc's statement sinking, partial redundancy elimination, dead store elimination, alias analysis, etc. Same with LLVM. They all rely on UB being UB to be able to do their work effectively. You could never, for example, hoist a load if it wasn't UB to dereference a null pointer, unless you could prove it was non-null (and you mostly can't). You'd have to insert null checks everywhere, like java, and then try to get rid of them. You could never move anything past (up or down) an external function call if you didn't rely on UB. I could go on, but it's pretty silly. Random assertions about "optimizations taking advantage of UB don't gain performance" simply are not in accord with reality. If it wasn't true, we'd just make programming languages with no UB. It's not like compiler writers are just lazy.