What is the difference between using a comparison with >= or simply >?

Asked

Viewed 283 times

11

Imagine the following scenario.

int i = 2;
if(i >= 2)
{
    //....
}

When we could just summarize to;

int i = 2;
if(i > 1)
{
    //....
}

My doubts with these two expressions are as follows::

When a low-level language interprets this scenario it makes two comparisons if it is > (major) or = (equal) to the value?

If yes, the short form if(i > 1) would be better in terms of processing or would give in the same?

Anyway, I’m specifying C# as a language, but if there’s any difference between others and you want to complement it, it would be nice.

  • 1

    Perhaps there will be some crazy answers from people who have not read the question. Anyway, I found interesting this +1

  • Thinking of this example of yours, the only reason to use the >=, would be to "facilitate" understanding. But I believe that the right thing would be not to use magic numbers, and that breaks with the argument of "facilitating" understanding. Thinking about it, it seems as if the >= is unnecessary. What an intriguing question. + 1

  • Excellent question, great answers. It is the same as asking why to use "int" and not "Int32" or when to use "==" and not "Equals". There are controversies! My opinion: from a practical point of view write the code that is easier to understand.

  • The analogy was not happy, it is not the same thing. Opinions do not help answer, give facts.

2 answers

13


Comparing by greater or equal can have a cost in processor cycles greater than just comparing with greater. As long as this operation is actually done.

Understand that it is common that in machine language you have an instruction to make one or the other operator. You don’t need more than one of them to find the result. But that doesn’t mean that the cost is the same.

One thing that a lot of people don’t understand is that code size and cost to execute are very different things. If short code were faster, just write Execute() and would do everything you want as quickly as possible :) There is no magic, even at a low level that is all bit manipulation some operations need a few steps to get the result, just like you would on paper or in your head. These steps are execution cycles (each processor’s Hertz). The thing is a bit more complicated than that because of the pipeling processor that can perform various things together (nothing to do with thread), but there’s no point in getting into it here.

It is also important to understand that the compiler can in some cases optimize high-level code to change the operator and use the one that will run faster. You don’t need to know that. Of course he can not always ensure that a change will not affect the execution and only with guarantees that the optimization will not create problem is that he would do. I’m not saying that the C# compiler does, but could do.

See the two codes as it would look on CIL (is not yet the machine code, which depends on where it will be executed, but already gives a basis). The Jitter could optimize at the time of execution:

.method public hidebysig static void  Main(string[] args) cil managed {
  // 
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1,
           bool V_2,
           bool V_3)
  IL_0000:  nop
  IL_0001:  ldc.i4.2
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  ldc.i4.2
  IL_0005:  clt       //<=========== operador aqui
  IL_0007:  ldc.i4.0
  IL_0008:  ceq       //<=========== operador aqui
  IL_000a:  stloc.2
  IL_000b:  ldloc.2
  IL_000c:  brfalse.s  IL_001b

  IL_000e:  nop
  IL_000f:  ldstr      "Maior ou igual"
  IL_0014:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0019:  nop
  IL_001a:  nop
  IL_001b:  ldc.i4.2
  IL_001c:  stloc.1
  IL_001d:  ldloc.1
  IL_001e:  ldc.i4.1
  IL_001f:  cgt       //<=========== operador aqui
  IL_0021:  stloc.3
  IL_0022:  ldloc.3
  IL_0023:  brfalse.s  IL_0032

  IL_0025:  nop
  IL_0026:  ldstr      "Maior"
  IL_002b:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0030:  nop
  IL_0031:  nop
  IL_0032:  ret
}

Which was compiled from:

public static void Main (string [] args) {
    int x = 2;
    if (x >= 2) WriteLine("Maior ou igual");
    int y = 2;
    if (y > 1) WriteLine("Maior");
}

I put in the Github for future reference.

Note that CIL preferred to have two instructions to perform the operation (clt and ceq). This does not mean that the machine code will do the same, it depends on how Jitter will create the native code. Note also that he preferred to invert the comparison to give the desired result.

It is likely that using the simple operator gives a better performance, but this is not guaranteed. There are other more important things to optimize that will give greater gain. Much more.

Obviously that goes for int, if it has decimal part it is clear that they do not do the same thing. So I usually try to use the most appropriate semantics and only worry about the performance in case so if I measure that accurate have this minimum gain of a few cycles.

I was researching and saw that in most current architectures (in my time it did) it makes no difference to use one operator or the other, they perform in the same amount of cycles. It is not easy to guarantee anything because even the processor does optimizations.

I did some tests that are not the ideals (I’m not even going to post it because it tests other things than this operation) and the only conclusion I’ve had is that it really doesn’t make a difference. There is a case that has come to give huge differences to one side or the other, which shows that even the momentary environment influences more than the individual operation. Where it will be used will also influence. Today except in very heavy mathematical calculations what runs on the processor makes very little difference, expensive is accessing memory, that’s what you have to worry about.

Something tells me that these differences have to do even indirectly with the effect of Spectre and Meltdown.

Has a question in the OS who speaks of it in detail. Don’t know English? This subject shouldn’t interest you then.

  • I often see this type of code inside loops that in this case would be some cycles multiplied by the amount of loop

  • 1

    Or the optimizations, even the processor, can take care of that, so I talked about measuring. I did some quick tests here, no scientific rigor, and they gave results so inconsistent that I wouldn’t know which is better.

  • Well, anyway it was good to know that.

3

There is another view that @Maniero did not mention.

Imagine you have a data structure with maximum capacity. And you want to check if the list has exceeded that capacity:

var list = new List<int>(5);
list.Add(1);//... por ai fora
if(list.Count > list.Capacity){
    //excedeu a capacidade...
}

So far all right used the sign > and has the expected behavior. Now imagine that for some reason you want to check whether the capacity is greater or equal, using only the higher signal the code would look like this

if(list.Count > list.Capacity - 1){
    //atingiu a capacidade...
}

It is in this scenario that the sign of >= we need.

Your argument is that the signal >= is computationally more painful and therefore must use >.

Here your argument falls to earth for you have to make a comparison and a subtraction with 1. The most suitable code in this case would be

if(list.Count >= list.Capacity){
    //atingiu a capacidade...
}
  • The example is not the happiest but I think you can get an idea that this signal is also important

  • Yes is a good example, but in my case I’m only thinking of variables with fixed values.

  • @Gokussjgod In this case see the reply of the bigown :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.