PLINQ donne des résultats différents de Linq - ce que je fais mal?
Question
Quelqu'un peut-il me dire ce que le code PLINQ correct est pour cela? J'ajoute la racine carrée de la valeur absolue du sinus de chaque élément fo un double tableau, mais le PLINQ me donne un résultat erroné.
La sortie de ce programme est:
Linq total = 75,8310477905274 (correct) PLINQ total = 38,0263653589291 (environ la moitié de ce qu'il devrait être)
Je dois faire quelque chose de mal, mais je ne peux pas travailler ce ...
(je suis en cela avec Visual Studio 2008 sur un Core 2 Duo Windows 7 x64 PC.)
Voici le code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
double[] array = new double[100];
for (int i = 0; i < array.Length; ++i)
{
array[i] = i;
}
double sum1 = array.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Linq aggregate = " + sum1);
IParallelEnumerable<double> parray = array.AsParallel<double>();
double sum2 = parray.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Plinq aggregate = " + sum2);
}
}
}
La solution
Aggregate fonctionne un peu différemment dans PLINQ.
De MSDN Blogs:
Plutôt que d'attendre une valeur initialiser l'accumulateur à la l'utilisateur nous donne une fonction usine qui génère la valeur:
public static double Average(this IEnumerable<int> source)
{
return source.AsParallel().Aggregate(
() => new double[2],
(acc, elem) => { acc[0] += elem; acc[1]++; return acc; },
(acc1, acc2) => { acc1[0] += acc2[0]; acc1[1] += acc2[1]; return acc1; },
acc => acc[0] / acc[1]);
}
Maintenant, PLINQ peut initialiser un accumulateur indépendant pour chaque fil. Maintenant que chaque thread obtient son propre accumulateur, à la fois le pliage la fonction et la combinaison de l'accumulateur fonction sont libres de muter le accumulateurs. PLINQ garantit que accumulateurs ne sera pas accessible simultanément à partir de plusieurs threads.
Alors, dans votre cas, vous devrez également passer une fonction d'accumulateur qui additionne les sorties des agrégats mis en parallèle (donc pourquoi vous voyez un résultat qui est à peu près la moitié de ce qu'elle devrait être).
Autres conseils
Merci blogs MSDN. Il semble maintenant fonctionner correctement. J'ai changé mon code comme suit:
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
Test();
}
static void Test()
{
double[] array = new double[100];
for (int i = 0; i < array.Length; ++i)
{
array[i] = i;
}
double sum1 = array.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Linq aggregate = " + sum1);
IParallelEnumerable<double> parray = array.AsParallel();
double sum2 = parray.Aggregate
(
0.0,
(total1, current1) => total1 + Math.Sqrt(Math.Abs(Math.Sin(current1))),
(total2, current2) => total2 + current2,
acc => acc
);
Console.WriteLine("Plinq aggregate = " + sum2);
}
}
}