PLinq 并行求和性能不一致

PLinq parallel summing performance inconsistency

我有以下代码来测试 PLinq 求和数字的速度比 Linq 快多少:

internal class Program
{
    private static readonly IEnumerable<Company> _smallCompanies = GenerateSmallCompanies();

    private static void Main(string[] args)
    {
        Console.WriteLine ("just decimals");
        var numbers=Enumerable.Range (0, 10000000).Select(n=>(decimal)n).ToList();
        for (int i = 0; i < 10; i++) {
            Console.WriteLine ("one thread");
            Timer (()=>numbers.Aggregate((a,b)=>a+b));
            Console.WriteLine ("plinq many threads");
            Timer (()=>numbers.AsParallel().Aggregate(decimal.Zero,(a,b)=>a+b,(r1,r2)=>r1+r2,f=>f));    
        }

        Console.WriteLine ("decimals in wrapper objects");
        for (int i = 0; i < 10; i++) {
            WithWrappers ();
        }
    }

    private static void Timer(Action act){
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        act ();
        stopwatch.Stop();

        Console.WriteLine( " Time: " +
            stopwatch.ElapsedMilliseconds);
    }

    private static void WithWrappers()
    {
        Console.WriteLine("==========================");

        PrintMergeResult(LinearMerger.LinearMerge, "one thread");
        PrintMergeResult(FunctionalParallelMerger.FunctionalParallelMerge, "plinq many threads");
    }

    private static void PrintMergeResult(Func<Company, IEnumerable<Company>, Company> mergeMethod,
        string funcApproach)
    {
        Console.WriteLine(funcApproach);
        Timer (() => mergeMethod (new Company { EvaluatedMarketValue = 0 }, _smallCompanies));
    }

    private static IEnumerable<Company> GenerateSmallCompanies()
    {
        return Enumerable
            .Range(0, 10000000)
            .Select(number => new Company {EvaluatedMarketValue = number});
    }
}

internal class Company
{
    public decimal EvaluatedMarketValue { get; set; }

    public Company Merge(Company that)
    {
        EvaluatedMarketValue += that.EvaluatedMarketValue;
        return this;
    }
}

internal class FunctionalParallelMerger
{
    public static Company FunctionalParallelMerge(Company bigCompany, IEnumerable<Company> smallCompanies)
    {
        return smallCompanies
            .AsParallel()
            .Aggregate(CreateShell,
                (shell, smallCompany) => shell.Merge(smallCompany),
                (shell1, shell2) => shell1.Merge(shell2),
                bigCompany.Merge);
    }

    private static Company CreateShell()
    {
        return new Company();
    }
}

internal class LinearMerger
{
    public static Company LinearMerge(Company bigCompany, IEnumerable<Company> smallCompanies)
    {
        foreach (var smallCompany in smallCompanies)
        {
            bigCompany.Merge(smallCompany);
        }
        return bigCompany;
    }
}

结果不如预期,当它只是十进制数字时,PLinq 更快,但是当数字被包裹在小公司对象中时,PLinq 更慢,这是日志:

just decimals
one thread
 Time: 562
plinq many threads
 Time: 525
one thread
 Time: 569
plinq many threads
 Time: 460
one thread
 Time: 573
plinq many threads
 Time: 283
one thread
 Time: 570
plinq many threads
 Time: 193
one thread
 Time: 548
plinq many threads
 Time: 194
one thread
 Time: 525
plinq many threads
 Time: 174
one thread
 Time: 524
plinq many threads
 Time: 137
one thread
 Time: 555
plinq many threads
 Time: 153
one thread
 Time: 535
plinq many threads
 Time: 145
one thread
 Time: 554
plinq many threads
 Time: 159
decimals in wrapper objects
==========================
one thread
 Time: 762
plinq many threads
 Time: 2880
==========================
one thread
 Time: 719
plinq many threads
 Time: 2915
==========================
one thread
 Time: 686
plinq many threads
 Time: 2924
==========================
one thread
 Time: 690
plinq many threads
 Time: 2919
==========================
one thread
 Time: 697
plinq many threads
 Time: 2928
==========================
one thread
 Time: 710
plinq many threads
 Time: 2984
==========================
one thread
 Time: 700
plinq many threads
 Time: 2986
==========================
one thread
 Time: 723
plinq many threads
 Time: 3054
==========================
one thread
 Time: 779
plinq many threads
 Time: 3030
==========================
one thread
 Time: 756
plinq many threads
 Time: 2901

这是什么原因?小物件太多? GC?

private static IEnumerable<Company> GenerateSmallCompanies()
{
    return Enumerable
        .Range(0, 10000000)
        .Select(number => new Company {EvaluatedMarketValue = number});
}

找到问题了,就是这个方法。 这里是在做lazy eval,最后加上ToList,或者ToArray,Plinq会快很多。