1

CPUを集中的に使用する作業を行う必要があるC#アプリケーションでスレッドプールを使用しています。ちなみに、遅すぎるようです(編集:デバッグ文字列"Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Heightは10秒ごとに数回しか出力されませんが、少なくともNUM_ROWS_GRID ^ 2 =数秒ごとに16回表示されると予想されます)、SetMinThreadsメソッドを介してMinThreadsも変更します。カスタムスレッドに切り替えるかどうか、またはそれを高速化する方法があるかどうかはわかりません。Googleで検索すると結果が返されますが、何も機能しません。MSDNと同じ状況。

古いコードは次のとおりです。

private void StreamerRoutine()
{
   if (this._state.Area.Width == 0 && this._state.Area.Height == 0)
      this._state.Area = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

   while (this._state.WorkEnd == false)
   {
      // Ends time slice if video is off
      if (this._state.VideoOn == false)
         Thread.Sleep(0);
      else
      {
         lock(this._state.AreaSync)
         {
             Int32 lWidth = this._state.Area.Width / Constants.NUM_ROWS_GRID;
             Int32 lHeight = this._state.Area.Height / Constants.NUM_ROWS_GRID;
             for (Int32 lX = 0; lX + lWidth <= this._state.Area.Width; lX += lWidth)
                for (Int32 lY = 0; lY + lHeight <= this._state.Area.Height; lY += lHeight)
                   ThreadPool.QueueUserWorkItem(CreateDiffFrame, (Object)new Rectangle(lX, lY, lWidth, lHeight));
         }
      }
    }
}

private void CreateDiffFrame(Object pState)
{
   Rectangle lSubArea = (Rectangle)pState;

   SmartDebug.DWL("Calculating on " 
          + lSubArea.X + ":" + lSubArea.Y + " " 
          + lSubArea.Width + ":" + lSubArea.Height);
   // TODO : calculate frame
   Thread.Sleep(0);
}

編集:CreateDiffFrame関数は、1秒間に何回呼び出されるかを知るために使用したスタブにすぎません。この場合、スレッドを使用する最良の方法を定義するので、CPUを集中的に使用する作業に置き換えられます。

編集:私はすべてのThread.Sleep(0);を削除しました。ルーチンを高速化する方法かもしれないと思いましたが、ボトルネックになる可能性があります。新しいコードは次のとおりです。

編集:キャッシュされた値と無限のループを回避するために、WorkEndとVideoOnを揮発性にしました。セマフォも追加して、前のバンチが完了した後にすべてのワークアイテムのバンチを開始するようにしました。現在は非常にうまく機能しています。

private void StreamerRoutine()
    {
        if (this._state.Area.Width == 0 && this._state.Area.Height == 0)
            this._state.Area = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);

        this._state.StreamingSem = new Semaphore(Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID, Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID);


        while (this._state.WorkEnd == false)
        {
            if (this._state.VideoOn == true)
            {
                for (int i = 0; i < Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID; i++)
                    this._state.StreamingSem.WaitOne();

                lock(this._state.AreaSync)
                {
                    Int32 lWidth = this._state.Area.Width / Constants.NUM_ROWS_GRID;
                    Int32 lHeight = this._state.Area.Height / Constants.NUM_ROWS_GRID;
                    for (Int32 lX = 0; lX + lWidth <= this._state.Area.Width; lX += lWidth)
                        for (Int32 lY = 0; lY + lHeight <= this._state.Area.Height; lY += lHeight)
                            ThreadPool.QueueUserWorkItem(CreateDiffFrame, (Object)new Rectangle(lX, lY, lWidth, lHeight));

                }
            }
        }
    }

private void CreateDiffFrame(Object pState)
    {
        Rectangle lSubArea = (Rectangle)pState;

        SmartDebug.DWL("Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Height);
        // TODO : calculate frame
        this._state.StreamingSem.Release(1);

    }
4

3 に答える 3

3

私が見ているものからコードを遅くしている原因を正確に伝える良い方法は本当にありませんが、目立つことがいくつかあります。

  1. Thread.Sleep(0)。これを行うと、OSから残りのタイムスライスを放棄し、すべての速度を低下させます。これは、CreateDiffFrame()がOSスケジューラーに戻るまで実際に戻ることができないためです。

  2. 構造体であるRectangleのオブジェクトキャスト。これが発生すると、ボクシングのオーバーヘッドが発生します。これは、真に計算集約型の操作に必要なものではありません。

  3. lock(this._state.AreaSync)への呼び出し。AreaSyncが別の場所でロックされている可能性があり、それによって処理速度が低下している可能性があります。

  4. アイテムをきめ細かくキューに入れる可能性があります。非常に小さな作業アイテムをキューに入れる場合、実際に行われた作業量と比較して、これらのアイテムを一度に1つずつキューに入れるオーバーヘッドが大きくなる可能性があります。また、このオーバーヘッドを削減するために、キューに入れられた作業項目内に内部ループの内容を配置することを検討することもできます。

これが並列計算で実行しようとしていることである場合、PLINQまたは別のそのようなフレームワークを使用して調査しましたか?

于 2011-12-10T15:03:33.123 に答える
0

KB976898ThreadPool.SetMinThreadsで説明されている方法には既知のバグがあります。

Microsoft .NET Framework 3.5でThreadPool.SetMinThreadsメソッドを使用した後、スレッドプールによって維持されているスレッドが期待どおりに機能しません

この動作の修正は、ここからダウンロードできます。

于 2011-12-10T15:39:09.860 に答える
0

私の推測では、CreateDiffFrameの最後のスリープです。これは、私が正しく覚えていれば、各スレッドが少なくともさらに10ミリ秒間生き続けることを意味します。実際の作業はおそらく10ミリ秒未満で実行できます。ThreadPoolはスレッドの使用を最適化しようとしますが、未処理のスレッドの総数には上限があると思います。したがって、実際にワークロードを模倣する場合は、スリープではなく、予想されるミリ秒数が経過するまで待機するタイトなループを作成します。

とにかく、ThreadPoolを使用することが実際のボトルネックであるとは思いません。他のスレッドメカニズムを使用しても、コードの速度は向上しません。

于 2011-12-10T15:00:40.270 に答える