Почти невозможно сказать, не увидев реальный код. Но в качестве общего руководства вы должны избегать использования P / LINQ во время обработки сложных чисел, поскольку издержки делегата и IEnumerable слишком велики. Скорость, которую вы получаете, используя потоки, вероятно, съедается удобными абстракциями, которые обеспечивает LINQ.
Вот некоторый код, который вычисляет сумму из двух целочисленных списков, выполняет некоторое сравнение с плавающей запятой, а затем вычисляет его стоимость. Довольно простые вещи, которые можно сделать с помощью LINQ, оператора .Zip ... или по старинке с циклом for.
Обновление 1 с обновленным ParallelLinq на моем ядре Haswell 8
- Linq 0,95 с
- Linq Parallel 0,19 с
- Оптимизировано 0,45 с
- Оптимизированная параллель 0,08 с
Обновление 1 Конец
- LINQ 1,65 с
- Оптимизировано 0,64 с
- Оптимизированная параллель 0,40 с
Разница во времени почти в 3 раза из-за лени IEnumerable и накладных расходов при вызове методов (я использовал режим Release x32 для Windows 7, двухъядерный .NET 4). Я пытался использовать AsParallel в версии LINQ, но он действительно стал медленнее (2,3 с). Если вы работаете с данными, вы должны использовать конструкцию Parallel.For, чтобы получить хорошую масштабируемость. IEnumerable сам по себе является плохим кандидатом на распараллеливание, поскольку
- Вы не знаете, сколько у вас работ, прежде чем перечислили до конца.
- Вы не можете сделать нетерпеливый отрывов, потому что вы не знаете, как быстро IEnumerable будет возвращать следующий элемент (может быть вызов веб-службы или доступ индексный массив).
Ниже приведен пример кода для иллюстрации сути. Если вы хотите оптимизировать работу с голым металлом, вам нужно сначала избавиться от абстракций, которые стоят слишком дорого за единицу. Доступ к массиву намного дешевле по сравнению со встроенными вызовами методов 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;
}
}
}