4 ms·
> ...another pass might notice the INT_MAX+1 and replace it by unreachable since UB code is “by definition” unreachable... I would love to see the concrete exa
by saurik 2y ago
> ...another pass might notice the INT_MAX+1 and replace it by unreachable since UB code is “by definition” unreachable...
I would love to see the concrete example where this actually makes the program faster, to give me a better understanding of what I'm up against if I prefer to make integer field arithmetic well-defined.
AFAIK, the main reason these loops are so frustrating in C is because no one wanted to bite the bullet and insist that fast loops all use size_t instead of int--as otherwise on systems where int isn't native (which is, today, most of them) you have to emulate the wrap by adding a mask to the iteration--and so the compilers started inferring stuff and the language spec got damaged to let them work around it instead.
- gpm 2y agoFor what it's worth rust has made all integer arithmetic well-defined unless you explicitly opt in to the alternative on a case by case basis. Opting into the alternative is not something I've seen people having to do for performance. Specifically in release builds it's defined to be 2s complement arithmetic. In debug builds it's defined to panic (in non rust speak that is basically throw an exception) on overflows, and the language theoretically reserves the right to change the behavior on release builds to do the same, but that hasn't been done because the exception route does actually incur a non-negligible performance penalty.
- steveklabnik 2y agoI agree with you that I don't see people opting in for performance, but it's also the case that C relies on this behavior a lot more than Rust does, since it doesn't have iterators. If more Rust programmers wrote "raw" for loops, maybe the overhead would be more significant. I don't know if there's any way to qualify this. Also, it' not done by default because it's believed to incur the penalty. We didn't try to do any significant analysis here, mostly relying on the widespread belief. If we wanted to go against the grain, we'd have to demonstrate that it's incorrect, and that's difficult, time consuming, and there's other things to spend time on. Google recently demonstrated pretty convincingly that bounds checks on array access in their C and C++ code has a very minor penalty that's absolutely worth it. I wonder if maybe we'll see something similar with this semantic in the future. I think it's lower priority because it's not a safety issue on its own to have the "maybe this, maybe that" behavior, since it's not undefined behavior.
- tialaramex 2y agoI would imagine it wouldn't be too hard to collect this information because you can just change the setting on a project and report back whether that's no significant difference, broken (indicating your Rust is wrong, you should fix that) a little slower or unacceptably slower. Actually that information might help inform people choosing this setting for a new project. I think many assume that Wrapping is the conservative choice but actually the Strict / panic semantic is going to be better for at least some of those people if they could afford it and so if they knew that say 60% of projects find this works out they might try it and benefit. As a side effect a handful of people get to find out that their project has a nasty bug where it actually relies on wrapping for routine functionality but does not correctly use a wrapping type.
- gpm 2y agoAlso, for what it's worth, it's easy to change, if you're building with cargo (basically everyone does) [profile.release] overflow-checks = true There have been a few projects where I've set this for one reason or another (e.g. because I'm dealing with intentionally wrapping arithmetic and I want the program to shout if I make a mistake).
- uecker 2y agoI use signed integer because they give me nice errors on overflow, instead of hideously difficult to find wrap-around bugs.
- saurik 2y agoJust in case you are saying this to defend why you are using "int" instead of "size_t" (as, otherwise, I am not sure of your point and a I'll hope you might clarify it), you would then want to use "ssize_t"... the issue I was referring to is not about signed vs. unsigned: it is about using a type which is smaller than a native full-width machine number type (such as when int is 32-bit on a 64-bit machine), as you then have to use suboptimal instructions or even add more code to emulate the wrapping semantics.
- uecker 2y agoI understood your comment to be about well-definedness of wraparound and my point is that I do not want it to be well-defined for signed integers because it would take a tool away from me for catching bugs. This is true independent of the size of the type. For similar reasons I use int and not ssize_t because I want an error in my lifetime if there is a bug that causes my loop not to terminate. (And yes, sometimes int would be too small.) Otherwise, there are many examples where signed integers lead to better code, here is one: https://godbolt.org/z/szGd1864a https://godbolt.org/z/szGd1864a
- saurik 2y agoI am sorry, I am missing something here... are you saying that you are using a smaller type "int" so that it wraps sooner? AFAIK, the compiler is going to use the fact that wrap is undefined to promote that int to a native type and make it not wrap until the same place ssize_t would! OK, and yeah: based on your godbolt example, I do feel like you still don't understand all the ramifications, as you are relying on a bunch of undefined behavior to make your loop sort of work just because you want to avoid working with field arithmetic. Fixing the code is just as fast: int f(unsigned long n) { int sum = 0; for (unsigned long k = n; k != n + 4; ++k) sum += k; return sum; } f(unsigned long): lea eax, [6+rdi*4] ret (Of course this is still a bit awkward, as this function should also clearly return an unsigned long. I am just trying to show you the minimal patch to the loop to demonstrate why you aren't buying anything... there is no benefit to this quirk of the compiler: please fix the code.) (edit: BTW, I just looked at your about, and the fact that you are or were on the C WG is demoralizing, as I'd have thought this kind of confusion would be reason to rid this behavior from the spec... but C developers really seem to love doing signed inequalities on field elements :/.)