37

次のSEの回答を参照してください。

書くとき

A = A ?? B;

それはと同じです

if( null != A )
    A = A;
else
    A = B;

それはそれを意味しますか

if( null == A ) A = B;

パフォーマンスの面で優先されますか?

または、同じオブジェクトが表記に含まれている場合、コンパイラがコードを最適化すると想定でき??ますか?

4

5 に答える 5

21

パフォーマンスについて心配する必要はありません。

興味がある場合は、いくつかのコードを記述してパフォーマンスをテストし、確認しStopwatchてください。ただし、違いを確認するには、数百万回の反復が必要になると思います。

また、物事の実装について決して仮定することはできません。それらは将来変更される可能性があり、仮定が無効になります。

私の仮定では、パフォーマンスの違いは非常に小さい可能性が高いです。個人的には、読みやすさのためにnull合体演算子を選びます。それは素晴らしく、凝縮されており、要点を十分に伝えています。私は時々遅延ロードチェックのためにそれを行います:

_lazyItem = _lazyItem ?? new LazyItem();
于 2012-07-10T11:49:37.757 に答える
4

のパフォーマンス??は無視できますが、副作用が無視できない場合があります。次のプログラムを検討してください。

using System;
using System.Diagnostics;
using System.Threading;

namespace TestProject
{
    class Program
    {
        private string str = "xxxxxxxxxxxxxxx";
        public string Str
        {
            get
            {
                return str;
            }
            set
            {
                if (str != value)
                {
                    str = value;
                }
                // Do some work which take 1 second
                Thread.Sleep(1000);
            }
        }

        static void Main(string[] args)
        {
            var p = new Program();

            var iterations = 10;

            var sw = new Stopwatch();
            for (int i = 0; i < iterations; i++)
            {
                if (i == 1) sw.Start();
                if (p.Str == null)
                {
                    p.Str = "yyyy";
                }
            }
            sw.Stop();
            var first = sw.Elapsed;

            sw.Reset();
            for (int i = 0; i < iterations; i++)
            {
                if (i == 1) sw.Start();
                p.Str = p.Str ?? "yyyy";
            }
            sw.Stop();
            var second = sw.Elapsed;

            Console.WriteLine(first);
            Console.WriteLine(second);

            Console.Write("Ratio: ");
            Console.WriteLine(second.TotalMilliseconds / first.TotalMilliseconds);
            Console.ReadLine();
        }

    }
}

私のPCで結果を実行します。

00:00:00.0000015
00:00:08.9995480
Ratio: 5999698.66666667

を使用した余分な割り当てがあり??、割り当てのパフォーマンスが保証されない場合があるためです。これにより、パフォーマンスの問題が発生する可能性があります。

if( null == A ) A = B;の代わりに使用したいと思いA = A ?? B;ます。

于 2017-07-13T07:18:58.223 に答える
3

私のアドバイスは、IL (中間言語) を調べて、さまざまな結果を比較することです。次に、それぞれが最終的に何になるかを正確に確認し、より最適化されたものを決定できます。しかし、Adam がコメントで述べたように、非常に小さなものでのパフォーマンスよりも読みやすさ/保守性に焦点を当てるのが最善の方法です。

編集: Visual Studio に同梱されている ILDASM.exe を使用して IL を表示し、コンパイル済みのアセンブリを開くことができます。

于 2012-07-10T11:50:57.033 に答える
3

私はC#でこれを試しました - 非常に迅速に、私のメソッドにエラーがある可能性があります. 次のコードを使用したところ、2 番目の方法は最初の方法よりも約 1.75 倍の時間がかかることがわかりました。
@Lockszmith : 以下の編集後、比率は 1.115 で、最初の実装が優先されました。

同じ時間がかかったとしても、組み込みの言語構造を個人的に使用します。組み込みの最適化がさらに組み込まれる可能性のある将来のコンパイラに対して、意図をより明確に表現できるからです。

@Lockszmith:コメントからの推奨事項を反映するようにコードを編集しました

var A = new object();
var B = new object();

var iterations = 1000000000;

var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{   
    if( i == 1 ) sw.Start();
    if (A == null)
    {
        A = B;
    }
}
sw.Stop();
var first = sw.Elapsed;

sw.Reset();
for (int i = 0; i < iterations; i++)
{
    if( i == 1 ) sw.Start();
    A = A ?? B;
}
sw.Stop();
var second = sw.Elapsed;

first.Dump();
second.Dump();

(first.TotalMilliseconds / second.TotalMilliseconds).Dump("Ratio");
于 2012-07-10T11:59:17.637 に答える
3

はい、違いがあります。

Visual Studio 2017 15.9.8ターゲティングの使用.NET Framework 4.6.1。以下のサンプルを検討してください。

static void Main(string[] args)
{
    // Make sure our null value is not optimized away by the compiler!
    var s = args.Length > 100 ? args[100] : null;
    var foo = string.Empty;
    var bar = string.Empty;

    foo = s ?? "foo";
    bar = s != null ? s : "baz";

    // Do not optimize away our stuff above!
    Console.WriteLine($"{foo} {bar}");
}

これを使用ILDasmすると、コンパイラがこれらのステートメントを同等に扱わないことが明らかになります。

?? オペレーター:

IL_001c:  dup
IL_001d:  brtrue.s   IL_0025
IL_001f:  pop
IL_0020:  ldstr      "foo"
IL_0025:  ldloc.0

条件付き null チェック:

IL_0026:  brtrue.s   IL_002f
IL_0028:  ldstr      "baz"
IL_002d:  br.s       IL_0030
IL_002f:  ldloc.0

どうやら、??演算子はスタック値の複製を暗示しています (s変数である必要がありますか?)。簡単なテストを (複数回) 実行して、どちらが速いかを感じました。で動作しstring、この特定のマシンで実行すると、次の平均値が得られました。

?? operator took:           583 ms
null-check condition took: 1045 ms

ベンチマーク サンプル コード:

static void Main(string[] args)
{
    const int loopCount = 1000000000;
    var s = args.Length > 1 ? args[1] : null; // Compiler knows 's' can be null
    int sum = 0;

    var watch = new System.Diagnostics.Stopwatch();
    watch.Start();

    for (int i = 0; i < loopCount; i++)
    {
        sum += (s ?? "o").Length;
    }

    watch.Stop();

    Console.WriteLine($"?? operator took {watch.ElapsedMilliseconds} ms");

    sum = 0;

    watch.Restart();

    for (int i = 0; i < loopCount; i++)
    {
        sum += (s != null ? s : "o").Length;
    }

    watch.Stop();

    Console.WriteLine($"null-check condition took {watch.ElapsedMilliseconds} ms");
}

答えはイエスです。違いがあります。

PS。StackOverflow は、同じ文で「パフォーマンス」と「ごくわずか」に言及している投稿に自動警告する必要があります。時間単位が無視できるかどうかを確実に知ることができるのは、元の投稿者だけです。

于 2019-03-18T16:27:20.697 に答える