没有看到实际的代码,很难确定。但是作为一般准则,应该避免在复杂的数字计算中使用P / LINQ,因为委托和IEnumerable开销太大。使用线程获得的速度很可能被LINQ提供的方便抽象所吞噬。
这里有一些代码,它计算了两个整数列表的总和,进行了一些int到float的比较,然后计算了它的cos。这是基本的东西,可以使用LINQ的.Zip运算符很好地完成...或者用for循环的老方法来完成。
更新1:使用Haswell 8核机器上的更新ParallelLinq
- Linq 0,95秒
- Linq Parallel 0,19秒
- Optimized 0,45秒
- Optimized Parallel 0,08秒
时间差别接近3倍,原因是IEnumerable的惰性和方法调用开销(我使用了Release模式x32 Windows 7,.NET 4双核)。我尝试在LINQ版本中使用AsParallel,但实际上速度变慢了(2.3秒)。如果你是数据驱动的,你应该使用Parallel.For结构来获得良好的可伸缩性。IEnumerable本身不适合并行化,因为:
- 在枚举到结束之前,你不知道有多少工作要做。
- 你不能急切地分块,因为你不知道IEnumerable将返回下一个元素有多快(可能是Web服务调用或数组索引访问)。
下面是一个代码示例来说明这一点。如果你想更多地优化裸机性能,你需要先摆脱成本太高的抽象。与非内联的MoveNext()和Current方法调用相比,数组访问要便宜得多。
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;
}
}
}
AsParallel
也是如此。在评估查询之前,什么都不会发生。您必须遍历或以其他方式执行查询。 - Anthony Pegram