关于c#4.0:并行递归比“顺序”递归要慢。 我做错了吗?

Parallel recursion slower than “sequential” recursion. Am I doing this wrong?

我正在做一些非常基本的测试,以查看在程序中使用并行处理是否可以提供明显的速度提升。到目前为止,我对结果感到困惑。在测试中,我正在建立一个分支因子为30的树结构。首先,我不使用并行性进行测试,然后使用并行for循环尝试相同的操作。这是我的结果:

顺序的:

1
2
3
4
5
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)

平行:

1
2
3
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)。

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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));
        }
    }
}

您可以使用以下方法进行测试:

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

我希望我做错了什么。仅仅是这种结构不利于并行性吗?


在一种情况下使用ConcurrentBag< T >而在另一种情况下使用List< T >并不能使它们之间进行比较。 对于非并行子代,将List< T >替换为ConcurrentBag< T >后,运行两个版本的速度或多或少相同。


您错误地使用了Parallelism,正在启动新的Task以创建单个节点,这就是为什么并行版本速度较慢的原因,因为尽管TPL并未为每次迭代实际创建任务,但仍创建了其中的一些任务,并且 创建任务在时间上很昂贵(不如线程多)。

您应该做的是分而治之,分工,使一个任务创建一堆TreeNode,而不仅仅是一个。