【问题标题】:Faster way to read file更快的文件读取方式
【发布时间】:2011-05-02 08:11:13
【问题描述】:

我正在开发一个包含大约 400 个输入文件和大约 40 个输出文件的程序。 这很简单:它读取每个输入文件并生成一个新文件,但文件要大得多(基于算法)。

我正在使用 BufferedReader 的 read() 方法:

String encoding ="ISO-8859-1";
FileInputStream fis = new FileInputStream(nextFile);
BufferedReader reader = new BufferedReader(new InputStreamReader(fis, encoding));
char[] buffer = new char[8192] ;

要读取我正在使用的输入文件:

private String getNextBlock() throws IOException{
    boolean isNewFile = false;

    int n = reader.read(buffer, 0, buffer.length);
    if(n == -1) {
        return null;
    } else {
        return new String(buffer,0,n);
    }
}

对于每个块,我都在做一些检查(比如查看块内的一些字符串),然后将其写入文件:

BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream("fileName"), encoding));

writer.write(textToWrite);

问题是大约需要 12 分钟。 我正在尝试更快地找到其他东西。 有人对更好的东西有什么想法吗?

谢谢。

【问题讨论】:

  • 您是否尝试过对不同的缓冲区大小进行基准测试?
  • 瓶颈是文件 IO 还是您用来组合数据的算法?
  • @CC 如果我的回答没有给您带来任何速度提升,您可以随时尝试线程池读取操作。进行同时读取可以提高性能(但也可能降低性能)
  • 文件大小是多少?硬盘的速度是多少?
  • 如果您正在读取/写入本地驱动器(网络驱动器会慢得多)需要 12 分钟(例如 6 正在读取),则文件的读取大小必须约为 10 GB对于写入或平均大约 25 MB 读取和 250 MB 写入。这听起来对吗?如果是你的磁盘就是你的极限。如果不是,那么 I/O 就不是您的瓶颈。

标签: java performance file-io


【解决方案1】:

你应该可以在这里找到答案:

http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly

为了获得最佳的 Java 读取性能,需要记住以下四点:

  • 通过一次读取一个数组而不是一次读取一个字节来最小化 I/O 操作。一个 8Kbyte 的数组是一个不错的大小。

  • 通过一次获取一个数组而不是一次获取一个字节来最小化方法调用。使用数组索引来获取数组中的字节。

  • 如果您不需要线程安全,请尽量减少线程同步锁。要么减少对线程安全类的方法调用,要么使用非线程安全类,如 FileChannel 和 MappedByteBuffer。

  • 尽量减少 JVM/OS、内部缓冲区和应用程序阵列之间的数据复制。将 FileChannel 与内存映射或直接或包装数组 ByteBuffer 一起使用。

【讨论】:

  • 仅链接的答案并不理想。你能至少总结一下这篇文章的发现吗? (谢谢!)
【解决方案2】:

由于您没有提供太多细节,我建议您尝试使用内存映射文件:

FileInputStream f = new FileInputStream(fileName);
FileChannel ch = f.getChannel( );
MappedByteBuffer mbb = ch.map( ch.MapMode.READ_ONLY, 0L, ch.size( ) );
while ( mbb.hasRemaining( ) )  {
      // Access the data using the mbb
}

如果您想更详细地说明您的文件具有哪种数据,则可以对其进行优化。

编辑

哪里是 // 使用 mbb 访问日期,你冷解码你的文本:

String charsetName = "UTF-16"; // choose the apropriate charset.
CharBuffer cb =  Charsert.forName(charsetName).decode(mbb);
String text = cb.toString();

【讨论】:

  • OP 想要将文件作为文本读取。您可能想包括如何使用默认编码(或特定编码,如 UTF-8)读取 MappedByteBuffer
  • 当他像字节一样读取映射文件时,不管结束。他需要在构建字符串时指定编码:String s = new String(mbb.array() , Charset.UTF-8),注意是否加载了数组,如果没有,则需要使用读取asCharBuffer() 并且还必须知道数组的大小和内容。
  • 啊,但魔鬼在细节中。 ;) 例如,您不能解码一个字符串,其中一个字符的一个字节已被读取,但另一个字符没有。 ;) 我不相信你可以在 MappedByteBuffer 上调用 mbb.array()
  • 关于 mbb.array 的想法,我错过了这个重要的细节。他将需要使用 Charset.decode 方法,我将使用它更新我的答案。
  • +1:做对并不简单,所以加个例子很有用。
【解决方案3】:

映射字节缓冲区是最快的方法:

 FileInputStream f = new FileInputStream( name );
FileChannel ch = f.getChannel( );
MappedByteBuffer mb = ch.map( ch.MapMode.READ_ONLY,
    0L, ch.size( ) );
byte[] barray = new byte[SIZE];
long checkSum = 0L;
int nGet;
while( mb.hasRemaining( ) )
{
    nGet = Math.min( mb.remaining( ), SIZE );
    mb.get( barray, 0, nGet );
    for ( int i=0; i<nGet; i++ )
    checkSum += barray[i];
}

【讨论】:

  • 不适用于比 Integer.MAX_VALUE 更大的大文件
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-13
  • 1970-01-01
  • 2023-03-16
  • 2012-12-04
  • 1970-01-01
  • 2014-09-03
相关资源
最近更新 更多