I've been testing the performance of System.Threading.Parallel vs a Threading and I'm surprised to see Parallel taking longer to finish tasks than threading. I'm sure it's due to my limited knowledge of Parallel, which I just started reading up on.
I thought i'll share few snippets and if anyone can point out to me paralle code is running slower vs threaded code. Also tried to run the same comparison for finding prime numbers and found parallel code finishing much later than threaded code.
public class ThreadFactory
{
int workersCount;
private List<Thread> threads = new List<Thread>();
public ThreadFactory(int threadCount, int workCount, Action<int, int, string> action)
{
workersCount = threadCount;
int totalWorkLoad = workCount;
int workLoad = totalWorkLoad / workersCount;
int extraLoad = totalWorkLoad % workersCount;
for (int i = 0; i < workersCount; i++)
{
int min, max;
if (i < (workersCount - 1))
{
min = (i * workLoad);
max = ((i * workLoad) + workLoad - 1);
}
else
{
min = (i * workLoad);
max = (i * workLoad) + (workLoad - 1 + extraLoad);
}
string name = "Working Thread#" + i;
Thread worker = new Thread(() => { action(min, max, name); });
worker.Name = name;
threads.Add(worker);
}
}
public void StartWorking()
{
foreach (Thread thread in threads)
{
thread.Start();
}
foreach (Thread thread in threads)
{
thread.Join();
}
}
}
Here is the program:
Stopwatch watch = new Stopwatch();
watch.Start();
int path = 1;
List<int> numbers = new List<int>(Enumerable.Range(0, 10000));
if (path == 1)
{
Parallel.ForEach(numbers, x =>
{
Console.WriteLine(x);
Thread.Sleep(1);
});
}
else
{
ThreadFactory workers = new ThreadFactory(10, numbers.Count, (min, max, text) => {
for (int i = min; i <= max; i++)
{
Console.WriteLine(numbers[i]);
Thread.Sleep(1);
}
});
workers.StartWorking();
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds.ToString());
Console.ReadLine();
Update:
Taking Locking into consideration: I tried the following snippet. Again the same results, Parallel seems to finish much slower.
path = 1; cieling = 10000000;
List<int> numbers = new List<int>();
if (path == 1)
{
Parallel.For(0, cieling, x =>
{
lock (numbers)
{
numbers.Add(x);
}
});
}
else
{
ThreadFactory workers = new ThreadFactory(10, cieling, (min, max, text) =>
{
for (int i = min; i <= max; i++)
{
lock (numbers)
{
numbers.Add(i);
}
}
});
workers.StartWorking();
}
Update 2: Just a quick update that my machine has Quad Core Processor. So Parallel have 4 cores available.
Refering to a blog post by Reed Copsey Jr:
Parallel.ForEach is a bit more complicated, however. When working with a generic IEnumerable, the number of items required for processing is not known in advance, and must be discovered at runtime. In addition, since we don’t have direct access to each element, the scheduler must enumerate the collection to process it. Since IEnumerable is not thread safe, it must lock on elements as it enumerates, create temporary collections for each chunk to process, and schedule this out.
The locking and copying could make Parallel.ForEach take longer. Also partitioning and the scheduler of ForEach could impact and give overhead. I tested your code and increased the sleep of each task, and then the results are closer, but still ForEach is slower.
[Edit - more research]
I added the following to the execution loops:
if (Thread.CurrentThread.ManagedThreadId > maxThreadId)
maxThreadId = Thread.CurrentThread.ManagedThreadId;
What this shows on my machine is that it uses 10 threads less with ForEach, compared to the other one with the current settings. If you want more threads out of ForEach, you would have to fiddle around with ParallelOptions and the Scheduler.
See Does Parallel.ForEach limits the number of active threads?