测量 java.io.InputStream 的性能

我有一个 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

http://img4.mukewang.com/63abaf28000164e507000065.jpg

由于 JNI 规范定义了

在临界区内,本机代码不得调用其他 JNI 函数,或任何可能导致当前线程阻塞并等待另一个 Java 线程的系统调用。(例如,当前线程不得在另一个 Java 线程写入的流上调用 read。)


潇潇雨雨
浏览 99回答 1
1回答

繁花不似锦

带有 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 启动的年轻 GCJDK-8057573如果 GCLocker 处于活动状态,则忽略 CMSScavengeBeforeRemarkJDK-8057586如果 GCLocker 处于活动状态,则忽略显式 GC所以,问题是你关心的是吞吐量还是延迟。如果您只需要更高的吞吐量,JNI critical 可能是正确的选择。但是,如果您还关心可预测的延迟(不是平均延迟,而是 99.9%),那么 JNI critical 似乎不是一个好的选择。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java