为什么新的Something()和Something.class.newinstance()之间没有可测量的差异?

时间:2017-03-13 19:32:50

标签: java performance

我正在进行以下实验,并且意外地意识到两次运行之间没有可测量的差异:

public static void main(String[] args) throws Exception {
    long count = 1_000_000_0L;
    long measureCount = 1000L;


    // Measurement 1
    SpecificRecord tp1 = null;
    List<Long> times = new ArrayList<>();
    for (long j=0; j<measureCount; ++j) {
        long timeStart = System.currentTimeMillis();
        for (long i = 0; i < count; ++i) {
            tp1 = new WebPageView();
        }
        times.add(System.currentTimeMillis() - timeStart);
    }
    Stats st = Stats.of(times);
    double avg = st.mean();
    double stdDev = st.populationStandardDeviation();
    times.sort(Long::compareTo);
    int upper = (int)Math.ceil(times.size()/2.0);
    int lower = (int)Math.floor(times.size()/2.0);
    double median = ((times.get(upper))+(times.get(lower)))/2.0;
    System.out.println("avg: "+avg);
    System.out.println("stdDev: "+stdDev);
    System.out.println("median: "+median);
    System.out.println(tp1);


    // Measurement 2
    SpecificRecord tp2 = null;
    List<Long> times2 = new ArrayList<>();
    for (long j=0; j<measureCount; ++j) {
        long timeStart = System.currentTimeMillis();
        for (long i = 0; i < count; ++i) {
            tp2 = WebPageView.class.newInstance();
        }
        times2.add(System.currentTimeMillis() - timeStart);
    }
    Stats st2 = Stats.of(times2);
    double avg2 = st2.mean();
    double stdDev2 = st2.populationStandardDeviation();
    times2.sort(Long::compareTo);
    int upper2 = (int)Math.ceil(times2.size()/2.0);
    int lower2 = (int)Math.floor(times2.size()/2.0);
    double median2 = ((times2.get(upper2))+(times2.get(lower2)))/2.0;
    System.out.println("avg: "+avg2);
    System.out.println("stdDev: "+stdDev2);
    System.out.println("median: "+median2);
    System.out.println(tp2);
  }
}

结果:

avg: 110.63300000000005
stdDev: 47.07256431298379
median: 100.0
{"aid": 0, "uid": 0, "rid": 0, "sid": 0, "d": null, "p": null, "r": null, "f": null, "q": null, "ts": 0}
avg: 101.0840000000001
stdDev: 7.8092857547921835
median: 99.0
{"aid": 0, "uid": 0, "rid": 0, "sid": 0, "d": null, "p": null, "r": null, "f": null, "q": null, "ts": 0}

UPDATE1:

你们中的许多人都指出,由于大量优化可以对新的Something()和Something.class.newinstance()之间的性能差异进行分析,因此无法以这种方式对JVM进行基准测试。

UPDATE2:

用建议的方法重复测试后,结果让我感到惊讶:

Benchmark                   Mode  Cnt   Score   Error  Units
ReflectionTest.newInstance  avgt    5  12.923 ± 0.801  ns/op
ReflectionTest.newOperator  avgt    5  11.524 ± 0.289  ns/op

UPDATE3: 测试代码:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class ReflectionTest {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(ReflectionTest.class.getSimpleName()).forks(1).build();
        new Runner(opt).run();
    }

    @Benchmark
    public WebPageView newOperator() {
        return new WebPageView();
    }

    @Benchmark
    public WebPageView newInstance() throws InstantiationException, IllegalAccessException {
        return WebPageView.class.newInstance();
    }
}

这个问题仍然没有答案,没有测试可以概述class.newInstance()与我们使用的类之间的任何区别。该类使用的是Avro SpecificRecord的实现。

public class WebPageView extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord

1 个答案:

答案 0 :(得分:4)

修改

测试仍然很好,但它与jdk-9一起运行,其中证明比jdk-8慢得多。

使用jdk-8运行,将证明JIT正在进行大量优化,真正的差异在于 2x

使用jmh进行一些显示差异很大的测试(使用jdk-9 运行

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class TestNewObject {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(TestNewObject.class.getSimpleName()).forks(1).build();
        new Runner(opt).run();
    }

    @Benchmark
    public Something newOperator() {
        return new Something();
    }

    @Benchmark
    public Something newInstance() throws InstantiationException, IllegalAccessException {
        return Something.class.newInstance();
    }

    static class Something {

    }
}

结果显示出相当大的差异。

Benchmark                  Mode  Cnt    Score    Error  Units
TestNewObject.newInstance  avgt    5  274.070 ± 50.554  ns/op
TestNewObject.newOperator  avgt    5    5.119 ±  3.550  ns/op

速度差异 44x 。对newInstance的调用会执行许多(检查源代码)非常昂贵的事情(反射和安全检查)。

虽然考虑TLAB的新运算符是分配对象的一种非常快速的方法。

有一点需要注意的是,jdk-9中不推荐使用newInstance