4 ms·
> And yet the world doesn't end and the sun will still rise tomorrow... No, you just get much slower, non-vectorized code because the compiler is forced to fo
by msbarnett 5y ago
> And yet the world doesn't end and the sun will still rise tomorrow...
No, you just get much slower, non-vectorized code because the compiler is forced to forgo an optimization if you use unsigned int as the loop bound (EDIT: tom_mellior's reply illustrates this extremely well: https://gcc.godbolt.org/z/cje6naYP4 https://gcc.godbolt.org/z/cje6naYP4)
Which is precisely the point: forcing a bunch of existing code with int loop bounds, which currently enjoys optimization, to take on the unsigned int semantics and get slower, is just going to piss off a different (and probably larger) set of people than the "compilers shouldn't assume that unsigned behaviour can't happen" set of people.
It's a tradeoff with some big downsides; this isn't the obvious win the anti-optimization crowd pretends it is.
- vyodaiken 5y agoAnd switch the i to a size_t and get vector code without the possibility of writing to random memory because your int overflows and GCC wants to pretend it cannot. This is a poorly written loop. C design model is that if it is not critical, we don't care, and if it is, the programmer should fix it so optimization can work. https://gcc.godbolt.org/z/ErMP4cn6s https://gcc.godbolt.org/z/ErMP4cn6s
- tom_mellior 5y agoYou changed writes to indices offset..offset+15 to writes to indices 0..15.
- vyodaiken 5y agothe offset is used to compute the index, not the count.
- tom_mellior 5y agoBut you're not using the offset to compute the index in bar(). void foo(int offset, int *arr) { for(int i = offset; i < (offset + 16); i++) { arr[i] = i + 32; } } If you call this with offset = 100, the arr[i] in the loop will write to arr[100], arr[101], ..., arr[115]. void bar(unsigned int offset, int *arr) { if(offset+16 < offset)fail(); for(size_t i = 0; i < 16; i++) { arr[i] = i+ offset + 32; } } If you call this with offset = 100, the arr[i] in the loop will write to arr[0], arr[1], ..., arr[15].
- vyodaiken 5y agoFixed it, but same kind of result https://gcc.godbolt.org/z/hx1zjE5xW https://gcc.godbolt.org/z/hx1zjE5xW
- tom_mellior 5y agoNice. If you like, could you explain again what the perceived difference is to adding if (offset >= INT_MAX - 16) fail(); in foo()? (I mean, besides the fact that the size of the buffer pointed to by arr is highly unlikely to agree exactly with either INT_MAX or UNIT_MAX.)
- vyodaiken 5y agoI wanted to show that we don't need UB justified deletes to get good code generation. There was no need to break all that working code when we could have just told people that size_t counters worked better in loops on x86-64 than ints. A lot of C optimization could work that way - relying on cooperation between the compiler and programmers. Java can't do that because Java programmers rely on complex abstractions that need a lot of compiler work to run fast.
- tom_mellior 5y ago> A lot of C optimization could work that way - relying on cooperation between the compiler and programmers. That is precisely how it works already. The reason your code has no bounds checks is exactly because the compiler can assume that you have done your part and ensured that all indices are in bounds. This is what "the compiler can ignore UB" is all about. The signed integer kerfuffle is just the same: The compiler assumes your cooperation in ensuring, beforehand, that your signed arithmetic never overflows. Its part of the bargain is generating the best possible code it can. Another part of its bargain is offering you the -fwrapv flag to communicate more about your expectations. A third part of the bargain is offering you sanitizers that can inform you that you have done something you probably didn't want.