2017-06-16 7 views
2

私はC#で書いたアルゴリズムを高速化しようとしています。私が最初に考えたのは、それを平行にすることです。C#マルチスレッドで十分なCPUが使用されない

アルゴリズムは、多くの(〜数百万)の2Dセグメントで実行する必要があり、各セグメントは他のセグメントとは独立しています。ここ

コードである: `空間データ構造に

private void DoMapping(Segment[] image, CancellationToken ct, int numTasks = 3) 
    { 
     long time = Environment.TickCount; 
     LaserOutput = new List<Vector3[]>(); 
     NormalsOutput = new List<Vector3>(); 
     Task< Tuple < List<Vector3[]>, List <Vector3>>>[] tasks = new Task<Tuple<List<Vector3[]>, List<Vector3>>>[numTasks]; 

     int perTaskSegments = image.Length/numTasks; 

     for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++) 
     { 

      int nseg = perTaskSegments * (taskIndex + 1) + (taskIndex == tasks.Length - 1 ? image.Length % tasks.Length : 0); 
      int from = perTaskSegments * taskIndex; 
      Tuple<int, int, Segment[], CancellationToken> obj = new Tuple<int, int, Segment[], CancellationToken>(from, nseg, image, ct); 
      tasks[taskIndex] = Task.Factory.StartNew(DoComputationsAction, obj, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 
     } 

     Task.WaitAll(tasks); 

     for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++) 
     { 
      LaserOutput.AddRange(tasks[taskIndex].Result.Item1); 
      NormalsOutput.AddRange(tasks[taskIndex].Result.Item2); 
     } 
    } 

    private Tuple<List<Vector3[]>, List<Vector3>> DoComputationsAction(object obj) 
    { 
     Tuple<int, int, Segment[], CancellationToken> parm = obj as Tuple<int, int, Segment[], CancellationToken>; 
     List<Vector3[]> tmpLaser = new List<Vector3[]>(); 
     List<Vector3> tmpNormals = new List<Vector3>(); 

     bool errorOccured = false; 
     for (int segCounter = parm.Item1; segCounter < parm.Item2 && !errorOccured; segCounter++) 
     { 
      if (parm.Item4.IsCancellationRequested) 
       break; 
      try 
      { 
       var res = SplitOverMap(parm.Item3[segCounter], (string error) => { 
        errorOccured = true; 
        MessageBox.Show(error, "An error occured", MessageBoxButtons.OK, MessageBoxIcon.Error); 
        Logger.Log("An error occured while mapping data to 3d."); 
       }); 

       if (res != null) 
       { 
        tmpLaser.AddRange(res.Item1); 
        tmpNormals.AddRange(res.Item2); 
       } 
      } 
      catch (Exception e) 
      { 
       Logger.Log("An error occured while calculating 3d map. Skipping polyline." + e.Message); 
      } 
     } 

     return new Tuple<List<Vector3[]>, List<Vector3>>(tmpLaser, tmpNormals); 
    }` 

内部SplitOverMap クエリ(のqtree)を行い、その後、いくつかの計算が発生します。

ロックなしは、プロセス全体で実行されます。 No Diskが使用されています。

CPUが40-60回しか使用できない原因について何か提案がありますか?

numタスクを4,6,8に変更しようとしました。大きな変更はありません。

私はGCについて考えていますが、実行するのを防ぐためにできることはたくさんありません。

EDIT: 私は少しにCPU使用率を向上させるために管理しているクラスのいくつかのメモリ使用量を減らすことにより、今では70%前後を実行します。

一方、QuadTreeのレベルスレッショルドを上げることによって、私は大幅なパフォーマンスの向上が得られました。

+0

各シングルスレッドは、単一の物理コアのみを使用できます。多分それはあなたが直面しているものです。また、 'Task.WaitAll(tasks);'はすべてのタスクが完了したときにのみ動くので、それらのいくつかはすべて完了する前に既に完了しているかもしれません。 – Karolis

+0

あなたは[mcve]を提供する必要があります。ペーストをコピーして問題を見るために実行できるコードが本当に必要です。理想的には、このコードの非並行バージョンを用意して、基本的な計算がわかるようにする必要があります。 – Enigmativity

+0

あなたの意図はわかりませんが、パラレルにして確認できます。それを行う簡単な方法は、Parallel forループを使用することです。やはりプロセッサのコアに依存するプロセッサアフィニティを設定することができます。 –

答えて

4

追加の同期が必要なセグメント間に依存関係がないため、タスク並列ライブラリ(TPL)を参照することをお勧めします。 Parallel.ForまたはParallel.ForEachが面白いかもしれません。既存のコードを最適化するために

、いくつかのオプションがあります。

  • ​​を削除するには。新しいスレッドが生成される可能性があり、時間がかかります。
  • 独自のタスクスケジューラを作成し、基礎となるスレッドに高い優先度を与えます。 TPL並列ループを試すときにも使用できます。現在、他のコンポーネントによって使用/ブロックされるデフォルトのスレッドプールを使用しています。

更新: も参照してくださいlowering priority of Task.Factory.StartNew thread HOWTOに異なる優先順位を使用してカスタムタスクスケジューラを作成します。それは非常にうまくいく、私はそれをいくつかのプロジェクトに使用した。 Stephen Toubのブログも参照してください。

関連する問題