2017-06-16 46 views
2

我試圖加快我用C#編寫的算法。我想到的第一件事是讓它平行。該算法必須運行大量(〜數百萬)的2D片段,每個片段獨立於其他片段。C#多線程沒有使用足夠的CPU

下面是代碼:`

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一個查詢到空間的數據結構(配額樹)被執行,則發生一些計算。

無鎖在整個過程中進行。 使用No Disk

你有什麼建議可能導致CPU只能達到40-60的使用?

我也嘗試將num任務更改爲4,6和8。沒有重大變化。

我正在考慮GC,但沒有太多的工作可以阻止它運行。

編輯: 通過減少一些類的內存使用情況,我設法改善了一點點的CPU使用率,現在它運行在70%左右。

另一方面,通過提高QuadTree的水平閾值,我獲得了顯着的性能提升。

+0

每個單線程只能關閉一個物理內核。也許這就是你所面對的。另外'Task.WaitAll(tasks);'只會在所有任務完成時繼續執行,因此其中一些可能在所有任務完成之前就已經完成。 – Karolis

+0

您需要提供[mcve]。我們確實應該有能夠複製粘貼並運行以查看問題的代碼。理想情況下,我們也應該有一個非平行版本的代碼,以便我們可以看到基本計算是什麼。 – Enigmativity

+0

不知道你的意圖是什麼,但你可以並行和檢查。簡單的做法是使用Parallel for循環。您還可以設置再次取決於處理器內核的處理器親和性。 –

回答

4

由於段之間不需要額外的同步,所以我建議查看任務並行庫(TPL)。 Parallel.ForParallel.ForEach可能對您有興趣。

要優化現有的代碼,有幾個選項:

  • 刪除​​。它可能會產生新的線程,這是非常耗時的。
  • 創建您自己的任務調度程序並給予基礎線程更高的優先級。在試驗TPL並聯迴路時也可以使用它。目前,您使用的默認線程池可能會被其他組件使用/阻止。

更新:lowering priority of Task.Factory.StartNew thread參見HOWTO創建具有不同的優先級自定義任務調度。它工作得很好,我用於幾個項目。另見Stephen Toub的博客。

相關問題