我正在进行以下实验,并且意外地意识到两次运行之间没有可测量的差异:
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
答案 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
。