我试图比较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)));
}
}
答案 0 :(得分:2)
两个运行时都会决定使用多少线程来完成并行操作。这是一项非常重要的任务,可以考虑许多因素,包括任务受CPU限制的程度,完成任务的预计时间等等。
每个运行时都会决定使用多少线程来解析请求。在系统范围的调度方面,这两项决策都没有明显的正确或错误,但是Java策略更好地执行了基准测试(并且为系统上的其他任务留下了更少的CPU资源)。