5

編集

正しく使用Stopwatchし、反復回数を 2 桁増やすと、次のようになります。

3 進数は 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;
    }
}

例外は発生せず、コンソールへの出力は次のようになります。

3 進数は 107 ミリ秒かかりました

通常は230ミリ秒かかりました

2 つの論理関数の 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
}

CILTernaryは少し短いですが、どちらの関数も CIL を介した実行パスは 3 回のロード、1 回または 2 回のジャンプ、および 1 回のリターンを必要とするように思えます。Ternary関数が 2 倍高速に見えるのはなぜですか。

実際には、どちらも非常に高速であり、実際には十分に高速であることは理解していますが、その不一致を理解したいと思います。

4

1 に答える 1

8

この2つは、ほぼ同じ時間かかります。

Stopwatch単に正しく使用しなかったため、結果はオフになっています。「正常」の測定には、両方のルーパーにかかった時間が含まれます。

2番目を変更した場合

stopwatch.Start();

stopwatch.Restart();

次に、正しい結果が得られます。


ちなみに、より公平な比較を行うには、おそらく実行する必要があります

    return (condition ? value + 2 : value + 1);

それ以外の

    return value + (condition ? 2 : 1);

そのため、他の関数とまったく同じです。それ以外の場合は、条件演算子だけでなく測定しています。

于 2012-09-17T15:30:45.790 に答える