java - 尝试对 Java 8 parallelSetAll() 与 setAll() 进行基准测试

标签 java concurrency java-8

<分区>

尽管通过重复进行“预热”,并且算法增加了一些复杂性,parallelSetAll() 在这里似乎始终较慢。我并不是真的想在这里进行微观基准测试,只是对正在发生的事情有一个粗略的感觉。

import java.util.*;
import java.time.*;

public class SlowParallelSetAll {
  static final int SIZE = 20_000_000;
  static long timeIt(Runnable test) {
    Instant start = Instant.now();
    test.run();
    long millis = Duration.between(start, Instant.now()).toMillis();
    System.out.println(millis);
    return millis;
  }
  public static void main(String[] args) {
    int reps = 10;
    long[] la = new long[SIZE];
    for(int i = 0; i < reps; i++)
      timeIt(() -> Arrays.setAll(la, n -> n * n * 11 + n * 7));
    System.out.println("###");
    for(int i = 0; i < reps; i++)
      timeIt(() -> Arrays.parallelSetAll(la, n -> n * n * 11 + n * 7));
  }
}
/* Output:
38
37
35
34
35
34
35
34
34
35
###
52
42
43
44
46
46
44
44
43
43
*/

(lambda 表达式)算法应该是独立的,因为它只依赖于索引值 n,因此看起来应该很容易并行化。

四处移动调用、交替使用两种方法等确实会产生不同的结果,但似乎这里除了微基准测试噪音之外还有更多的东西。一方面,我期待正常版本和并行版本之间有更大的时间差异。此外,似乎人们可能会偶然发现并行版本似乎是正确选择但普通版本实际上更合适的情况。基本上,我正在寻找对此的一些见解 --- 包括是否有一些简单的方法可以证明我所看到的纯粹是一种微基准测试现象。

为了它的值(value),这里它被重写为使用 System.nanoTime() 并且只是交替测试。这些结果似乎是合理的,尽管整个微基准测试问题令人生畏:

import java.util.*;
import java.time.*;
import java.util.concurrent.*;

public class SlowParallelSetAll {
  static final int SIZE = 20_000_000;
  static long timeIt(Runnable test) {
    long start = System.nanoTime();
    test.run();
    long delta = System.nanoTime() - start;
    long millis = TimeUnit.NANOSECONDS.toMillis(delta);
    System.out.println(millis);
    return millis;
  }
  public static void main(String[] args) {
    int reps = 10;
    long[] la = new long[SIZE];
    for(int i = 0; i < reps; i++) {
      timeIt(() -> Arrays.parallelSetAll(la, n -> n * n * 11 + n * 7));
      timeIt(() -> Arrays.setAll(la, n -> n * n * 11 + n * 7));
    }
  }
}
/* Output:
41
74
41
73
41
67
40
67
40
67
41
67
41
67
40
67
40
67
40
67
*/

最佳答案

使用 jmh,我得到以下结果(大小是数组的长度)。分数是每次调用的运行时间,以微秒为单位(更小 = 更好)。

Benchmark                     (size)  Mode  Cnt       Score       Error  Units
SO34929316.parallelSetAll          1  avgt   20       0.077 ±     0.003  us/op
SO34929316.parallelSetAll       1000  avgt   20       9.935 ±     0.478  us/op
SO34929316.parallelSetAll     100000  avgt   20      56.024 ±     7.008  us/op
SO34929316.parallelSetAll    1000000  avgt   20     518.495 ±    75.331  us/op
SO34929316.parallelSetAll   10000000  avgt   20    5640.574 ±   139.324  us/op
SO34929316.setAll                  1  avgt   20       0.016 ±     0.001  us/op
SO34929316.setAll               1000  avgt   20       1.257 ±     0.023  us/op
SO34929316.setAll             100000  avgt   20     116.760 ±     3.116  us/op
SO34929316.setAll            1000000  avgt   20    1168.868 ±    42.153  us/op
SO34929316.setAll           10000000  avgt   20   12347.399 ±   766.931  us/op

如您所料,parallelSetAll 对于较大的数组更快,对于较小的数组则更慢。然而,根据测试(在 Win 8.1/Xeon 2630 v2/6 核 (x2) 上运行),加速因子仅为 ~ x2。


供引用,代码:

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO34929316 {

  @Param({"1", "1000", "100000", "1000000", "10000000"}) int size;
  long[] array;

  @Setup(Level.Iteration)
  public void setup(){
    array = new long[size];
  }

  @Benchmark
  public void setAll(Blackhole bh) {
    Arrays.setAll(array, n -> n * n * 11 + n * 7);
    bh.consume(array);
  }

  @Benchmark
  public void parallelSetAll(Blackhole bh) {
    Arrays.parallelSetAll(array, n -> n * n * 11 + n * 7);
    bh.consume(array);
  }
}

关于java - 尝试对 Java 8 parallelSetAll() 与 setAll() 进行基准测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34929316/

相关文章:

c# - 检查控制变量后调用 AutoResetEvent.WaitOne

java - AspectJ EDT-Checker 代码问题

java - 动态找到正确的方法以避免重复相同的代码

Java 8 - 获取列表元素的 'parent' 对象

java - 访问 jList/listModel 中对象的元素

Java ExecutorService invokeAll() 中断

java - 如何消除类似方法中重复的try-catch代码?

grails - IntelliJ Grails 3 项目 : Unable to Run Grails project

java - 在内存中压缩并序列化一个大的HashMap

java - 等待事件发生的队列