Why this way of using a conditional statement will give better performance than usual if / else

There are 3 codes that do the same, however their performance differs from the x64 version.

I guess this is because of Branch Prediction . Could anyone stop in more detail?

Conditional: takes 41 ms

for (int j = 0; j < 10000; j++)
{
    ret = (j * 11 / 3 % 5) + (ret % 11 == 4 ? 2 : 1);
}

Normal: takes 51 ms

for (int j = 0; j < 10000; j++)
{
    if (ret % 11 == 4)
    {
        ret = 2 + (j * 11 / 3 % 5);
    }
    else
    {
        ret = 1 + (j * 11 / 3 % 5);
    }
}

Cached: takes 44 ms

for (int j = 0; j < 10000; j++)
{
    var tmp = j * 11 / 3 % 5;
    if (ret % 11 == 4)
    {
        ret = 2 + tmp;
    }
    else
    {
        ret = 1 + tmp;
    }
}
+1
source share
2 answers

EDIT 3 If I go back to the original test with a fixed synchronization error, I get a similar result.

Conditional took 67 ms

Normal took 83 ms

Caching took 73 ms

, Ternary/Conditional for. , , , if Ternary/Conditional, , , / , .

, if. , , .

2

infact a

Stopwatch reset , Stopwatch.Restart Stopwatch.Start 1000000000,

22404

21403

, CIL. , "" if , Ternary\Conditional, .

, , , / , if. .

, . CIL, if, , , -, , 3 1 2 , ?.


,

using System.Diagnostics;

class Program
{
    static void Main()
    {
        var stopwatch = new Stopwatch();

        var conditional = Conditional(10);
        var normal = Normal(10);
        var cached = Cached(10);

        if (new[] { conditional, normal }.Any(x => x != cached))
        {
            throw new Exception();
        }

        stopwatch.Start();
        conditional = Conditional(10000000);
        stopWatch.Stop();
        Console.WriteLine(
            "Conditional took {0}ms", 
            stopwatch.ElapsedMilliseconds);

        ////stopwatch.Start(); incorrect
        stopwatch.Restart();
        normal = Normal(10000000);
        stopWatch.Stop();
        Console.WriteLine(
            "Normal took {0}ms", 
            stopwatch.ElapsedMilliseconds);

        ////stopwatch.Start(); incorrect
        stopwatch.Restart();
        cached = Cached(10000000);
        stopWatch.Stop();
        Console.WriteLine(
            "Cached took {0}ms", 
            stopwatch.ElapsedMilliseconds);

        if (new[] { conditional, normal }.Any(x => x != cached))
        {
            throw new Exception();
        }

        Console.ReadKey();
    }

    static int Conditional(int iterations)
    {
        var ret = 0;
        for (int j = 0; j < iterations; j++)
        {
            ret = (j * 11 / 3 % 5) + (ret % 11 == 4 ? 2 : 1);
        }

        return ret;
    }

    static int Normal(int iterations)
    {
        var ret = 0;
        for (int j = 0; j < iterations; j++)
        {
            if (ret % 11 == 4)
            {
                ret = 2 + (j * 11 / 3 % 5);
            }
            else
            {
                ret = 1 + (j * 11 / 3 % 5);
            }
        }

        return ret;
    }

    static int Cached(int iterations)
    {
        var ret = 0;
        for (int j = 0; j < iterations; j++)
        {
            var tmp = j * 11 / 3 % 5;
            if (ret % 11 == 4)
            {
                ret = 2 + tmp;
            }
            else
            {
                ret = 1 + tmp;
            }
        }

        return ret;
    }
}

x64 . ,

65

148

217

.


ILDASM , , CIL , Conditional .


"", . , , , .


even , .

static int Conditional(bool condition, int value)
{
    return value + (condition ? 2 : 1);
}

static int Normal(bool condition, int value)
{
    if (condition)
    {
        return 2 + value;
    }

    return 1 + value;
}

static int Looper(int iterations, Func<bool, int, int> operation)
{
    var ret = 0;
    for (var j = 0; j < iterations; j++)
    {
        var condition = ret % 11 == 4;
        var value = ((j * 11) / 3) % 5;
        ret = operation(condition, value);
    }
}

- , , IL .

... Conditional ...
{
     : ldarg.1      // push second arg
     : ldarg.0      // push first arg
     : brtrue.s T   // if first arg is true jump to T
     : ldc.i4.1     // push int32(1)
     : br.s F       // jump to F
    T: ldc.i4.2     // push int32(2)
    F: add          // add either 1 or 2 to second arg
     : ret          // return result
}

... Normal ...
{
     : ldarg.0      // push first arg
     : brfalse.s F  // if first arg is false jump to F
     : ldc.i4.2     // push int32(2)
     : ldarg.1      // push second arg
     : add          // add second arg to 2
     : ret          // return result
    F: ldc.i4.1     // push int32(1)
     : ldarg.1      // push second arg
     : add          // add second arg to 1
     : ret          // return result
}
+2

3 , ,

, ? , .

, - Branch Prediction.

, , . , ?: .
, 1 , .

, , /. .

+1

All Articles