I am comparing difference in time execution of two breadth first search algorithms. Parallel and non parallel. But for every graph I make, the non parallel version is 10X faster than the parallel one.
This is parallel Breadth first search. I don’t know where the problem is but I suppose somewhere in this method:
public static int PBFS(Node start, Node end)
{
var queue = new ConcurrentQueue<Node>();
queue.Enqueue(start);
while (queue.Count != 0)
{
bool isFinished = false;
if (isFinished) break;
Parallel.ForEach<Node>(queue, CurrentNode =>
{
if (queue.TryDequeue(out CurrentNode))
{
foreach (Node Child in CurrentNode.Children.Keys)
{
if (Child.IsVisited == false)
{
Child.IsVisited = true;
if (Child == end) { isFinished = true; return; }
}
queue.Enqueue(Child);
}
}
return;
});
} return 1;
}
This is non parallel BFS:
public static int BFS(Node start, Node end)
{
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(start);
while (queue.Count != 0)
{
Node CurrentNode = queue.Dequeue();
CurrentNode.IsVisited = true;
foreach (Node Child in CurrentNode.Children.Keys)
{
if (Child.IsVisited == false)
{
Child.IsVisited = true;
//Console.Write(Child.Name+" ");
if (Child == end) return 1;
}
queue.Enqueue(Child);
}
// Console.WriteLine();
}
// Console.WriteLine();
return 0;
}
Parallelisation and concurrency with shared data requires synchronization. Synchronization is expensive–as you’re likely witnessing.
ConcurrentQueuehas it’s own synchronization which likely isn’t optimal for your situation.Parallelization beyond the number of CPUs (which likely isn’t occurring here, but it’s relevant) incurs a lot of Context Switches–which are expensive and reduce the productivity of the code that’s running in parallel. i.e. the premise of throwing more threads at a problem often produces the opposite effect.
If performance is a concern, I think you might want to look at a different design, maybe Actor-based, message-passing, or producer/consumer to avoid having shared data and avoid synchronization of that shared data.