PLINQ(C#/ .Net 4.5.1)vs Stream(JDK / Java 8)性能

时间:2014-05-20 22:28:09

标签: c# java performance java-8

我试图比较Java 8和PLINQ(C#/ .Net 4.5.1)中并行流之间的性能。

以下是我在我的机器上获得的结果(系统制造商Dell Inc.系统型号Precision M4700处理器Intel(R)Core(TM)i7-3740QM CPU @ 2.70GHz,2701 Mhz,4 Core(s),8逻辑处理器已安装物理内存(RAM)16.0 GB操作系统名称Microsoft Windows 7企业版6.1.7601 Service Pack 1 Build 7601)

C#.Net 4.5.1(X64-release)

串行:

470.7784,491.4226,502.4643,481.7507,464.1156,463.0088,546.149,481.2942,502.414,483.1166

平均值:490.6373

并行:

158.6935,133.4113,217.4304,182.3404,184.188,128.5767,160.352,277.2829,127.6818,213.6832

平均值:180.5496

Java 8(X64)

串行:

471.911822,333.843924,324.914299,325.215631,325.208402,324.872828,324.888046,325.53066,325.765791,325.935861

平均:326.241715

并行:

212.09323,73.969783,68.015431,66.246628,66.15912,66.185373,80.120837,75.813539,70.085948,66.360769

平均:70.3286

看起来PLINQ不能跨CPU核心扩展。我想知道我是否想念一些东西。

以下是C#的代码:

class Program
{
    static void Main(string[] args)
    {
        var NUMBER_OF_RUNS = 10;
        var size = 10000000;
        var vals = new double[size];

    var rnd = new Random();
    for (int i = 0; i < size; i++)
    {
        vals[i] = rnd.NextDouble();
    }

    var avg = 0.0;
    Console.WriteLine("Serial:");
    for (int i = 0; i < NUMBER_OF_RUNS; i++)
    {
        var watch = Stopwatch.StartNew();
        var res = vals.Select(v => Math.Sin(v)).ToArray();
        var elapsed = watch.Elapsed.TotalMilliseconds;
        Console.Write(elapsed + ", ");

        if (i > 0)
            avg += elapsed;
    }
    Console.Write("\nAverage: " + (avg / (NUMBER_OF_RUNS - 1)));

    avg = 0.0;
    Console.WriteLine("\n\nParallel:");
    for (int i = 0; i < NUMBER_OF_RUNS; i++)
    {
        var watch = Stopwatch.StartNew();
        var res = vals.AsParallel().Select(v => Math.Sin(v)).ToArray();
        var elapsed = watch.Elapsed.TotalMilliseconds;
        Console.Write(elapsed + ", ");

        if (i > 0)
            avg += elapsed;
    }
    Console.Write("\nAverage: " + (avg / (NUMBER_OF_RUNS - 1)));
}
}

以下是Java的代码:

import java.util.Arrays;
import java.util.Random;
import java.util.stream.DoubleStream;

public class Main {
    private static final Random rand = new Random();
    private static final int MIN = 1;
    private static final int MAX = 140;
    private static final int POPULATION_SIZE = 10_000_000;
    public static final int NUMBER_OF_RUNS = 10;
public static void main(String[] args) throws InterruptedException {
    Random rnd = new Random();
    double[] vals1 = DoubleStream.generate(rnd::nextDouble).limit(POPULATION_SIZE).toArray();

    double avg = 0.0;
    System.out.println("Serial:");
    for (int i = 0; i < NUMBER_OF_RUNS; i++)
    {
        long start = System.nanoTime();
        double[] res = Arrays.stream(vals1).map(Math::sin).toArray();
        double duration = (System.nanoTime() - start) / 1_000_000.0;
        System.out.print(duration + ", " );

        if (i > 0)
            avg += duration;
    }
    System.out.println("\nAverage:" + (avg / (NUMBER_OF_RUNS - 1)));

    avg = 0.0;
    System.out.println("\n\nParallel:");
    for (int i = 0; i < NUMBER_OF_RUNS; i++)
    {
        long start = System.nanoTime();
        double[] res = Arrays.stream(vals1).parallel().map(Math::sin).toArray();
        double duration = (System.nanoTime() - start) / 1_000_000.0;
        System.out.print(duration + ", " );

        if (i > 0)
            avg += duration;            
    }
    System.out.println("\nAverage:" + (avg / (NUMBER_OF_RUNS - 1)));
}

}

1 个答案:

答案 0 :(得分:2)

两个运行时都会决定使用多少线程来完成并行操作。这是一项非常重要的任务,可以考虑许多因素,包括任务受CPU限制的程度,完成任务的预计时间等等。

每个运行时都会决定使用多少线程来解析请求。在系统范围的调度方面,这两项决策都没有明显的正确或错误,但是Java策略更好地执行了基准测试(并且为系统上的其他任务留下了更少的CPU资源)。