Java:InputStream 读取大文件太慢

Posted

技术标签:

【中文标题】Java:InputStream 读取大文件太慢【英文标题】:Java: InputStream too slow to read huge files 【发布时间】:2012-05-06 20:09:37 【问题描述】:

我必须一个字符一个字符地读取一个 53 MB 的文件。当我使用 ifstream 在 C++ 中执行此操作时,它会在几毫秒内完成,但使用 Java InputStream 则需要几分钟。 Java 这么慢是正常的还是我错过了什么?

另外,我需要用 Java 完成程序(它使用 servlet,我必须从中调用处理这些字符的函数)。我在想也许用 C 或 C++ 编写文件处理部分,然后使用 Java Native Interface 将这些函数与我的 Java 程序接口......这个想法如何?

谁能给我任何其他提示...我真的需要更快地阅读文件。我尝试使用缓冲输入,但它仍然没有提供接近 C++ 的性能。

已编辑:我的代码跨越多个文件,而且很脏,所以我给出了概要

import java.io.*;

public class tmp 
    public static void main(String args[]) 
        try
        InputStream file = new BufferedInputStream(new FileInputStream("1.2.fasta"));
        char ch;        
        while(file.available()!=0) 
            ch = (char)file.read();
                    /* Do processing */
            
        System.out.println("DONE");
        file.close();
        catch(Exception e)
    

【问题讨论】:

向我们展示您的代码。如果不了解您的工作方式,我们无法猜测您的问题。 你在使用BufferedInputStream吗?你应该在BufferedReader 上使用它。您的访问模式是否可以使用java.nio 对文件的部分进行内存映射?具体来说,当您说“char by char”时,您是否足够了解编码以处​​理 chars 其字节序列可能分布在多个内存映射段中? 没有办法只读取那些 53M 字符而不做任何其他事情可能需要超过几秒钟,缓冲或不缓冲。肯定还有别的东西。 逐字阅读可能是您的问题。 您错误地使用了file.available()。试试这个,while((ch = (char)file.read()) >= 0) 并删除 ch = (char)file.read(); 【参考方案1】:

我使用 183 MB 的文件运行此代码。它打印“Elapsed 250 ms”。

final InputStream in = new BufferedInputStream(new FileInputStream("file.txt"));
final long start = System.currentTimeMillis();
int cnt = 0;
final byte[] buf = new byte[1000];
while (in.read(buf) != -1) cnt++;
in.close();
System.out.println("Elapsed " + (System.currentTimeMillis() - start) + " ms");

【讨论】:

不错。另外,我需要逐字符处理文件。因此,我不会从文件中读取单个字符,而是从缓冲区中检索它,如果它用完,请再次填充它。非常感谢:) 是的,我认为 Java 在方法调度方面陷入了困境,而 C++ 甚至可能内联调用。有时在调用足够多之后,HotSpot 也会内联调用,但我不能确定这种情况。 @MarkoTopolnik 除了调用 InputStream.available() 5300 万次(即 5300 万次冗余系统调用)之外,这里没有证据表明 Java 在任何事情上都“陷入困境”。由于他使用的是 BufferedInputStream,实际读取文件的系统调用次数为 53/8192 百万,因此调用 available() 是一个巨大的开销。 @MarkoTopolnik 我用BufferedInputStream.read(byte[]) 得到 676 毫秒; FileInputStream.read(byte[]) 为 2441 毫秒,BufferedInputStream.read() 为 610 毫秒(一次一个字节)。具有随机内容的不同文件(以避免缓存),全部为 183MB,1000 字节缓冲区。 Java 6. 多次运行的结果非常一致。没有 50 倍问题的迹象。 @EJP 好的,所以我们可能在谈论 OS X 上的糟糕实现。在阅读了您的结果后,我仔细检查了我的结果。这是真的。【参考方案2】:

我会试试这个

// create the file so we have something to read.
final String fileName = "1.2.fasta";
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(new byte[54 * 1024 * 1024]);
fos.close();

// read the file in one hit.
long start = System.nanoTime();
FileChannel fc = new FileInputStream(fileName).getChannel();
ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
while (bb.remaining() > 0)
    bb.getLong();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f seconds to read %.1f MB%n", time / 1e9, fc.size() / 1e6);
fc.close();
((DirectBuffer) bb).cleaner().clean();

打印

Took 0.016 seconds to read 56.6 MB

【讨论】:

DirectBuffer 符号未找到。所以我删除了最后一行,但运行它抛出了 java.nio.BufferUnderflowException。 (53.4 MB 文件) 我一次读取 8 个字节以加快速度,如果长度不是 8 的倍数,这是不好的。您可以改用bb.get()。 DirectBuffer 位于 sun.nio.ch 中,这使其成为可以删除的内部使用 API。【参考方案3】:

使用BufferedInputStream

InputStream buffy = new BufferedInputStream(inputStream);

【讨论】:

我也使用了 BufferedInputStream。 InputStream fh = new BufferedInputStream(new FileInputStream("file"));【参考方案4】:

如上所述,使用 BufferedInputStream。你也可以使用 NIO 包。请注意,对于大多数文件,BufferedInputStream 的读取速度与 NIO 一样快。但是,对于非常大的文件,NIO 可能会做得更好,因为您可以进行内存映射文件操作。此外,NIO 包执行可中断 IO,而 java.io 包则没有。这意味着如果你想从另一个线程取消操作,你必须使用 NIO 来使其可靠。

ByteBuffer buf = ByteBuffer.allocate(BUF_SIZE);
FileChannel fileChannel = fileInputStream.getChannel();
int readCount = 0;
while ( (readCount = fileChannel.read(buf)) > 0) 
  buf.flip();
  while (buf.hasRemaining()) 
    byte b = buf.get();
  
  buf.clear();

【讨论】:

我认为内存映射文件对顺序读取没有任何好处。 @MarkoTopolnik 他们对任何事情的时间收益都不过 20%,但我不知道您为什么认为顺序阅读是一种特殊情况。它不是。磁盘仍会进行预读,就像使用流或读取器时一样。 @EJP 是的,但是预读无论如何都是在较低级别完成的(即使在磁盘电子设备中,也在磁盘缓存实现中)。 @MarkoTopolnik 为什么 MM 文件对顺序读取没有好处? @EJP MM 文件主要是为了方便随机访问读取,因为它们提供了一个简单的 API 来访问文件,就好像它是 RAM 中的一个数组一样。如果您所做的只是从上到下运行 MM 文件,那么您只会给内存管理器施加压力,而不会从范例中获得任何好处。

以上是关于Java:InputStream 读取大文件太慢的主要内容,如果未能解决你的问题,请参考以下文章

字节流

在Java中读取3GB的非常大的csv文件的内存有效方法是什么?

java InputStream读取数据问题

Java I/O(输入输出流)

Java如何从InputStream中读取收入大小? [复制]

java 中inputstream 和outputstream 怎么进行文件的读取写入的??