What is the difference between these two?
What is the best way to compare ?
It is always better plinq ?
When we use plinq ?
I also wanted to know when to use PLINQ instead of LINQ so I ran some tests.
Summary: There are two questions to answer when deciding whether to use LINQ or PLINQ to run a query.
How many iterations are involved in running the query (how many objects are in the collection)?
How much work is involved in an iteration?
Use LINQ unless PLINQ is more performant. PLINQ can be more performant than LINQ if querying the collection involves too many iterations AND/OR each iteration involves too much work.
But then two difficult questions arise:
My advice is to test your query. Test once using LINQ and once using PLINQ and then compare the two results.
Test 1: Increasing the number of iterations in the query by increasing the number of objects in the collection.
The overhead of initialising PLINQ takes around 20ms. If PLINQ's strengths aren't utilised, this is wasted time because LINQ has 0ms overhead.
The work involved in each iteration is always the same for each test. The work is kept minimal.
Definition of work: Multiplying the int (object in the collection) by 10.
When iterating 1 million objects where each iteration involves minimal work, PLINQ is faster than LINQ. Although in a professional environment, I've never queried or even initialised a collection of 10 million objects in memory so this might be an unlikely scenario where PLINQ happens to be superior to LINQ.
╔═══════════╦═══════════╦════════════╗
║ # Objects ║ LINQ (ms) ║ PLINQ (ms) ║
╠═══════════╬═══════════╬════════════╣
║ 1 ║ 1 ║ 20 ║
║ 10 ║ 0 ║ 18 ║
║ 100 ║ 0 ║ 20 ║
║ 1k ║ 0 ║ 23 ║
║ 10k ║ 1 ║ 17 ║
║ 100k ║ 4 ║ 37 ║
║ 1m ║ 36 ║ 76 ║
║ 10m ║ 392 ║ 285 ║
║ 100m ║ 3834 ║ 2596 ║
╚═══════════╩═══════════╩════════════╝
Test 2: Increasing the work involved in a iteration
I set the number of objects in the collection to always be 10 so the query involves a low number of iterations. For each test, I increased the work involved to process each iteration.
Definition of work: Multiplying the int (object in the collection) by 10.
Definition of increasing the work: Increasing the number of iterations to multiply the int by 10.
PLINQ was faster at querying the collection as the work was significantly increased when the number of iterations inside a work iteration was increased to 10 million and I concluded that PLINQ is superior to LINQ when a single iteration involves this amount of work.
"# Iterations" in this table means the number of iterations inside a work iteration. See Test 2 code below.
╔══════════════╦═══════════╦════════════╗
║ # Iterations ║ LINQ (ms) ║ PLINQ (ms) ║
╠══════════════╬═══════════╬════════════╣
║ 1 ║ 1 ║ 22 ║
║ 10 ║ 1 ║ 32 ║
║ 100 ║ 0 ║ 25 ║
║ 1k ║ 1 ║ 18 ║
║ 10k ║ 0 ║ 21 ║
║ 100k ║ 3 ║ 30 ║
║ 1m ║ 27 ║ 52 ║
║ 10m ║ 263 ║ 107 ║
║ 100m ║ 2624 ║ 728 ║
║ 1b ║ 26300 ║ 6774 ║
╚══════════════╩═══════════╩════════════╝
Test 1 code:
class Program
{
private static IEnumerable _numbers;
static void Main(string[] args)
{
const int numberOfObjectsInCollection = 1000000000;
_numbers = Enumerable.Range(0, numberOfObjectsInCollection);
var watch = new Stopwatch();
watch.Start();
var parallelTask = Task.Run(() => ParallelTask());
parallelTask.Wait();
watch.Stop();
Console.WriteLine($"Parallel: {watch.ElapsedMilliseconds}ms");
watch.Reset();
watch.Start();
var sequentialTask = Task.Run(() => SequentialTask());
sequentialTask.Wait();
watch.Stop();
Console.WriteLine($"Sequential: {watch.ElapsedMilliseconds}ms");
Console.ReadKey();
}
private static void ParallelTask()
{
_numbers
.AsParallel()
.Select(x => DoWork(x))
.ToArray();
}
private static void SequentialTask()
{
_numbers
.Select(x => DoWork(x))
.ToArray();
}
private static int DoWork(int @int)
{
return @int * 10;
}
}
Test 2 code:
class Program
{
private static IEnumerable _numbers;
static void Main(string[] args)
{
_numbers = Enumerable.Range(0, 10);
var watch = new Stopwatch();
watch.Start();
var parallelTask = Task.Run(() => ParallelTask());
parallelTask.Wait();
watch.Stop();
Console.WriteLine($"Parallel: {watch.ElapsedMilliseconds}ms");
watch.Reset();
watch.Start();
var sequentialTask = Task.Run(() => SequentialTask());
sequentialTask.Wait();
watch.Stop();
Console.WriteLine($"Sequential: {watch.ElapsedMilliseconds}ms");
Console.ReadKey();
}
private static void ParallelTask()
{
_numbers
.AsParallel()
.Select(x => DoWork(x))
.ToArray();
}
private static void SequentialTask()
{
_numbers
.Select(x => DoWork(x))
.ToArray();
}
private static int DoWork(int @int)
{
const int numberOfIterations = 1000000000;
for (int i = 0; i < numberOfIterations; i++)
{
@int = @int * 10;
}
return @int;
}
}