Ternary operator is twice as slow as an if-else block?

后端 未结 10 897
无人及你
无人及你 2020-11-30 16:20

I read everywhere that ternary operator is supposed to be faster than, or at least the same as, its equivalent if-else block.

However, I di

相关标签:
10条回答
  • 2020-11-30 16:57

    Looking at the IL generated, there are 16 less operations in that than in the if/else statement (copying and pasting @JonSkeet's code). However, that doesn't mean it should be a quicker process!

    To summarise the differences in IL, the if/else method translates to pretty much the same as the C# code reads (performing the addition within the branch) whereas the conditional code loads either 2 or 3 onto the stack (depending on the value) and then adds it to value outside of the conditional.

    The other difference is the branching instruction used. The if/else method uses a brtrue (branch if true) to jump over the first condition, and an unconditional branch to jump from the first out of the if statement. The conditional code uses a bgt (branch if greater than) instead of a brtrue, which could possibly be a slower comparison.

    Also (having just read about branch prediction) there may be a performance penalty for the branch being smaller. The conditional branch only has 1 instruction within the branch but the if/else has 7. This would also explain why there's a difference between using long and int, because changing to an int reduces the number of instructions in the if/else branches by 1 (making the read-ahead less)

    0 讨论(0)
  • 2020-11-30 16:58

    I did what Jon Skeet did and ran through 1 iteration and 1,000 iterations and got a different result from both OP and Jon. In mine, the ternary is just slightly faster. Below is the exact code:

    static void runIfElse(int[] array, int iterations)
        {
            long value = 0;
            Stopwatch ifElse = new Stopwatch();
            ifElse.Start();
            for (int c = 0; c < iterations; c++)
            {
                foreach (int i in array)
                {
                    if (i > 0)
                    {
                        value += 2;
                    }
                    else
                    {
                        value += 3;
                    }
                }
            }
            ifElse.Stop();
            Console.WriteLine(String.Format("Elapsed time for If-Else: {0}", ifElse.Elapsed));
        }
    
        static void runTernary(int[] array, int iterations)
        {
            long value = 0;
            Stopwatch ternary = new Stopwatch();
            ternary.Start();
            for (int c = 0; c < iterations; c++)
            {
                foreach (int i in array)
                {
                    value += i > 0 ? 2 : 3;
                }
            }
            ternary.Stop();
    
    
            Console.WriteLine(String.Format("Elapsed time for Ternary: {0}", ternary.Elapsed));
        }
    
        static void Main(string[] args)
        {
            Random r = new Random();
            int[] array = new int[20000000];
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = r.Next(int.MinValue, int.MaxValue);
            }
            Array.Sort(array);
    
            long value = 0;
    
            runIfElse(array, 1);
            runTernary(array, 1);
            runIfElse(array, 1000);
            runTernary(array, 1000);
            
            Console.ReadLine();
        }
    

    The output from my program:

    Elapsed time for If-Else: 00:00:00.0140543

    Elapsed time for Ternary: 00:00:00.0136723

    Elapsed time for If-Else: 00:00:14.0167870

    Elapsed time for Ternary: 00:00:13.9418520

    Another run in milliseconds:

    Elapsed time for If-Else: 20

    Elapsed time for Ternary: 19

    Elapsed time for If-Else: 13854

    Elapsed time for Ternary: 13610

    This is running in 64-bit XP, and I ran without debugging.

    Edit - Running in x86:

    There's a big difference using x86. This was done without debugging on and on the same xp 64-bit machine as before, but built for x86 CPUs. This looks more like OP's.

    Elapsed time for If-Else: 18

    Elapsed time for Ternary: 35

    Elapsed time for If-Else: 20512

    Elapsed time for Ternary: 32673

    0 讨论(0)
  • 2020-11-30 17:01

    In the following code if/else seems to be roughly 1.4 times faster than the ternary operator. However, I found that introducing a temporary variable decreases the ternary operator's run time approximately 1.4 times:

    If/Else: 98 ms

    Ternary: 141 ms

    Ternary with temp var: 100 ms

    using System;
    using System.Diagnostics;
    
    namespace ConsoleApplicationTestIfElseVsTernaryOperator
    {
        class Program
        {
            static void Main(string[] args)
            {
                Random r = new Random(0);
                int[] array = new int[20000000];
                for (int i = 0; i < array.Length; i++)
                {
                    array[i] = r.Next(int.MinValue, int.MaxValue);
                }
                Array.Sort(array);
                long value;
                Stopwatch stopwatch = new Stopwatch();
    
                value = 0;
                stopwatch.Restart();
                foreach (int i in array)
                {
                    if (i > 0)
                    {
                        value += 2;
                    }
                    else
                    {
                        value += 3;
                    }
                    // 98 ms
                }
                stopwatch.Stop();
                Console.WriteLine("If/Else: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
    
                value = 0;
                stopwatch.Restart();
                foreach (int i in array)
                {
                    value += (i > 0) ? 2 : 3; 
                    // 141 ms
                }
    
                stopwatch.Stop();
                Console.WriteLine("Ternary: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
    
                value = 0;
                int tempVar = 0;
                stopwatch.Restart();
                foreach (int i in array)
                {
                    tempVar = (i > 0) ? 2 : 3;
                    value += tempVar; 
                    // 100ms
                }
                stopwatch.Stop();
                Console.WriteLine("Ternary with temp var: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
    
                Console.ReadKey(true);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 17:04

    The assembler code generated will tell the story:

    a = (b > c) ? 1 : 0;
    

    Generates:

    mov  edx, DWORD PTR a[rip]
    mov  eax, DWORD PTR b[rip]
    cmp  edx, eax
    setg al
    

    Whereas:

    if (a > b) printf("a");
    else printf("b");
    

    Generates:

    mov edx, DWORD PTR a[rip]
    mov eax, DWORD PTR b[rip]
    cmp edx, eax
    jle .L4
        ;printf a
    jmp .L5
    .L4:
        ;printf b
    .L5:
    

    So the ternary can be shorter and faster simply due to using fewer instructions and no jumps if you are looking for true/false. If you use values other than 1 and 0, you will get the same code as an if/else, for example:

    a = (b > c) ? 2 : 3;
    

    Generates:

    mov edx, DWORD PTR b[rip]
    mov eax, DWORD PTR c[rip]
    cmp edx, eax
    jle .L6
        mov eax, 2
    jmp .L7
    .L6:
        mov eax, 3
    .L7:
    

    Which is the same as the if/else.

    0 讨论(0)
提交回复
热议问题