1

Which one works faster - regular operators (such as <,>,=) or using (-lt, -gt, -eq)? and why?

  • 5
    Why would that even matter when they are different operators, giving different results? – Michał Politowski May 23 '15 at 8:19
  • It depends on your shell and whether the test operators are builtins or not – fpmurphy May 23 '15 at 12:45
  • Compare [ 1 = 01 ] and [ 1 -eq 01], and ask yourself if a negligible difference in speed would matter. Use the string operators for strings, the integer operators for integers, and don't worry about something that will never be a bottleneck in shell script. If comparisons constitute any significant portion of your script's total running time, you are using the wrong language. – chepner May 23 '15 at 14:25
2

Simply you can compare the performance of these operators with time command:

time [ 1 -eq 0 ] real 0m0.000s user 0m0.000s sys 0m0.000s

time [ 1 = 0 ] real 0m0.000s user 0m0.000s sys 0m0.000s

Which is real means Wall Clock time and user means User Space time and sys means System or kernel time.

Now, if you compare these operators with external time command with a script which contains following lines you will see similar output:

Sepahrad@localhost 15:17:13 [~]$cat test.sh

#!/bin/bash [ 1 -eq 0 ] echo $?

Sepahrad@localhost 15:17:13 [~]$cat test1.sh

#!/bin/bash [ 1 == 0 ] echo $?

Now run the external time command you will see similar output again:

/usr/bin/time ./test.sh or /usr/bin/time ./test1.sh

1 0.00user 0.00system 0:00.00elapsed 50%CPU (0avgtext+0avgdata 4752maxresident)k 0inputs+0outputs (0major+331minor)pagefaults 0swaps

Now let's a look at the source code of bash command: (You can find bash source code from http://www.gnu.org/software/bash/ and take a look at test.c file)

int
binary_test (op, arg1, arg2, flags)
     char *op, *arg1, *arg2;
     int flags;
{
  int patmatch;

  patmatch = (flags & TEST_PATMATCH);

  if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
    return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
  else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
    {
#if defined (HAVE_STRCOLL)
      if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
        return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
      else
#endif
        return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
    }
  else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
    return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
  else if (op[2] == 't')
    {
      switch (op[1])
        {
        case 'n': return (filecomp (arg1, arg2, NT));           /* -nt */
        case 'o': return (filecomp (arg1, arg2, OT));           /* -ot */
        case 'l': return (arithcomp (arg1, arg2, LT, flags));   /* -lt */
        case 'g': return (arithcomp (arg1, arg2, GT, flags));   /* -gt */
        }
    }
  else if (op[1] == 'e')
    {
      switch (op[2])
        {
        case 'f': return (filecomp (arg1, arg2, EF));           /* -ef */
        case 'q': return (arithcomp (arg1, arg2, EQ, flags));   /* -eq */
        }
    }
  else if (op[2] == 'e')
    {
      switch (op[1])
        {
        case 'n': return (arithcomp (arg1, arg2, NE, flags));   /* -ne */
        case 'g': return (arithcomp (arg1, arg2, GE, flags));   /* -ge */
        case 'l': return (arithcomp (arg1, arg2, LE, flags));   /* -le */
        }
    }

  return (FALSE);       /* should never get here */
}

You will see -eq will interpreted with this line:

case 'q': return (arithcomp (arg1, arg2, EQ, flags));   /* -eq

And = will interpreted with:

if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
    return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));

And > or < operators will interpreted with:

 else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
    {
#if defined (HAVE_STRCOLL)
      if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
        return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
      else
#endif
        return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
    }

And -gt or -lt will interpreted with:

    case 'l': return (arithcomp (arg1, arg2, LT, flags));   /* -lt */
    case 'g': return (arithcomp (arg1, arg2, GT, flags));   /* -gt */

Conclusion: As you see these operators interpreted with different function in bash source code but I think there is no difference in the performance as you did see in time command!

  • Your source code review is great. Your timing analysis (and conclusion) is flawed because there is no consideration given to the time taken for the overhead of starting the script and writing the status in view of the very low overall durations. Maybe running the comparison inside a x1000 loop would return more meaningful timings. You could then attempt to remove the overhead using similar code but with a no-op inside the loop. Cautious +1. – roaima May 23 '15 at 13:56
  • 3
    Amazingly, you did all this analysis and even looked at the sources, and still managed to miss the main problem with the OP's question: <, >, and = are string comparisons, while -lt, -gt, -eq are numeric comparisons. You're trying to compare apples with orange juice. – lcd047 May 23 '15 at 14:22
  • @roaima, Thanks for your attention, Yes may be there will be difference in loop with million repeats! How ever I don't think so... – Sepahrad Salour May 23 '15 at 19:13
  • 2
    @lcd047 I don't think Sepahrad did miss the point - at the end of the analysis he does point out that they are different operations. BICBW and it's useful to state it explicitly like you have done. – roaima May 23 '15 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.