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会快很多。
我有以下代码来测试 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会快很多。