java - ByteArrayOutputStream 会发生什么样的 JVM 优化?

标签 java performance performance-testing benchmarking jmh

我有以下 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 优化?

最佳答案

假设您在 5M ops/s 下得到了稳定的结果,这可信吗? 为了便于讨论,我们假设一个 3GHz CPU(您可能使用的是开启频率缩放和睿频加速的笔记本电脑,但无论如何),5M ops/s => 每个 op 200ns => 600 个周期。我们要求 CPU 做什么?

  • 分配ByteArrayOutputStream,默认构造函数 -> new byte[32], + 更改
  • 简单的计数循环,65次,将一个字节写入数组
  • 调整字节数组大小 2 次。 32 -> 64 -> 128
  • 复制到新数组 (65) 并返回
  • JMH 的简单循环计算

我们希望发生什么样的优化?

  • 从解释器到 native 编译(废话)
  • 循环展开和大量循环优化,所有这些可能都没有多大帮助
  • 对 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 header )= 60,并且“ByteArrayOutputStream”上的数组长度 + 计数字段 = 20。因此,根据我的计算,更改应该是 80b,但我可能遗漏了某物。底线是,我们没有逃脱分析。但一些 CompressedOop 会有所帮助。您可以使用分配分析器(如 JVisualVM 中的分配分析器)来跟踪此处的所有不同分配,也可以使用采样分配分析器(如 Java Mission Control 中的分配分析器)。

您可以使用-prof perfasm查看该级别的程序集输出和配置文件。这是一个很长的练习,所以我不打算在这里详细介绍。您可以看到的一项很酷的优化是,JVM 不会将其在方法结束时创建的新数组副本归零。您还可以看到,数组的分配和复制是按预期花费时间的地方。

最后,这里进行的明显优化只是 JIT 编译。您可以使用 JITWatch 等工具来探索每个编译级别的作用。您可以使用命令行标志来查找每个编译级别的性能(-jvmArgs=-Xint' 在解释器中运行-XX:TieredStopAtLevel=1` 以在 C1 处停止)。

另一个大规模优化是扩展堆以适应分配率。您可以试验堆大小以了解这对性能有何影响。

玩得开心:-)

关于java - ByteArrayOutputStream 会发生什么样的 JVM 优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50867157/

相关文章:

java - 如何在不破坏 Maven 发布插件的情况下传递 javac 多个命令行参数,其中一些参数包括冒号?

c++ - Intel Xeon Phi 上的动态内存变慢

Android 大量跳帧错误

vba - 识别 VBA UDF 瓶颈

android - 需要一个应用程序/应用程序来测量页面在 Android、iPhone 和 iPad 设备上的加载时间

multithreading - 如何在 Gatling 中实现特定的线程数

java - Maven 依赖于项目——没有 jar,只有类

java - 如何使用 Comparator 类来处理 double 对象的数据标记?

java - 套接字传输的文件: contents are empty

performance - Redis集群性能——低负载超时率高