为了账号安全,请及时绑定邮箱和手机立即绑定

测量 java.io.InputStream 的性能

测量 java.io.InputStream 的性能

潇潇雨雨 2022-12-28 10:50:59
我有一个 5GB 大小的文件,我想按块读取,比如 2MB。使用java.io.InputStream效果很好。所以我测量了这个东西如下:static final byte[] buffer = new byte[2 * 1024 * 1024];public static void main(String args[]) throws IOException {    while(true){        InputStream is = new FileInputStream("/tmp/log_test.log");        long bytesRead = 0;        int readCurrent;        long start = System.nanoTime();        while((readCurrent = is.read(buffer)) > 0){            bytesRead += readCurrent;        }        long end = System.nanoTime();        System.out.println(            "Bytes read = " + bytesRead + ". Time elapsed = " + (end - start)        );    }}结果 = 2121714428可以看出平均需要2121714428纳米。之所以如此,是因为实现确实(*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);将数据读入了ed 或堆栈分配的缓冲区,如此处malloc所示。所以需要相当多的 CPU 时间:memcpy由于 JNI 规范定义了在临界区内,本机代码不得调用其他 JNI 函数,或任何可能导致当前线程阻塞并等待另一个 Java 线程的系统调用。(例如,当前线程不得在另一个 Java 线程写入的流上调用 read。)
查看完整描述

1 回答

?
繁花不似锦

TA贡献1851条经验 获得超4个赞

带有 FileInputStream 的 2MB 缓冲区可能不是最佳选择。有关详细信息,请参阅此问题。虽然它是在 Windows 上,但我在 Linux 上看到了类似的性能问题。根据操作系统的不同,分配一个临时的大缓冲区可能会导致额外的mmap调用和随后的页面错误。如此大的缓冲区也使 L1/L2 缓存变得无用。

从常规文件中读取只会被短暂阻塞,并且不依赖于任何 Java 线程。

这并非总是如此。在您的基准测试中,文件显然缓存在操作系统页面缓存中,并且没有发生设备 I/O。访问真实硬件(尤其是旋转磁盘)可能要慢几个数量级。磁盘 I/O 的最坏时间是不完全可以预测的——它可以大到数百毫秒,这取决于硬件条件、I/O 队列的长度、调度策略等。

JNI 临界区的问题是每当发生延迟时,它可能会影响所有线程,而不仅仅是执行 I/O 的线程。这对于单线程应用程序来说不是问题,但这可能会导致多线程应用程序中出现不需要的 stop-the-world 暂停。

反对 JNI 的另一个关键原因是与 GCLocker 相关的 JVM 错误。有时它们可能会导致冗余的 GC 周期或忽略某些 GC 标志。以下是一些示例(仍未修复):

  • JDK-8048556不必要的 GCLocker 启动的年轻 GC

  • JDK-8057573如果 GCLocker 处于活动状态,则忽略 CMSScavengeBeforeRemark

  • JDK-8057586如果 GCLocker 处于活动状态,则忽略显式 GC

所以,问题是你关心的是吞吐量还是延迟。如果您只需要更高的吞吐量,JNI critical 可能是正确的选择。但是,如果您还关心可预测的延迟(不是平均延迟,而是 99.9%),那么 JNI critical 似乎不是一个好的选择。


查看完整回答
反对 回复 2022-12-28
  • 1 回答
  • 0 关注
  • 97 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信