3

私は現在可能な限り高速に実行されることになっているいくつかのc#コードを書いています。通常、100%で約25分間単一のコアを使用します。このコードを複数のコアで実行するメリットは、このプロジェクトを複数回同時に実行するほど大きくないため、コードをシングルコアのままにする必要があります。

問題のコードは次のとおりです。

public Double UpdateStuff(){

    ClassA[] CAArray = ClassA[*a very large number indeed*];
    Double Value = 0;
    int length = CAArray.Length;

    for (int i= 0; i< length ; i++)
        {
         Value += CAArray[i].ClassB.Value * CAArray[i].Multiplier;
        }  
    return Value;
}

プロファイラーによると、コードのこの領域はアプリケーションの負荷の78%を占めているため、最適化の候補として適しているようです。

関数がreturnタイプvoidからreturnタイプDoubleに変更されていることに注意してください。これは疑似コードであり、読みやすくするための実際のコードではありません。

明確にするために:.net、c#4.0、Visual Studio 2010、ターゲットマシン:Windows Server2008x64。

編集:さらに明確にする:このコンテキストのすべての変数はパブリックであり、プロパティではありません。CAArray [i] .ClassB.Valueの値は、ペアマッチングできないdoubleを永久に変更します。

4

11 に答える 11

11

これを削除する必要があります:

int length = CAArray.Length;

ループを次のように置き換えます。

for (int i= 0; i < CAArray.Length; i++)
{
    Value += CAArray[i].ClassB.Value * CAArray[i].Multiplier;
} 

元のコードのように長さを保存すると、実際にはC#コードの速度が低下します(直感に反します、私は知っています)。これは、forループに直接Array.Lengthがある場合、ジッターは配列境界の実行をスキップするためです。ループの各反復でチェックしてください。

また、このプロセスを並列化することを強くお勧めします。これを行う最も簡単な方法は

CAArray.AsParallel().Sum(i => i.ClassB.Value * i.Multiplier);

ただし、LINQがないとさらに高速になる可能性があります(ただし、複数のスレッドを管理するための低レベルの詳細について心配する必要があります)。

于 2012-06-26T20:34:52.740 に答える
7

1つの違いは、現在の値を保持するために、forループ内で一時変数を使用することです。

2番目の違いは、おそらくもっと重要なことですが、forループの境界にcountではなくCAArray.Lengthを配置することです。コンパイラは、そのようなループを最適化して、境界チェックを排除します。

for (int i = 0; i < CAArray.Length; i++)
{
    var curr = CAArray[i];
    Value += curr.ClassB.Value * curr.Multiplier;
}

可能であれば、ClassB、ClassB.Value、およびMultiplierプロパティをフィールドとして作成することもできます。

最後に、ソリューションのプロパティの[コードの最適化]を確認して、コンパイラがコードを最適化できるようにすることを忘れないでください。

于 2012-06-26T19:02:29.143 に答える
6

試す:

for (int i = 0; i < length; i++)
{
    var a = CAArray[i];
    Value += a.ClassB.Value * a.Multiplier;
}  
于 2012-06-26T18:54:52.207 に答える
3

ちなみに、非常に大きなセットのパフォーマンスに影響を与える可能性のある別のマイクロ最適化は、の代わりにを定義することです。fieldproperty

for (int i= 0; i< length ; i++)
{
    var a = CAArray[i];
    Value += a.ClassB.value_field * a.multiplier_field;
}  

プロパティの使用がMSからのガイドラインとして提案されている場合でも、プロパティがもたらすオーバーヘッドは非常に小さい(ただし、非常に大きなデータに関連する可能性がある)ことはよく知られています。

お役に立てれば。

于 2012-06-26T18:58:18.267 に答える
1

乗数とClassB.Valuesの重複が多い場合は、すべての個別のペアを見つけて、各ペアを1回乗算してから、このペアの出現回数を乗算します。

また、AsParallel()すべてのコアを使用します。

于 2012-06-26T18:56:46.460 に答える
1

あなたがどれだけのコントロールを持っているかはわかりませんがClassAMultiplierあなたのClassBプロパティであるため、この計算値のプロパティを持つようにClassA変更する必要があるようです。ClassA理論的には、これらのクラスはすべて、それぞれのプロパティがすでに設定された状態でインスタンス化されているため、またはthis.ClassB.Value * this.Multiplierの設定での目的の値を簡単に計算できます。このようにして、このループのコストを削減し、代わりにデータのインスタンス化に向けて移動します。これは価値のあるトレードオフですか?決定するには、アプリケーションで何が起こっているかを詳しく知る必要がありますが、これにより、この特定の機能の作業負荷が軽減されます。その後、あなたがする必要があるのは:ClassB.ValueMultiplier

public void UpdateStuff(){

    ClassA[] CAArray = ClassA[*a very large number indeed*];
    Double Value = 0;
    int length = CAArray.Length;

    for (int i= 0; i< length ; i++)
    {
        Value += CAArray[i].MultipliedClassBValue;
    }
return Value;
}

それに加えて、ここの立派な人々が思いつくことができるさらなる改善が何であれ。

于 2012-06-26T19:41:39.647 に答える
0

配列には膨大な数の要素があるため、このようなものは、ループを反復処理する他の方法よりも高速になります。

try
{
    for (int i= 0; ; i++)
    {
        var a = CAArray[i];
        Value += a.ClassB.value_field * a.multiplier_field;
    }
}
catch (IndexOutOfRangeException)
{ }

Though admittedly it looks rather ugly and is definitely not a 'pure' way of programming. But at the same time using public fields instead of properties is not pure as well.

In addition to gains from removing the exit condition, a curious bug in CLR 2.0 for X86, makes the for loop run faster if it is enclosed by try catch as Jitter in that case somehow prefers to use registers over CPU stack to store locals.

于 2012-06-28T08:06:47.183 に答える
0

もう1つのわずかな改善は、インデックスにpreincrementを使用することです。これは、postincrementは、イテレータがインクリメントする前に持っていた値を返す必要があるためです。そのため、前の値を適切な増分で変更する前にどこかにコピーする必要があるため、元に戻すことができます。

余分な作業は少なからず多額になる可能性がありますが、増分を実行して変更したばかりの値を返すことができる事前増分と比較して、確かにゼロ未満にすることはできません-コピーなし//保存//など必要です。

于 2012-06-26T19:06:55.680 に答える
0
  1. 並列化します。
  2. ループを展開してみてください。(コンパイラーはそれ自体でこれを行う場合があります。)
于 2012-06-26T19:14:08.563 に答える
0

もう1つ注意しなければならないのは、非常に大きな配列(86K以上のデータ)を頻繁に割り当て、サイズが毎回異なる場合、このオブジェクトはLOHに割り当てられるため、GCに過度の負荷をかける可能性があることです。

于 2012-06-26T19:16:02.893 に答える
-3

まず、それはvoidなので、何も返さないはずです(または、Doubleを返す必要があります)。第二に、C#は一般的にエジプトの中括弧を使用しませんが、それは実際には問題ではありません。

次に、Linqとラムダを使用してみることができます。少なくとも、よりクリーンな方がよいと思います。

public void UpdateStuff()
{
    ClassA[] CAArray = new ClassA[large_number];
    Double Value = CAArray.Select(x => x.ClassB.Value * x.Multiplier).Sum();
}
于 2012-06-26T19:00:58.313 に答える