c# - 高速化 - visual studio 重い メモリ




フィールド対プロパティ。 パフォーマンスの最適化 (4)

唯一の説明は、CLRが追加の最適化を行うということです(私がここで間違っていれば私に合っています)。

はい、インライン展開と呼ばれます。 これはコンパイラ(マシンコードレベル - JIT)で行われます。 getter / setterが簡単なので(つまり非常に単純なコード)、メソッド呼び出しは破壊され、getter / setterは周囲のコードで記述されます。

これは、デバッグをサポートするためにデバッグモードでは発生しません(ゲッターやセッターにブレークポイントを設定する機能)。

ビジュアルスタジオでは、デバッガでそれを行う方法はありません。 リリースをコンパイルし、デバッガを接続せずに実行すると、完全な最適化が得られます。

私はそれらのプロパティがより洗練された方法で同じ方法で最適化される実際のアプリケーションではそれを信じていません。

世界は間違った錯覚でいっぱいです。 それらはまだ簡単なので最適化されます(つまり、単純なコードなのでインライン展開されます)。

この質問はパフォーマンスのみに関連していますのでご注意ください。 デザインのガイドライン、哲学、互換性、移植性、そして純粋なパフォーマンスに関係しないものを飛ばすことができます。 ありがとうございました。

今質問に。 私はいつもC#のgetters / setterは本当に偽装のメソッドなので、publicフィールドを読むことはgetterを呼び出すよりも速くなければならないと仮定しました。

だから私はテストを行ったことを確認する(以下のコード)。 しかし、このテストでは、Visual Studio内から実行すると、期待される結果しか得られません(つまり、 フィールドはゲッターよりも34%高速です )。

コマンドラインからそれを実行すると、ほぼ同じタイミングが表示されます...

唯一の説明は、CLRが追加の最適化を行うことです(私がここで間違っていれば修正します)。

私はそれらのプロパティがより洗練された方法で同じ方法で最適化される実際のアプリケーションではそれを信じていません。

実際の生活の中では、畑よりもプロパティが遅いというアイデアを証明したり、反証したりしてください。

質問は、パブリックフィールドがゲッターよりも大きくなるように、CLR変更動作を行うためにテストクラスをどのように変更する必要があるかです。 または、内部ロジックのないプロパティはフィールドと同じように動作します(少なくともゲッ​​ターでは)

編集:私はリリースx64のビルドについて話しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PropertyVsField
{
    class Program
    {
        static int LEN = 20000000;
        static void Main(string[] args)
        {
            List<A> a = new List<A>(LEN);
            List<B> b = new List<B>(LEN);

            Random r = new Random(DateTime.Now.Millisecond);

            for (int i = 0; i < LEN; i++)
            {
                double p = r.NextDouble();
                a.Add(new A() { P = p });
                b.Add(new B() { P = p });
            }

            Stopwatch sw = new Stopwatch();

            double d = 0.0;

            sw.Restart();
            for (int i = 0; i < LEN; i++)
            {
                d += a[i].P;
            }

            sw.Stop();

            Console.WriteLine("auto getter. {0}. {1}.", sw.ElapsedTicks, d);

            sw.Restart();
            for (int i = 0; i < LEN; i++)
            {
                d += b[i].P;
            }

            sw.Stop();

            Console.WriteLine("      field. {0}. {1}.", sw.ElapsedTicks, d);

            Console.ReadLine();
        }
    }

    class A
    {
        public double P { get; set; }
    }
    class B
    {
        public double P;
    }
}

JITは、内部メトリクスによって決定されるすべてのメソッド(ゲッターだけでなく)をより高速にインライン展開します。 標準的なプロパティがreturn _Property; あらゆる場合にインラインで表示されます。

異なる動作が見られる理由は、デバッガが接続されたデバッグモードでは、JITが重大な障害を受けて、スタック位置がコードから期待されるものと一致することが保証されます。

また、パフォーマンスの第一のルールを忘れている、テストビートの思考。 例えば、クイックソートは挿入ソートよりも漸近的に速いものですが、挿入ソートは実際には非常に小さな入力に対して高速です。


Properties vs Fieldsを見てください- なぜそれは重要ですか? (Jonathan Aneja) MSDNのVBチームメンバーの1人からのブログ記事。 彼はプロパティとフィールドの引数について概説し、また、以下のような簡単なプロパティについても説明します:

CLRのJust-In-Time(JIT)コンパイラがプロパティのアクセスをインライン化し、次のようなコードを生成するので、プロパティの上にフィールドを使用することについて聞いた1つの引数は、「フィールドは高速です」ですが、フィールドへの直接アクセスとして効率的です。


既に述べたように、getterはインライン展開されています。

インライン展開を避けたい場合は、

  • 自動プロパティを手動プロパティに置き換えます。

    class A 
    {
        private double p;
        public double P
        {
            get { return p; }
            set { p = value; }
        }
    } 
    
  • ゲッターをインライン化しないようにコンパイラに指示します(または、もし気に入ったら、両方とも)。

            [MethodImpl(MethodImplOptions.NoInlining)]
            get { return p; }
    

最初の変更ではパフォーマンスに違いはありませんが、2回目の変更ではメソッド呼び出しのオーバーヘッドが明確になることに注意してください。

手動プロパティ:

auto getter. 519005. 10000971,0237547.
      field. 514235. 20001942,0475098.

ゲッターのインライン化はありません:

auto getter. 785997. 10000476,0385552.
      field. 531552. 20000952,077111.




optimization