简体   繁体   English

衡量java.io.InputStream的性能

[英]Measuring performance of java.io.InputStream

I have a file 5GB in size which I want to read by chunks, say 2MB. 我有一个5GB的文件,我想按块读取,例如2MB。 Using java.io.InputStream works fine. 使用java.io.InputStream可以正常工作。 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)
        );
    }
}

RESULT = 2121714428 结果= 2121714428

It can be seen that averagely it takes 2121714428 nanos. 可以看出,平均需要2121714428纳米。 It is so because the implementation does (*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf); 之所以如此,是因为该实现确实执行(*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf); of the data read into a malloc ed or stack allocated buffer as shown here . 的数据的读入malloc ED或堆栈分配的缓冲区,如图这里 So memcpy takes pretty large amount of CPU time: 因此memcpy需要大量的CPU时间:

在此处输入图片说明

Since JNI spec defines that 由于JNI规范定义了

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. 在关键区域内,本机代码不得调用其他JNI函数或任何可能导致当前线程阻塞并等待另一个Java线程的系统调用。 (For example, the current thread must not call read on a stream being written by another Java thread.) (例如,当前线程不得在另一个Java线程正在写入的流上调用read。)

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. 从常规文件读取仅被短暂阻止,并且不依赖于任何Java线程。 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 functions: 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;
}

RESULT = 1179852225 结果= 1179852225

Runnning this in a loop it takes averagely 1179852225 nanos which is almost twice more efficient. 循环运行此过程平均需要1179852225纳米,这几乎是效率的两倍。

Question: What's the actual problem with reading from a regular file within critical section? 问题:从关键部分的常规文件中读取的实际问题是什么?

2MB buffer with FileInputStream is probably not the best choice. 带有FileInputStream的2MB缓冲区可能不是最佳选择。 See this question for details. 有关详细信息,请参见此问题 Although it was on Windows, I've seen a similar performance issue on Linux. 尽管它在Windows上,但在Linux上我也遇到了类似的性能问题 Depending on the OS, allocating a temporary large buffer may result in extra mmap calls and subsequent page faults. 根据操作系统的不同,分配临时大缓冲区可能会导致额外的mmap调用和后续的页面错误。 Also such a large buffer makes L1/L2 caches useless. 同样,如此大的缓冲区也会使L1 / L2缓存无效。

Reading from a regular file is blocked only briefly and does not depend on any java thread. 从常规文件读取仅被短暂阻止,并且不依赖于任何Java线程。

This is not always true. 这并非总是如此。 In your benchmark the file is apparently cached in OS page cache and no device I/O happens. 在您的基准测试中,文件显然已缓存在OS页面缓存中,并且没有发生设备I / O。 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. 磁盘I / O的最坏时间无法完全预测-可能长达数百毫秒,具体取决于硬件条件,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. JNI关键部分的问题是,每当发生延迟时,它就可能影响所有线程,而不仅仅是影响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. 对于单线程应用程序来说,这不是问题,但是这可能会导致多线程应用程序中的世界停顿。

The other reason against JNI critical is JVM bugs related to GCLocker . 反对JNI的另一个重要原因是与GCLocker相关的JVM错误 Sometimes they may cause redundant GC cycles or ignoring certain GC flags. 有时,它们可能会导致多余的GC循环或忽略某些GC标志。 Here are some examples (still not fixed): 以下是一些示例(仍未解决):

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

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. 如果只需要更高的吞吐量,那么JNI关键可能是正确的选择。 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. 但是,如果您还关注可预测的延迟(不是平均延迟,而是说99.9%),那么JNI关键似乎不是一个好选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM