我有以下JMH基准(Java8):
@Benchmark
public byte[] outputStream() {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < size; i++) {
baos.write(i);
}
return baos.toByteArray();
}例如,当size == 65输出如下:
# Warmup Iteration 1: 3296444.108 ops/s
# Warmup Iteration 2: 2861235.712 ops/s
# Warmup Iteration 3: 4909462.444 ops/s
# Warmup Iteration 4: 4969418.622 ops/s
# Warmup Iteration 5: 5009353.033 ops/s
Iteration 1: 5006466.075 ops/sm 19s]
...显然,在热身第二阶段发生了一些事情,所以在热身之后会有一个巨大的加速。
我怎么知道在这一点上发生了什么样的JVM优化?
发布于 2018-06-15 15:42:38
假设你有一个稳定的结果在5米操作/秒,这可信吗?为了便于论证,让我们假设一个3 3GHz的CPU(您可能在一台具有频率缩放和涡轮增压的膝上型计算机上,但无论如何),每个op => 600周期有5MOPS/s的=> 200‘s。我们让CPU做什么了?
ByteArrayOutputStream,默认构造函数->新byte32,+更改我们希望会发生什么样的优化?
ByteArrayOutputStream及其伙伴分词的逃逸分析。我不认为这是真的。我怎么知道发生了什么?我会用一些有用的分析器来运行它。JMH提供了很多这样的服务。
有了-prof gc,我可以看到这里的分配率是多少:·gc.alloc.rate.norm: 360.000 B/op,所以,我猜到了32 + 64 + 128 + 65 + change = 289b + change => change = 71b,这是变化的分配,对吗?如果你考虑了对象头的话就不会了。我们有4个数组和一个对象=> 5* 12 (压缩oops头)= 60,以及‘`ByteArrayOutputStream’= 20上的数组长度+计数字段。因此,根据我的计算,更改应该是80b,但我可能遗漏了一些东西。底线是,我们没有EscapeAnalysis。但是一些CompressedOops能帮上忙。您可以使用分配分析器(比如JVisualVM中的配置分析器)来跟踪这里的所有不同的分配,也可以使用抽样分配分析器(如Java任务控制中的配置分析器)。
您可以使用-prof perfasm查看该级别的程序集输出和配置文件。这是一个很长的练习,所以我不打算在这里进行。您可以看到的一个很酷的优化是,JVM没有将它在方法末尾生成的新数组副本归零。您还可以看到数组的分配和复制是按预期花费时间的地方。
最后,这里最明显的优化就是JIT编译。通过使用像JITWatch这样的工具,您可以了解每个级别的编译都做了什么。您可以使用命令行标志来查找每个编译级别的性能(-jvmArgs=-Xint' to run in the interpreter,-XX:TieredStopAtLevel=1`,停止在C1)。
另一个正在发挥作用的大规模优化是堆的扩展以适应分配速率。您可以对堆大小进行试验,以了解这对性能的影响。
玩得开心:-)
https://stackoverflow.com/questions/50867157
复制相似问题