I am doing some very basic tests to see if using parallel processing in my program would provide any noticable speed boost. So far, I’m confused as to the results. In my test, I’m building a tree structure with a branching factor of 30. First, I do my testing using no parallelism, then I try the same thing using a parallel for loop. Here are my results:
Sequential:
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)
Parallel:
Depth: 2 Time: 0.0389201 (900 nodes)
Depth: 3 Time: 0.1180270 (27,000 nodes)
Depth: 4 Time: 6:06.2296531 (810,000 nodes)
I didn’t bother testing further, as I don’t see it taking less than 7 minutes by the 6 depth.
I have a Dual Core processor, and while I understand that parallelism has a certain amount of overhead, I throught that it wouldn’t be so significant. I have verified that the tree structures genereated in both situations are properly formed to the specified depth with the appropriate number of children (30) at each node.
Here is my code:
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));
}
}
}
You can test it using:
TreeStructure ts = new TreeStructure(4, true);//TreeStructure(int targetDepth, bool runParallel)
I’m hoping that I’m doing something wrong. Is it just that this sort of structure isn’t condusive to parallelism?
The use of
ConcurrentBag<T>in one case andList<T>in the other does not make it comparing apples-to-apples. Once you replaceList<T>withConcurrentBag<T>for non-concurrent children, the speed of running both versions becomes more or less the same.