私はこの記事を見つけましたLazy
:C#4.0での怠惰– Lazy
レイジーオブジェクトを使用して最高のパフォーマンスを実現するためのベストプラクティスは何ですか?誰かが実際のアプリケーションでの実用的な使用を私に指摘できますか?つまり、いつ使用すればよいですか。
get { if (foo == null) foo = new Foo(); return foo; }
セーフではありませんがLazy<T>
、デフォルトではスレッドセーフです。
私はこの記事を見つけましたLazy
:C#4.0での怠惰– Lazy
レイジーオブジェクトを使用して最高のパフォーマンスを実現するためのベストプラクティスは何ですか?誰かが実際のアプリケーションでの実用的な使用を私に指摘できますか?つまり、いつ使用すればよいですか。
get { if (foo == null) foo = new Foo(); return foo; }
セーフではありませんがLazy<T>
、デフォルトではスレッドセーフです。
回答:
通常は、実際に使用するときに初めてインスタンス化するときに使用します。これにより、常にコストが発生する代わりに、必要な場合/必要になるまで作成コストが遅延します。
通常、これはオブジェクトが使用される場合と使用されない場合があり、オブジェクトを構築するコストが重要な場合に適しています。
Lazy<T>
。ただし、各プロパティを作成するために、かなり簡単な線形補間(または双線形補間)を行っていますが、コストがかかります。(私が行って自分で実験することを提案するつもりですか?)
シングルトンの使用は避けるべきですが、必要な場合は、Lazy<T>
遅延のあるスレッドセーフなシングルトンを簡単に実装できます。
public sealed class Singleton
{
// Because Singleton's constructor is private, we must explicitly
// give the Lazy<Singleton> a delegate for creating the Singleton.
static readonly Lazy<Singleton> instanceHolder =
new Lazy<Singleton>(() => new Singleton());
Singleton()
{
// Explicit private constructor to prevent default public constructor.
...
}
public static Singleton Instance => instanceHolder.Value;
}
偉大な実世界の遅延ロードが便利になる場所の例は、Entity FrameworkのとNHibernateのようORMの(オブジェクト関係マッパー)です。
Name、PhoneNumber、およびOrdersのプロパティを持つエンティティCustomerがあるとします。NameとPhoneNumberは通常の文字列ですが、Ordersは、顧客がこれまでに行ったすべての注文のリストを返すナビゲーションプロパティです。
多くの場合、すべての顧客を調べ、顧客の名前と電話番号を取得して電話をかけます。これは非常にすばやく簡単なタスクですが、顧客を作成するたびに自動的に実行され、複雑な結合を行って何千もの注文を返す場合を想像してください。最悪なのは、注文を使用することさえないため、リソースを完全に浪費することです。
Orderプロパティが遅延の場合、実際に必要でない限り顧客のすべての注文を取得しないので、これは遅延読み込みに最適な場所です。Orderプロパティが辛抱強くスリープしているときに、必要なときに備えて、名前と電話番号のみを取得するCustomerオブジェクトを列挙できます。
Db.Customers.Include("Orders")
。これにより、Customer.Orders
プロパティが最初に使用されたときではなく、その時点で順序結合が実行されます。遅延読み込みは、DbContextを介して無効にすることもできます。
私はLazy<T>
、自分のコードのパフォーマンスを向上させるために(そしてそれについてもう少し学ぶために)プロパティを使用することを検討しています。私はいつそれを使うべきかについての答えを探してここに来ましたが、どこにでも行くようなフレーズがあるようです:
特にプログラムの存続期間中にそのような作成または実行が発生しない可能性がある場合は、遅延初期化を使用して、大きなオブジェクトまたはリソースを大量に消費するオブジェクトの作成、またはリソースを大量に消費するタスクの実行を延期します。
以下からのMSDNのLazy <T>クラス
どこに線を引くかわからないので、少し混乱しています。たとえば、線形補間はかなり高速な計算と見なしていますが、実行する必要がない場合は、遅延初期化を行うことで回避でき、価値がありますか?
結局私は自分のテストを試すことにし、結果をここで共有したいと思いました。残念ながら、私は実際にはこの種のテストを行うことの専門家ではないので、改善を提案するコメントをいただければ幸いです。
説明
私の場合、特に興味があったのは、レイジープロパティが多くの補間を行うコードの一部(ほとんどが未使用)を改善するのに役立つかどうかを確認することで、3つのアプローチを比較するテストを作成しました。
アプローチごとに20のテストプロパティ(tプロパティと呼ぶことにします)を含む個別のテストクラスを作成しました。
テスト結果はmsで測定され、50回のインスタンス化または20回のプロパティ取得の平均です。その後、各テストを5回実行しました。
テスト1の結果:インスタンス化(50回のインスタンス化の平均)
Class 1 2 3 4 5 Avg % ------------------------------------------------------------------------ GetInterp 0.005668 0.005722 0.006704 0.006652 0.005572 0.0060636 6.72 InitInterp 0.08481 0.084908 0.099328 0.098626 0.083774 0.0902892 100.00 InitLazy 0.058436 0.05891 0.068046 0.068108 0.060648 0.0628296 69.59
テスト2の結果:最初の取得(平均20件のプロパティ取得)
Class 1 2 3 4 5 Avg % ------------------------------------------------------------------------ GetInterp 0.263 0.268725 0.31373 0.263745 0.279675 0.277775 54.38 InitInterp 0.16316 0.161845 0.18675 0.163535 0.173625 0.169783 33.24 InitLazy 0.46932 0.55299 0.54726 0.47878 0.505635 0.510797 100.00
テスト3の結果: 2回目の取得(平均20件のプロパティ取得)
Class 1 2 3 4 5 Avg % ------------------------------------------------------------------------ GetInterp 0.08184 0.129325 0.112035 0.097575 0.098695 0.103894 85.30 InitInterp 0.102755 0.128865 0.111335 0.10137 0.106045 0.110074 90.37 InitLazy 0.19603 0.105715 0.107975 0.10034 0.098935 0.121799 100.00
観察
GetInterp
何も実行しないため、期待どおりにインスタンス化するのが最も高速です。遅延プロパティを設定する際のオーバーヘッドが線形補間計算よりも速いことを示唆するInitLazy
よりも、インスタンス化の方InitInterp
が高速です。ただし、InitInterp
20個の線形補間(tプロパティを設定するため)を実行する必要があるため、ここでは少し混乱していますが、インスタンス化(テスト1)には0.09ミリ秒しかGetInterp
かかりません。初回(テスト2)、0.1ミリ秒で2回目(テスト3)。
最初のプロパティを取得するのにInitLazy
比べGetInterp
て約2倍の時間がかかりますInitInterp
が、インスタンス化中にプロパティにデータが入力されるため、最速です。(少なくとも、それは本来実行されるべきことでしたが、インスタンス化の結果が単一の線形補間よりもはるかに速いのはなぜですか?正確にこれらの補間を実行しているのはいつですか?)
残念ながら、私のテストでは自動コード最適化が行われているようです。それは取るべきGetInterp
財産、それは二回目と同様に最初の時間を取得するために同じ時間を、それはより速くより2倍として表示されます。この最適化は他のクラスにも影響を与えているようです。それらはすべてテスト3にほぼ同じ時間を費やしているためです。しかし、このような最適化は、私自身の製品コードでも行われる可能性があり、これも重要な考慮事項となる場合があります。
結論
一部の結果は予想どおりですが、おそらくコードの最適化に起因する、非常に興味深い予期しない結果もあります。コンストラクターで多くの作業を行っているように見えるクラスの場合でも、インスタンス化の結果は、doubleプロパティを取得する場合と比較して、作成が非常に迅速であることを示しています。この分野の専門家はコメントと調査をより徹底的に行うことができるかもしれませんが、私の個人的な感覚では、このテストをもう一度実行する必要がありますが、そこではどのような最適化が行われているのかを調べるために、本番のコードで行います。しかし、私はそれInitInterp
が進むべき道であると期待しています。
lazy
いくつかの余分な簿記を行う必要があるためInitLazy
、他のソリューションよりも多くのメモリを使用します。また、アクセスごとにパフォーマンスにわずかな影響が出る可能性がありますが、すでに値があるかどうかを確認します。巧妙なトリックはそのオーバーヘッドを取り除くことができますが、ILでの特別なサポートが必要になります。(Haskellはこれをすべての遅延値を関数呼び出しにすることで行います。値が生成されると、毎回その値を返す関数に置き換えられます。)
Mathewが投稿した例を指摘します
public sealed class Singleton
{
// Because Singleton's constructor is private, we must explicitly
// give the Lazy<Singleton> a delegate for creating the Singleton.
private static readonly Lazy<Singleton> instanceHolder =
new Lazy<Singleton>(() => new Singleton());
private Singleton()
{
...
}
public static Singleton Instance
{
get { return instanceHolder.Value; }
}
}
レイジーが生まれる前は、次のようにしていたでしょう。
private static object lockingObject = new object();
public static LazySample InstanceCreation()
{
if(lazilyInitObject == null)
{
lock (lockingObject)
{
if(lazilyInitObject == null)
{
lazilyInitObject = new LazySample ();
}
}
}
return lazilyInitObject ;
}
MSDNから:
Lazyのインスタンスを使用して、大きなプログラムやリソースを大量に消費するオブジェクトの作成や、リソースを大量に消費するタスクの実行を延期します。特に、プログラムの存続期間中にそのような作成や実行が発生しない可能性がある場合はそうです。
James Michael Hareの回答に加えて、Lazyは値のスレッドセーフな初期化を提供します。このクラスのさまざまな種類のスレッドセーフモードを説明するLazyThreadSafetyMode列挙のMSDNエントリをご覧ください。
遅延読み込みアーキテクチャを理解するには、この例を見る必要があります
private readonly Lazy<List<int>> list = new Lazy<List<int>>(() =>
{
List<int> configList = new List<int>(Thread.CurrentThread.ManagedThreadId);
return configList;
});
public void Execute()
{
list.Value.Add(0);
if (list.IsValueCreated)
{
list.Value.Add(1);
list.Value.Add(2);
foreach (var item in list.Value)
{
Console.WriteLine(item);
}
}
else
{
Console.WriteLine("Value not created");
}
}
->出力-> 0 1 2
しかし、このコードが「list.Value.Add(0);」と書かない場合
出力->値は作成されません
get { if (foo == null) foo = new Foo(); return foo; }
。そして、それを使用する可能性のある場所は無数にあります...