-4

遅延初期化を使用することに決めた場合、通常はその代償を払わなければなりません。

class Loafer
{
    private VeryExpensiveField field;
    private VeryExpensiveField LazyInitField()
    {
        field = new VeryExpensiveField();
        // I wanna here remove null check from accessor, but how?
        return field;
    }
    property Field { get { return field ?? LazyInitField(); } }
}

基本的に、バッキング フィールドに null/nil 値があるかどうかを毎回確認する必要があります。彼がこの習慣から逃れることができたらどうしますか?フィールドの初期化に成功すると、このチェックを取り除くことができますよね?

残念ながら、製品言語の大部分では、実行時に関数を変更することはできません。特に、関数本体から単一の命令を追加または削除することはできませんが、賢明に使用すると役立ちます。ただし、C#では、デリゲート(最初にそれらを発見し、その後、ネイティブ言語が関数ポインターを持つ理由に気づきました)とイベントメカニズムを使用して、そのような動作を模倣し、結果としてパフォーマンスが低下する可能性があります.nullチェックは下位レベルに移動するだけですが、完全には消えません。LISP や Prolog などの一部の言語では、コードを簡単に変更できますが、プロダクション言語として扱うことはほとんどできません。

Delphi や C/C++ などのネイティブ言語では、セーフとラピッドの 2 つの関数を記述し、それらをポインタで呼び出し、初期化後にこのポインタをラピッド バージョンに切り替える方がよいようです。追加の頭痛の種なしに、コンパイラまたは IDE がこれを行うためのコードを生成できるようにすることもできます。ただし、@hvd が述べたように、CPU はこれらの関数がほぼ同じであることを認識しないため、速度が低下する可能性があり、キャッシュにプリフェッチされません。

はい、私は好奇心を養うために、明示的な問題のないパフォーマンスを求めているパフォーマンスマニアです。そのような機能を開発するために、どのような一般的なアプローチが存在しますか?

4

2 に答える 2

2

実際には、遅延ツールキット フレームワークは、実際の計算とオーバーヘッドを比較すると、必ずしもそれほど重要ではありません。

多くのアプローチがあります。Lazy、自己変更ラムダ設定、ブール値、またはワークフローに最適なものを使用できます。

遅延評価ツールキットのオーバーヘッドは、計算を繰り返す場合にのみ考慮する必要があります。

マイクロ ベンチマークを使用した私のコード例では、ループ内の付随するより高価な操作のコンテキストで、遅延計算の比較オーバーヘッドを調べます。

レイジー ツールキットのオーバーヘッドは、比較的チップ ペイロードの操作と併用した場合でも無視できることがわかります。

void Main()
{
    // If the payload is small, laziness toolkit is not neglectible
    RunBenchmarks(i => i % 2 == 0, "Smaller payload");

    // Even this small string manupulation neglects overhead of laziness toolkit
    RunBenchmarks(i => i.ToString().Contains("5"), "Larger payload");
}

void RunBenchmarks(Func<int, bool> payload, string what)
{
    Console.WriteLine(what);
    var items = Enumerable.Range(0, 10000000).ToList();

    Func<Func<int, bool>> createPredicateWithBoolean = () =>
    {
        bool computed = false;
        return i => (computed || (computed = Compute())) && payload(i);
    };

    items.Count(createPredicateWithBoolean());
    var sw = Stopwatch.StartNew();
    Console.WriteLine(items.Count(createPredicateWithBoolean()));
    sw.Stop();
    Console.WriteLine("Elapsed using boolean: {0}", sw.ElapsedMilliseconds);

    Func<Func<int, bool>> createPredicate = () =>
    {
        Func<int, bool> current = i =>
        {
            var computed2 = Compute();
            current = j => computed2;
            return computed2;
        };
        return i => current(i) && payload(i);
    };

    items.Count(createPredicate());
    sw = Stopwatch.StartNew();
    Console.WriteLine(items.Count(createPredicate()));
    sw.Stop();
    Console.WriteLine("Elapsed using smart predicate: {0}", sw.ElapsedMilliseconds);
    Console.WriteLine();
}

bool Compute()
{
    return true; // not important for the exploration
}

出力:

Smaller payload
5000000
Elapsed using boolean: 161
5000000
Elapsed using smart predicate: 182

Larger payload
5217031
Elapsed using boolean: 1980
5217031
Elapsed using smart predicate: 1994
于 2015-09-29T08:38:07.253 に答える
1

FWIW Spring4D の助けを借りて、これは Delphi でも実行できます。

var
  field: Lazy<VeryExpensiveField>;
begin
  field :=
    function: VeryExpensiveField
    begin
      Result := VeryExpensiveField.Create;
    end;
于 2015-09-29T08:09:06.187 に答える