Plinq дает отличные результаты от Linq - что я делаю не так? - PullRequest
6 голосов
/ 13 мая 2009

Может кто-нибудь сказать мне, что правильный код Plinq для этого? Я складываю квадратный корень абсолютного значения синуса каждого элемента для двойного массива, но Plinq дает мне неверный результат.

Вывод этой программы:

Агрегат Linq = 75,8310477905274 (правильный) Агрегат Plinq = 38.0263653589291 (примерно половина того, что должно быть)

Должно быть, я что-то делаю не так, но не могу понять, что ...

(я запускаю это с Visual Studio 2008 на ПК с Core 2 Duo Windows 7 x64.)

Вот код:

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);
        }
    }
}

Ответы [ 2 ]

3 голосов
/ 13 мая 2009

Агрегат работает немного по-другому в PLINQ.

Из Блоги MSDN:

Вместо ожидания значения инициализировать аккумулятор до пользователь дает нам заводскую функцию, которая генерирует значение:

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]);
}

Теперь PLINQ может инициализировать независимый аккумулятор для каждого нить. Теперь, когда каждый поток получает свой собственный аккумулятор, как складной функция и объединение аккумулятора функция свободна мутировать аккумуляторы. PLINQ гарантирует, что аккумуляторы не будут доступны одновременно из нескольких потоков.

Итак, в вашем случае вам также нужно будет передать функцию накопителя, которая суммирует выходные данные параллельных агрегатов (следовательно, почему вы видите результат, который примерно вдвое меньше, чем должен быть).

0 голосов
/ 13 мая 2009

Спасибо Блоги MSDN. Теперь кажется, что работает правильно. Я изменил свой код следующим образом:

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);
        }
    }
}
...