2012-01-19 57 views
1

我正在做一些非常基本的測試,看看在我的程序中使用並行處理是否會提供任何明顯的速度提升。到目前爲止,我對結果感到困惑。在我的測試中,我構建了一個分支因子爲30的樹結構。首先,我使用無並行性來測試,然後使用parallel for循環嘗試相同的測試。下面是我的結果:並行遞歸比「順序」遞歸慢。我做錯了嗎?

順序:

Depth: 2 Time: 0.0013964 (900 nodes) 
Depth: 3 Time: 0.0053703 (27,000 nodes) 
Depth: 4 Time: 0.3994147 (810,000 nodes) 
Depth: 5 Time: 14.8306510 (24,300,000 nodes) 
Depth: 6 Time: 6:54.4050838 (729,000,000 nodes) 

並行:

Depth: 2 Time: 0.0389201 (900 nodes) 
Depth: 3 Time: 0.1180270 (27,000 nodes) 
Depth: 4 Time: 6:06.2296531 (810,000 nodes) 

我沒有理會進一步的測試,因爲我沒有看到它由6取不到7分鐘深度。

我有一個雙核處理器,雖然我知道並行性有一定的開銷,但我認爲它不會那麼重要。我已經證實,在兩種情況下基因處理的樹結構都是在每個節點適當數量的兒童(30)的適當深度下形成的。

這裏是我的代碼:

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Threading.Tasks; 

namespace ParallelRecursion 
{ 
    class TreeStructure 
    { 
     public TreeStructure(int targetLevel, bool runParallel) 
     { 
      _root = new TreeNode(targetLevel, runParallel); 
     } 

     private TreeNode _root; 
    } 

    class TreeNode 
    { 
     public TreeNode(int targetLevel, bool runParallel) 
     { 
      _runParallel = runParallel; 

      _rnd = new Random(); 
      _score = _rnd.Next(int.MinValue, int.MaxValue); 

      _level = 0; 
      _targetlevel = targetLevel; 

      if (_level < _targetlevel) 
      { 
       if (!_runParallel) 
       { 
        _children = new List<TreeNode>(); 
        GenerateChildren(); 
       } 
       else 
       { 
        _concurrentChildren = new ConcurrentBag<TreeNode>(); 
        GenerateParallelChildren(); 
       } 
      } 
     } 

     public TreeNode(TreeNode treeNode) 
     { 
      _runParallel = treeNode._runParallel; 

      _rnd = treeNode._rnd; 
      _score = _rnd.Next(int.MinValue, int.MaxValue); 
      _parent = treeNode; 

      _level = treeNode._level + 1; 
      _targetlevel = treeNode._targetlevel; 

      if (_level < _targetlevel) 
      { 
       if (!_runParallel) 
       { 
        _children = new List<TreeNode>(); 
        GenerateChildren(); 
       } 
       else 
       { 
        _concurrentChildren = new ConcurrentBag<TreeNode>(); 
        GenerateParallelChildren(); 
       }     
      } 
     } 

     private bool _runParallel; 
     private Random _rnd; 
     private int _score; 
     private int _level; 
     private int _targetlevel; 
     private TreeNode _parent; 
     private List<TreeNode> _children; 
     private ConcurrentBag<TreeNode> _concurrentChildren; 

     private void GenerateChildren() 
     { 
      for (int i = 0; i < 30; i++) 
      { 
       _children.Add(new TreeNode(this)); 
      } 
     }   

     private void GenerateParallelChildren() 
     { 
      Parallel.For(0, 30, i => { GenerateChild(); }); 
     } 

     private void GenerateChild() 
     { 
      _concurrentChildren.Add(new TreeNode(this)); 
     } 
    } 
} 

可以使用測試:

TreeStructure ts = new TreeStructure(4, true);//TreeStructure(int targetDepth, bool runParallel) 

我希望,我做錯了什麼。這僅僅是這種結構對並行性沒有影響嗎?

回答

1

在一種情況下使用ConcurrentBag<T>而在另一種情況下使用List<T>並不能使它比較蘋果與蘋果。一旦你將List<T>替換爲ConcurrentBag<T>對於非併發兒童來說,兩種版本的運行速度差不多一樣。

+0

我已經嘗試過使用ConcurrentBag進行測試,然後都沒有兒童列表(只是孩子的父母保持它的樹)。在這兩種情況下,速度差異都大大平衡。儘管如此,平行於6的深度花費了連續運行的兩倍。還有什麼我可能做錯了嗎? – Chronicide

+0

@Chronicide運行'for'循環(非常高效)和讓一個方法爲你運行'for'循環(效率較低)有一些區別。你可以用'var x = Enumerable.Range(0,30).Select(n => new TreeNode(this))。ToArray();'替換非並行'for'循環以進一步均衡。我認爲其餘的差異來自於開始和結束大量的任務,這些任務都是爲內存分配器而競爭的。 – dasblinkenlight

+0

啊,我明白你的意思了。那麼,你認爲這僅僅是我對並行化的理解不夠了解嗎?這會妨礙我獲得任何明顯的速度增益,或者你認爲它僅僅是因爲這個問題不適合並行化? (或兩者...但我不知道你是否有任何方法來並行化這可能會提高速度...) – Chronicide

0

您正在使用Parallelism錯誤,您正在啓動一個新任務來創建單個節點,這就是並行版本較慢的原因,因爲儘管TPL實際上並未爲每次迭代創建一個任務,但它仍然包含幾個其中,創建任務在時間上是昂貴的(不如線程)。

你應該做的是分而治之,劃分你的工作,做一個任務創建一堆TreeNode而不只是一個。

+0

我測試過,所有三十個孩子都在一個附加線程中產生(而不是每個孩子產生一個線程)。這實際上使並行版本慢了近一倍。也許我誤解了你的答案。 – Chronicide

+0

時間上的差異很奇怪,因爲你正在使用ConcurrentBag,並且這個類有鎖定機制,減慢了代碼的速度。那添加到任務創建開銷可能會讓事情變得更慢。儘管如此,我仍然希望看到你正在花費時間結果的代碼,你可以發佈它嗎? – DVD

+0

刪除ConcurrentBag後,我再次執行了我的測試。現在,每個孩子都維護一個父母的引用,而父母沒有辦法跟蹤它的孩子。時差與以前相比要少得多,但仍然存在。順序深度= 6:1:11.821,平行深度= 6:2:00.841。對於代碼,我改變了調用GenerateParallelChildren()使用Paralle.Invoke()和GenerateParallelChildren()我用一個簡單的(i = 0; i <30; i ++)循環來創建子項。這種方法比每個任務產生一個孩子更快,但仍然比順序處理慢。 – Chronicide