6

20,000 回を超える反復を行う for ループがあります。反復ごとに約 2 ~ 3 秒かかり、合計で約 20 分かかります。これを for ループに最適化する方法。.net3.5 を使用しているため、foreach の並列処理はできません。そのため、200000 個の番号を小さなチャンクに分割し、いくつかのスレッドを実装したことで、時間を 50% 短縮できるようになりました。これらの種類の for ループを最適化する他の方法はありますか?

私のサンプルコードを以下に示します

    static double sum=0.0;
    public double AsyncTest()
    {
            List<Item> ItemsList = GetItem();//around 20k items
            int count = 0;
            bool flag = true;
            var newItemsList = ItemsList.Take(62).ToList();
            while (flag)
            {
                int j=0;
                WaitHandle[] waitHandles = new WaitHandle[62];
                foreach (Item item in newItemsList)
                {
                    var delegateInstance = new MyDelegate(MyMethod);
                    IAsyncResult asyncResult = delegateInstance.BeginInvoke(item.id, new AsyncCallback(MyAsyncResults), null);
                    waitHandles[j] = asyncResult.AsyncWaitHandle;
                    j++;
                }
                WaitHandle.WaitAll(waitHandles);
                count = count + 62;
                newItemsList = ItemsList.Skip(count).Take(62).ToList();  
            }
            return sum;
    }

    public double MyMethod(int id)
    {
        //Calculations
        return sum;
    }

    static public void MyAsyncResults(IAsyncResult iResult)
    {
        AsyncResult asyncResult = (AsyncResult) iResult;
        MyDelegate del = (MyDelegate) asyncResult.AsyncDelegate;
        double mySum = del.EndInvoke(iResult);
        sum = sum + mySum;
    }
4

6 に答える 6

3

さまざまな手法でループ数を減らすことができます。ただし、ループ内で大量の計算が実行されるため、これによって目立った改善は見られません。すべてのCPUコアを使用するためにすでに並列化した場合、実行することはあまりありません。実行する必要のある計算量は一定であり、使用可能なコンピューターの能力も一定です。あなたはそれが提供できる以上にあなたのマシンから搾り出すことはできません。

次のことを試すことができます。

  1. 可能であれば、アルゴリズムのより効率的な実装を行います
  2. 管理されていないC/C++などのより高速な環境/言語に切り替えます。
于 2012-11-08T06:38:47.503 に答える
1
  1. バッチ サイズ (62) の背後にある根拠はありますか?
  2. 「MyMethod」メソッドは IO バウンドまたは CPU バウンドですか?

各サイクルで行うことは、すべてのバッチが完了するまで待機することであり、これによりいくつかのサイクルが無駄になります (実際には、次のバッチを取得する前に 62 回の呼び出しすべてが完了するのを待機しています)。アプローチを少し変更して、N 個の操作を同時に実行し続け、実行操作の1 つが完了するとすぐに新しい操作を起動しないのはなぜですか?

于 2012-11-08T06:58:19.530 に答える
0

私が間違っている場合は訂正してください。しかし、あなたのスレッドは個々のアイテムレベルにあるように見えます。これは少し細かすぎるのではないかと思います。

あなたはすでに62アイテムのブロックであなたの仕事をしています。それらのアイテムを取得して、単一のスレッド内ですべてを処理するとしたらどうでしょうか。つまり、次のようなものになります。

void RunMyMethods(IEnumerable<Item> items)
{
    foreach(Item item in items)
    {
        var result = MyMethod(item);
        ...
    }
}

WaitHandleオブジェクトはオブジェクトを使用するよりも遅くなる可能性があることに注意してくださいMonitorhttp ://www.yoda.arachsys.com/csharp/threads/waithandles.shtml

それ以外の場合は、通常のアドバイスが当てはまります。パフォーマンスのプロファイルを作成して、真のボトルネックを見つけます。あなたの質問では、反復ごとに2〜3秒かかると述べています。20000回の反復では、20分よりかなり長くかかります。

編集:

CPU時間を最大限に活用したい場合は、20000個のアイテムをたとえば5000個の4つのグループに分割し、各グループを独自のスレッドで処理するのが最適な場合があります。この種の「厚い'n分厚い」並行性は、非常にきめ細かいアプローチよりも効率的だと思います。

于 2012-11-08T07:04:12.763 に答える
0

CPU を集中的に使用しているようですMyMethod。CPU を集中的に使用するタスクの場合、並列化によって大幅な改善を得ることができますが、すべての CPU コアをより有効に活用できる程度にとどまります。その点を超えると、並列化が多すぎるとパフォーマンスが低下し始める可能性があります。これはあなたが行っていることだと思います。(これは、可能な限りほとんど並列化する I/O 集中型タスクとは異なります。)

私の意見では、あなたがする必要があるのは、(単一のアイテムではなく)アイテムの「チャンク」を取り、それらの「合計」を返す別のメソッドを書くことです:

double SumChunk(IEnumerable<Item> items)
{
    return items.Sum(x => MyMethod(x));
}

次に、アイテムの数をn ( nは並列度です。n = CPU コアの数を試し、それを x2 と比較します) で割り、各チャンクを の非同期タスクに渡しますSumChunk。最後に、サブ結果をまとめます。

また、チャンクのいずれかが他のチャンクよりもかなり前に完了しているかどうかを確認してください。その場合、タスクの分布は均一ではありません。小さなチャンク (たとえば 300 アイテムのチャンク) を作成し、それらを に渡す必要がありますSumChunk

于 2012-11-08T07:13:47.837 に答える
0

まず、数字は加算されません。

20,000 回の反復。反復ごとに約 2 ~ 3 秒かかり、合計で約 20 分かかります。

これはx40の「並列処理係数」です。通常のマシンでは、これを実行することはできません。

第 2 に、CPU を集中的に使用する計算を「最適化」する場合、コア数を超えて並列化しても意味がありません。その魔法6216ベンチテストに落としてみてください - 実際にはより速く実行されます.

私はラップトップであなたのコードの変形された不正バージョンを実行し、使用して10〜20%の改善を得ましたParallel.ForEach

では、20 分ではなく 17 分間実行できるようにすることもできますが、それは本当に問題なのでしょうか?

于 2012-11-08T09:39:03.147 に答える
0

このブログによると、コレクションの場合、 for ループは foreach よりも高速です。でループしてみてくださいfor。それが役立ちます。

于 2012-11-08T06:46:42.600 に答える