7

Imagine I have a program that needs to check if a variable i is greater than zero. i is always positive, so saying that i > 0 is equivalent to saying i != 0.

Is there a performance difference between those two expressions and why?

I am aware that there isn't a noticable performance difference, this is more of a philosophical question.

PLPeeters
  • 1,009
  • 12
  • 26
  • 12
    No, there isn’t, stop worrying about this kind of thing – Ry- Jul 12 '14 at 21:59
  • 1
    I know the difference, if there is one, is probably negligible, but everytime I write one of those conditions, the question still pops into my head. – PLPeeters Jul 12 '14 at 22:01
  • If there ever was a performance difference, it was probably barely measurable. Now, both compilers and CPUs are so incredibly good, I guarantee you'll see no difference. – Michael Petrotta Jul 12 '14 at 22:01
  • 1
    I'm aware there is no visible difference, this is more a kind of philosophical question. – PLPeeters Jul 12 '14 at 22:02
  • There is no difference that you can measure with this kind of comparison, so don't worry about it. The majority of your performance problems will be [a] loops, [b] I/O. Concentrate on these areas. – Burhan Khalid Jul 12 '14 at 22:02
  • Related: https://stackoverflow.com/questions/24581474/pros-and-cons-of-i-n-vs-i-n-in-an-int-for-loop – Ry- Jul 12 '14 at 22:03
  • Sorry about the duplicate by the way @false. I was pretty sure this had to be on here somewhere, but my searches somehow turned up nothing. – PLPeeters Jul 12 '14 at 22:03
  • No, I’m sorry, it’s not *really* a duplicate. You’re not specifically asking about a loop, so… – Ry- Jul 12 '14 at 22:04
  • Yeah, but it's the same idea. The fact that it is or not in a loop doesn't affect the answer. – PLPeeters Jul 12 '14 at 22:06
  • There are some very rare cases where such things do make a difference (if it's the innermost loop and bottleneck of your heavy duty number crunching algorithm or graphics driver, it does make a difference if for example your inner loop has 5 or 6 machine instructions - a 20% difference in speed), but if the QA asks this question it means this is not the case here. – vsz Jul 12 '14 at 22:07
  • 1
    Do you even know for what instruction set you are asking the question? For what processor model? – Pascal Cuoq Jul 12 '14 at 22:08
  • @vsz So you're saying that, strictly speaking, there is a difference? – PLPeeters Jul 12 '14 at 22:09
  • @PLPeeters: I think it's safe to say `!=` will *never* be slower than `>`. – user541686 Jul 12 '14 at 22:09
  • @PLPeeters : no, I mean there might be very specific situations where there is a difference, but it's unlikely to be in your case, unless you are writing a device driver or other machine-level stuff. – vsz Jul 12 '14 at 22:10
  • @BenVoight Thanks, that type of answer was exactly what I was looking for. – PLPeeters Jul 12 '14 at 22:15
  • @Mehrdad, I don't think you're right about that. Calculating `-(x != 0)`, a test and a negation, may well be ever so slightly slower than calculating `-(x < 0)`, which can be written `x >> 63` (or whatever the appropriate shift is). It all depends on context and also CPU architecture. – dfeuer Jul 12 '14 at 22:47
  • @dfeuer: I guess I meant in a context where you're actually using the condition for branching, not in some scenario where you're using the boolean for integer arithmetic or something obscure like that. – user541686 Jul 12 '14 at 22:54
  • @Mehrdad, it's not really so obscure, I don't think. Any time you write something that looks like (or that can be rewritten to look like) `cond ? expr1 : expr2`, a sufficiently good compiler/backend like LLVM will do its best to tear your code apart in disgustingly incomprehensible ways to make it fast. – dfeuer Jul 12 '14 at 23:12
  • @dfeuer: Do you have an actual example in mind (where the Boolean is used for branching instead of arithmetic)? – user541686 Jul 12 '14 at 23:15
  • @Mehrdad, I'm not sure what you mean. Obviously, if you're just using the result for loop termination or some such, it's pretty darn unlikely to matter. But in many other contexts, code may be written in a "branching" form that can be compiled to a non-branching form that may or may not be better. – dfeuer Jul 12 '14 at 23:21
  • @dfeuer: I mean if you look at it that way, then you can't *ever* claim anything is faster than anything else, because the compiler has full discretion in deciding how to compile your code, and you never know, maybe you'll get lucky once in a blue moon and the compiler will do something crazy that you never dreamed of. With that kind of reasoning (which is "technically" correct) you can't draw meaningful conclusions about what's faster and what's not. – user541686 Jul 12 '14 at 23:52
  • @Mehrdad, to a certain extent that is correct. A programmer who cares about performance needs to have a sense of what kinds of large-scale optimizations their compiler performs, and in some cases a sense of what kinds of small-scale changes may matter. A C programmer needs to understand the advantage (sometimes) of using a signed loop variable instead of an unsigned one. A Haskell programmer has to know the advantage (sometimes) of using functions that the compiler's rewrite rules will fuse together rather than hand-written recursive ones. – dfeuer Jul 13 '14 at 00:07
  • @dfeuer: Exactly, and what I'm trying to say is precisely that I don't know of any situation in which `!=` can be *expected* to be better than `>` or `<`. Sure, you might get lucky because of a compiler fluke and because of how expressions happened to get rearranged due to the context, but that's luck (noise), not something you can systematically expect. So that's why I say `!=` is never slower. But if you have a counterexample that you can actually *justify* expecting to be faster (i.e. not just a random fluke), I'd be more than happy to see it and learn a thing or two from it. – user541686 Jul 13 '14 at 00:13
  • Even if you would like to know, how would this be language agnostic? – nawfal Jul 17 '14 at 11:02

1 Answers1

4

I don't think it's measurably different, but contrary to popular wisdom, I'm going to tell you to use != rather than > or < on the grounds that the former is a more general operation, and if you were going to convert your code to C++ and use iterators instead of pointers, not all iterators would support < or > (but all of them would support !=).

user541686
  • 205,094
  • 128
  • 528
  • 886
  • it *would* be contrary to popular wisdom if he were using this as a check on a loop, but. – Michael Petrotta Jul 12 '14 at 22:11
  • 4
    Premature not-even-an-optimization. `!=` works with forward-iterators, but not with strides greater than 1. `<` works with large strides, but only random-access iterators. Which is preferred is very context-specific. – Ben Voigt Jul 12 '14 at 22:12
  • @MichaelPetrotta: I guess. In any case, I apply this reasoning to loops as well as non-loops, so it really doesn't make much difference to me. – user541686 Jul 12 '14 at 22:12
  • @BenVoigt: If you're working with larger stride than 1 then you're already doing it wrong with `<`; the iterator will go out of bounds an you're in undefined behavior land. – user541686 Jul 12 '14 at 22:13
  • @Mehrdad: I presume you mean overflow? But how about "print all multiples of `N` less than 10,000", where the upper bound is fixed? Then `<` is perfect. – Ben Voigt Jul 12 '14 at 22:14
  • `iterators` is an implementation thing, IMO. Once it winds down to machine code it has to be expressed as either `a < b` or `a != b`, even for iterators (have you checked the resulting assembly?) – wildplasser Jul 12 '14 at 22:15
  • @Mehrdad: To avoid iteration beyond the end, you test `while (end - it > stride)` instead of `while (it + stride < end)` – Ben Voigt Jul 12 '14 at 22:15
  • I already preempted that claim, it's wrong. – Ben Voigt Jul 12 '14 at 22:16
  • @BenVoigt: Edit: yeah you're right, sorry about that. – user541686 Jul 12 '14 at 22:17
  • pre-empted, as in, my comment above yours shows how to prevent going out-of-bounds. Also, your loop is wrong; it does go out of bounds (when `i + 2 == v.end()`, you then do `i += 2` making `i = v.end()` and test `v.end() + 2 <= v.end()`, hence UB). – Ben Voigt Jul 12 '14 at 22:20
  • @BenVoigt: By the way, your `end - it > stride` is wrong as well -- just substitute `1` for stride and your condition is equivalent to `it + 1 < end` which is not the same as the usual `it < end`. I think what I *meant* to say (because I've certainly come across this situation before, I just didn't remember the exact code, so I typo'd) was that you would write it as `for (i = v.begin(); i <= v.end() - 2; i += 2)` (assuming you know you always have one element to process which isn't my point here). i.e., you can't do it with a `<`, you need a `<=`. – user541686 Jul 12 '14 at 22:26
  • @BenVoigt: So if you wanted to fix that edge case when the vector doesn't have enough elements, your fix should be `v.end() - i >= stride`, not `v.end() - it > stride` unlike in your claim. In other words, *strict inequalities are already a poor choice when the stride is greater than `1`*, so they don't pose any issue regarding what I said. I simply typo'd when trying to explain what I meant earlier, sorry. – user541686 Jul 12 '14 at 22:35
  • @Mehrdad: It's the correct condition, and the test needs to be immediately before the increment. Yours is still wrong. Think I might write a blog post about this. Correct total is: `auto it = begin(), end = end(); if (it != end) { process(*it); while (stride < end - it) { it += stride; process(*it); } }` – Ben Voigt Jul 12 '14 at 22:36
  • @BenVoigt: I don't think mine is wrong. What part of `v.end() - i >= stride` is wrong? – user541686 Jul 12 '14 at 22:38
  • @Mehrdad: The part where you then deference `v.end()` inside the loop body – Ben Voigt Jul 12 '14 at 22:38
  • @BenVoigt: I don't understand, where do I dereference `v.end()`? The full code is `for (i = v.begin(); v.end() - i >= stride; i += stride) { foo(*i); }`, for reference. – user541686 Jul 12 '14 at 22:39
  • @Mehrdad: Well, now you terminated one iteration early, when the length isn't divisible. – Ben Voigt Jul 12 '14 at 22:40
  • @BenVoigt: No I think you're still wrong. And I didn't change anything, it's exactly the same condition as the above, I simply added back the rest of the loop. Pretty sure it's correct. – user541686 Jul 12 '14 at 22:40
  • @Mehrdad: Count from 0 to 10 with a stride of 3... (the correct result is {0, 3, 6, 9} but your result is {0, 3, 6}) – Ben Voigt Jul 12 '14 at 22:41
  • @BenVoigt: OH -- yes you're right, and it made me finally realize why I wrote that: I've been confusing the scenario of a large stride with the scenario in which we want to partition the array into chunks of a certain size... in that scenario, 9 would indeed be skipped, because there isn't a full interval of size 3 starting from 9. But I didn't realize the difference between the two when you mentioned strides; that was a really good catch, thanks! If you write a blog post you should mention both of these, because chances are people will make the same mistake I just made. – user541686 Jul 12 '14 at 22:55
  • Ahh, that does explain it. – Ben Voigt Jul 12 '14 at 23:06