衡量java.io.InputStream的性能 [英] Measuring performance of java.io.InputStream

查看:96
本文介绍了衡量java.io.InputStream的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个5GB的文件,我想按块读取,例如2MB.使用java.io.InputStream可以正常工作.因此,我对这件事进行了如下测量:

I have a file 5GB in size which I want to read by chunks, say 2MB. Using java.io.InputStream works fine. So I measured this thing as follows:

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

RESULT = 2121714428

可以看出,平均需要2121714428纳米.之所以如此,是因为该实现会将(*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);的数据读入malloc ed或堆栈分配的缓冲区中,如

It can be seen that averagely it takes 2121714428 nanos. It is so because the implementation does (*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf); of the data read into a malloced or stack allocated buffer as shown here. So memcpy takes pretty large amount of CPU time:

由于JNI规范定义了

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

Inside a critical region, native code must not call other JNI functions, or any system call that may cause the current thread to block and wait for another Java thread. (For example, the current thread must not call read on a stream being written by another Java thread.)

在关键部分的 常规文件 中,我看不到有任何问题.从常规文件读取仅被短暂阻止,并且不依赖于任何Java线程.像这样:

I don't see any problems to do read from a regular file within a critical section. Reading from a regular file is blocked only briefly and does not depend on any java thread. Something like this:

static final byte[] buffer = new byte[2 * 1024 * 1024];

public static void main(String args[]) throws IOException {
    while (true) {
        int fd = open("/tmp/log_test.log");
        long bytesRead = 0;
        int readCurrent;
        long start = System.nanoTime();
        while ((readCurrent = read(fd, buffer)) > 0) {
            bytesRead += readCurrent;
        }
        long end = System.nanoTime();
        System.out.println("Bytes read = " + bytesRead + ". Time elapsed = " + (end - start));
    }
}

private static native int open(String path);

private static native int read(int fd, byte[] buf);

JNI函数:

JNIEXPORT jint JNICALL Java_com_test_Main_open
  (JNIEnv *env, jclass jc, jstring path){
    const char *native_path = (*env)->GetStringUTFChars(env, path, NULL);
    int fd = open(native_path, O_RDONLY);
    (*env)->ReleaseStringUTFChars(env, path, native_path);
    return fd;
}


JNIEXPORT jint JNICALL Java_com_test_Main_read
  (JNIEnv *env, jclass jc, jint fd, jbyteArray arr){
    size_t java_array_size = (size_t) (*env)->GetArrayLength(env, arr);
    void *buf = (*env)->GetPrimitiveArrayCritical(env, arr, NULL);
    ssize_t bytes_read = read(fd, buf, java_array_size);
    (*env)->ReleasePrimitiveArrayCritical(env, arr, buf, 0);
    return (jint) bytes_read;
}

结果= 1179852225

RESULT = 1179852225

以循环方式运行此程序平均需要1179852225纳米,这几乎是效率的两倍.

Runnning this in a loop it takes averagely 1179852225 nanos which is almost twice more efficient.

问题: 从关键区域中的 常规文件 中读取数据的实际问题是什么?

Question: What's the actual problem with reading from a regular file within critical section?

推荐答案

带有FileInputStream的2MB缓冲区可能不是最佳选择.请参见类似的性能问题在Linux上.根据操作系统的不同,分配临时大缓冲区可能会导致额外的mmap调用和后续的页面错误.如此大的缓冲区也使L1/L2缓存无用.

2MB buffer with FileInputStream is probably not the best choice. See this question for details. Although it was on Windows, I've seen a similar performance issue on Linux. Depending on the OS, allocating a temporary large buffer may result in extra mmap calls and subsequent page faults. Also such a large buffer makes L1/L2 caches useless.

从常规文件读取仅被短暂阻止,并且不会 取决于任何Java线程.

Reading from a regular file is blocked only briefly and does not depend on any java thread.

这并不总是正确的.在您的基准测试中,文件显然已缓存在OS页面缓存中,并且没有发生设备I/O.访问实际硬件(尤其是旋转磁盘)的速度可能要慢几个数量级.磁盘I/O的最坏时间无法完全预测-可能长达数百毫秒,具体取决于硬件条件,I/O队列的长度,调度策略等.

This is not always true. In your benchmark the file is apparently cached in OS page cache and no device I/O happens. Accessing the real hardware (especially a spinning disk) can be orders of magnitude slower. The worst time of disk I/O is not fully predictable - it can be as large as hundreds of milliseconds, depending on the hardware condition, the length of I/O queue, the scheduling policy and so on.

JNI关键部分的问题是每当发生延迟时,它可能会影响所有线程,不仅影响I/O.对于单线程应用程序来说,这不是问题,但是这可能会导致多线程应用程序出现意外的停顿停顿.

The problem with JNI critical section is whenever a delay happens, it may affect all threads, not only the one doing I/O. This is not an issue for a single-threaded application, but this may cause undesirable stop-the-world pauses in a multi-threaded app.

反对JNI严重的另一个原因是与GCLocker相关的JVM错误.有时它们可​​能会导致多余的GC循环或忽略某些GC标志.以下是一些示例(仍未解决):

The other reason against JNI critical is JVM bugs related to GCLocker. Sometimes they may cause redundant GC cycles or ignoring certain GC flags. Here are some examples (still not fixed):

  • JDK-8048556 不必要的由GCLocker启动的年轻GC
  • JDK-8057573 如果GCLocker处于活动状态,则CMSScavengeBeforeRemark将被忽略
  • JDK-8057586 如果GCLocker处于活动状态,则显式GC将被忽略
  • >
  • JDK-8048556 Unnecessary GCLocker-initiated young GCs
  • JDK-8057573 CMSScavengeBeforeRemark ignored if GCLocker is active
  • JDK-8057586 Explicit GC ignored if GCLocker is active

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

So, the question is whether you care about throughput or latency. If you need only higher throughput, JNI critical is probably the right way to go. However, if you also care about predictable latency (not the average latency, but say, 99.9%) then JNI critical does not seem like the good choice.

这篇关于衡量java.io.InputStream的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆