It is almost impossible to say without seeing the actual code. But as a general guideline, you should consider to avoid P / LINQ during a complex crunch number because the delegate and IEnumerable overhead are too high. The speed you get with threads is very likely eaten by the convenient abstractions LINQ provides.
Here is some code that calculates the sum of 2 whole lists, makes some int to compare with a floating point, and then calculates cos from it. Pretty simple things you can do well with the LINQ.Zip operator ... or the old fashioned way with a for loop.
Update 1 with updated ParallelLinq on my main Haswell 8 machine
- Linq 0.95s
- Linq Parallel 0.19s
- Optimized 0.45s
- Optimized Parallel 0.08 s
Update 1 end
- LINQ 1.65s
- Optimized 0.64 s
- Optimized Parallel 0.40 s
The time difference is almost factor 3 due to IEnumerable laziness and method invocation (I used Release x32 mode of Windows 7, .NET 4 dual core). I tried using AsParallel in the LINQ version, but it really got slower (2,3s). If you are managing data, you should use the Parallel.For construct to get good scalability. IEnumerable itself is a poor candidate for parallelization because
- You do not know how much work you have before you transfer to the end.
- You cannot make hot snippets because you donโt know how quickly IEnumerable will return the next element (maybe a web service call or access to an array index).
The following is sample code to illustrate this point. If you want to optimize more towards bare metal, you first need to get rid of the abstractions that are too expensive for each item. Access to the array is much cheaper compared to uninterrupted calls to MoveNext () and Current method.
class Program { static void Main(string[] args) { var A = new List<int>(Enumerable.Range(0, 10*1000*1000)); var B = new List<int>(Enumerable.Range(0, 10*1000*1000)); double[] Actual = UseLinq(A, B); double[] pActual = UseLinqParallel(A, B); var other = Optimized(A, B); var pother = OptimizedParallel(A, B); } private static double[] UseLinq(List<int> A, List<int> B) { var sw = Stopwatch.StartNew(); var Merged = A.Zip(B, (a, b) => a + b); var Converted = A.Select(a => (float)a); var Result = Merged.Zip(Converted, (m, c) => Math.Cos((double)m / ((double)c + 1))); double[] Actual = Result.ToArray(); sw.Stop(); Console.WriteLine("Linq {0:F2}s", sw.Elapsed.TotalSeconds); return Actual; } private static double[] UseLinqParallel(List<int> A, List<int> B) { var sw = Stopwatch.StartNew(); var x = A.AsParallel(); var y = B.AsParallel(); var Merged = x.Zip(y, (a, b) => a + b); var Converted = x.Select(a => (float)a); var Result = Merged.Zip(Converted, (m, c) => Math.Cos((double)m / ((double)c + 1))); double[] Actual = Result.ToArray(); sw.Stop(); Console.WriteLine("Linq Parallel {0:F2}s", sw.Elapsed.TotalSeconds); return Actual; } private static double[] OptimizedParallel(List<int> A, List<int> B) { double[] result = new double[A.Count]; var sw = Stopwatch.StartNew(); Parallel.For(0, A.Count, (i) => { var sum = A[i] + B[i]; result[i] = Math.Cos((double)sum / ((double)((float)A[i]) + 1)); }); sw.Stop(); Console.WriteLine("Optimized Parallel {0:F2}s", sw.Elapsed.TotalSeconds); return result; } private static double[] Optimized(List<int> A, List<int> B) { double[] result = new double[A.Count]; var sw = Stopwatch.StartNew(); for(int i=0;i<A.Count;i++) { var sum = A[i] + B[i]; result[i] = Math.Cos((double)sum / ((double)((float)A[i]) + 1)); } sw.Stop(); Console.WriteLine("Optimized {0:F2}s", sw.Elapsed.TotalSeconds); return result; } } }