jmh:对环境的奇怪依赖

lymgl2op  于 2021-07-12  发布在  Java
关注(0)|答案(0)|浏览(193)

当我第一次使用 JMH 为了给我的课做个基准测试,我遇到了一个让我困惑的行为,我想在继续之前澄清一下这个问题。
让我困惑的是:
当我运行基准测试时,cpu被外部进程加载(78%-80%),结果如下 JMH 看起来相当可信和稳定:

Benchmark                                  Mode  Cnt    Score   Error  Units
ArrayOperations.a_bigDecimalAddition       avgt    5  264,703 ± 2,800  ns/op
ArrayOperations.b_quadrupleAddition        avgt    5   44,290 ± 0,769  ns/op
ArrayOperations.c_bigDecimalSubtraction    avgt    5  286,266 ± 2,454  ns/op
ArrayOperations.d_quadrupleSubtraction     avgt    5   46,966 ± 0,629  ns/op
ArrayOperations.e_bigDecimalMultiplcation  avgt    5  546,535 ± 4,988  ns/op
ArrayOperations.f_quadrupleMultiplcation   avgt    5   85,056 ± 1,820  ns/op
ArrayOperations.g_bigDecimalDivision       avgt    5  612,814 ± 5,943  ns/op
ArrayOperations.h_quadrupleDivision        avgt    5  631,127 ± 4,172  ns/op

相对较大的误差是因为我现在只需要一个粗略的估计,我故意用精确来换取快速。
但是在处理器上没有额外负载的情况下获得的结果对我来说是惊人的:

Benchmark                                  Mode  Cnt    Score     Error  Units
ArrayOperations.a_bigDecimalAddition       avgt    5  684,035 ± 370,722  ns/op
ArrayOperations.b_quadrupleAddition        avgt    5   83,743 ±  25,762  ns/op
ArrayOperations.c_bigDecimalSubtraction    avgt    5  531,430 ± 184,980  ns/op
ArrayOperations.d_quadrupleSubtraction     avgt    5   85,937 ± 103,351  ns/op
ArrayOperations.e_bigDecimalMultiplcation  avgt    5  641,953 ± 288,545  ns/op
ArrayOperations.f_quadrupleMultiplcation   avgt    5  102,692 ±  31,625  ns/op
ArrayOperations.g_bigDecimalDivision       avgt    5  733,727 ± 161,827  ns/op
ArrayOperations.h_quadrupleDivision        avgt    5  820,388 ± 546,990  ns/op

一切似乎都慢了近两倍,迭代时间非常不稳定(在相邻迭代中可能从500到1300 ns/op不等),并且错误分别大得令人无法接受。
第一组结果是通过运行一组应用程序获得的,包括folding@home 分布式计算客户端( FahCore_a7.exe )它占用了75%的cpu时间,一个bittorrent客户端主动使用磁盘、浏览器中的十几个选项卡、电子邮件客户端等。平均cpu负载约为85%。基准执行期间 FahCore 减少负载,以便 Java 占25%,总负荷为100%。
第二组结果是当所有不必要的进程都停止时,cpu实际上是空闲的,只有 Java 需要25%和几个百分比用于系统需求。
我的cpu是intel i5-4460,4内核,3.2 ghz,ram 32 gb,os windows server 2008 r2。
java版本“1.8.0\U 231”
java(tm)se运行时环境(build 1.8.0çu 231-b11)
java hotspot(tm)64位服务器vm(内部版本25.231-b11,混合模式)
问题是:
为什么基准测试会显示出更糟糕和不稳定的结果,而它是装载机器的唯一任务?
当第一组结果如此显著地依赖于环境时,我能认为它们或多或少是可靠的吗?
我是否应该以某种方式设置环境以消除这种依赖关系?
还是应该怪我的代码?
代码:

package com.mvohm.quadruple.benchmarks;

// Required imports here

import com.mvohm.quadruple.Quadruple; // The class under tests

@State(value = Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(java.util.concurrent.TimeUnit.NANOSECONDS)
@Fork(value = 1)
@Warmup(iterations = 3, time = 7)
@Measurement(iterations = 5, time = 10)
public class ArrayOperations {

  // To do BigDecimal arithmetic with the precision close to this of Quadruple
  private static final MathContext MC_38 = new MathContext(38, RoundingMode.HALF_EVEN);

  private static final int DATA_SIZE = 0x1_0000;        // 65536
  private static final int INDEX_MASK = DATA_SIZE - 1;  // 0xFFFF

  private static final double RAND_SCALE = 1e39; // To provide a sensible range of operands,
                                                 // so that the actual calculations don't get bypassed

  private final BigDecimal[]      // Data to apply operations to
      bdOp1     = new BigDecimal[DATA_SIZE],  // BigDecimals 
      bdOp2     = new BigDecimal[DATA_SIZE],
      bdResult  = new BigDecimal[DATA_SIZE];
  private final Quadruple[]
      qOp1      = new Quadruple[DATA_SIZE],   // Quadruples
      qOp2      = new Quadruple[DATA_SIZE],
      qResult   = new Quadruple[DATA_SIZE];

  private int index = 0;

  @Setup
  public void initData() {
    final Random rand = new Random(12345); // for reproducibility
    for (int i = 0; i < DATA_SIZE; i++) {
      bdOp1[i] = randomBigDecimal(rand);
      bdOp2[i] = randomBigDecimal(rand);
      qOp1[i] = randomQuadruple(rand);
      qOp2[i] = randomQuadruple(rand);
    }
  }

  private static Quadruple randomQuadruple(Random rand) {
    return Quadruple.nextNormalRandom(rand).multiply(RAND_SCALE); // ranged 0 .. 9.99e38
  }

  private static BigDecimal randomBigDecimal(Random rand) {
    return Quadruple.nextNormalRandom(rand).multiply(RAND_SCALE).bigDecimalValue();
  }

  @Benchmark
  public void a_bigDecimalAddition() {
    bdResult[index] = bdOp1[index].add(bdOp2[index], MC_38);
    index = ++index & INDEX_MASK;
  }

  @Benchmark
  public void b_quadrupleAddition() {
    // semantically the same as above 
    qResult[index] = Quadruple.add(qOp1[index], qOp2[index]); 
    index = ++index & INDEX_MASK;
  }

  // Other methods are similar 

  public static void main(String... args) throws IOException, RunnerException {
    final Options opt = new OptionsBuilder()
        .include(ArrayOperations.class.getSimpleName())
        .forks(1)
        .build();
    new Runner(opt).run();
  }

}

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题