为什么条件(三元)运算符看起来明显更快?

Jod*_*ell 5 c# stopwatch

编辑

如果我Stopwatch正确使用并且迭代次数增加了两个数量级,我得到了

三元花了22404ms

正常需要21403ms

这些结果更接近我的预期,让我感觉世界上的一切都是正确的(如果不是我的代码.)

三元/条件运算符实际上稍慢.


这个问题之后,我已经部分回答了这个问题.

我在x64发布模式下编译此控制台应用程序,并启用了优化,并在没有附加调试器的情况下从命令行运行它.

using System; 
using System.Diagnostics;

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

        var ternary = Looper(10, Ternary);
        var normal = Looper(10, Normal);

        if (ternary != normal)            {
            throw new Exception();
        }

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

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

        if (ternary != normal)            {
            throw new Exception();
        }

        Console.ReadKey();
    }

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

        return result;
    }

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

    static int Normal(int iterations)
    {
        if (condition)
        {
            return = 2 + value;
        }

        return = 1 + value;
    }
}
Run Code Online (Sandbox Code Playgroud)

我没有任何异常,输出到控制台是接近,

三元花了107毫秒

正常需要230毫秒

当我为两个逻辑函数分解CIL时,我得到了这个,

... Ternary ...
{
     : 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
}
Run Code Online (Sandbox Code Playgroud)

虽然TernaryCIL有点短,但在我看来,通过CIL执行任一函数的执行路径需要3次加载和1或2次跳转以及返回.为什么Ternary函数看起来快两倍.

我认为,在实践中,它们都非常快,而且确实足够,但是,我想了解这种差异.

Tim*_*mwi 8

两者的时间差不多完全相同.

您的结果已关闭,因为您没有Stopwatch正确使用."正常"的测量包括两个弯曲者所花费的时间.

如果你改变第二个

stopwatch.Start();
Run Code Online (Sandbox Code Playgroud)

stopwatch.Restart();
Run Code Online (Sandbox Code Playgroud)

然后你会得到正确的结果.


顺便说一下,为了获得更公平的比较,你应该执行

    return (condition ? value + 2 : value + 1);
Run Code Online (Sandbox Code Playgroud)

代替

    return value + (condition ? 2 : 1);
Run Code Online (Sandbox Code Playgroud)

这样它就完全等同于另一个功能.否则,您不仅要测量条件运算符.