代码之家  ›  专栏  ›  技术社区  ›  ARKBAN

在使用fileinputstream时,如何确定理想的缓冲区大小?

  •  132
  • ARKBAN  · 技术社区  · 17 年前

    我有一个从文件创建messagedigest(散列)的方法,我需要对许多文件(>=100000)执行此操作。为了最大限度地提高性能,我应该使用于读取文件的缓冲区有多大?

    大多数人都熟悉基本代码(我将在这里重复,以防万一):

    MessageDigest md = MessageDigest.getInstance( "SHA" );
    FileInputStream ios = new FileInputStream( "myfile.bmp" );
    byte[] buffer = new byte[4 * 1024]; // what should this value be?
    int read = 0;
    while( ( read = ios.read( buffer ) ) > 0 )
        md.update( buffer, 0, read );
    ios.close();
    md.digest();
    

    最大化吞吐量的缓冲区的理想大小是多少?我知道这依赖于系统,我很确定它的操作系统、文件系统, 和 取决于硬盘,可能还有其他硬件/软件。

    (我应该指出,我对Java有点陌生,所以这可能只是一些我不知道的Java API调用。)

    编辑: 我不知道这种系统会在什么时候使用,所以我不能假设很多。(我为此使用Java。)

    编辑: 上面的代码缺少Try..Catch之类的内容,以便缩小日志

    9 回复  |  直到 9 年前
        1
  •  187
  •   Kevin Day    17 年前

    最佳缓冲区大小与许多因素有关:文件系统块大小、CPU缓存大小和缓存延迟。

    大多数文件系统配置为使用4096或8192的块大小。理论上,如果配置缓冲区大小,使读取的字节数超过磁盘块,则使用文件系统的操作可能非常低效(即,如果将缓冲区配置为一次读取4100字节,则每次读取都需要文件系统读取2个块)。如果这些块已经在缓存中,那么您最终将支付RAM的价格->L3/L2缓存延迟。如果您不走运,而且块还没有在缓存中,那么您也要付出磁盘的代价--gt;RAM延迟。

    这就是为什么大多数缓冲区的大小是2的幂,通常大于(或等于)磁盘块大小。这意味着您的流读取之一可能导致多个磁盘块读取-但这些读取将始终使用一个完整的块-没有浪费的读取。

    现在,在典型的流场景中,这会有很大的偏移,因为当您点击下一次读取时,从磁盘读取的块仍将在内存中(毕竟,我们在这里进行顺序读取),所以您最终会在下一次读取时支付RAM->L3/L2缓存延迟价格,但不会支付磁盘->RAM延迟。从数量级来看,磁盘->RAM的延迟非常慢,几乎超过了您可能要处理的任何其他延迟。

    因此,我怀疑如果您使用不同的缓存大小运行测试(我自己还没有这样做),您可能会发现缓存大小对文件系统块大小的影响很大。除此之外,我怀疑事情会很快平息下来。

    有一个 吨 这里的条件和例外情况-系统的复杂性实际上相当惊人(仅获取L3->L2缓存传输的句柄就非常复杂,而且它会随着每种CPU类型的变化而变化)。

    这就引出了“现实世界”的答案:如果你的应用程序有99%的可用性,将缓存大小设置为8192,然后继续(更好的是,选择封装而不是性能,并使用BufferedInputstream隐藏细节)。如果你在高度依赖磁盘吞吐量的1%的应用程序中,那么你可以精心设计你的实现,这样你就可以交换不同的磁盘交互策略,并提供旋钮和拨号盘,让你的用户测试和优化(或想出一些自优化系统)。

        2
  •  14
  •   Jon Skeet    17 年前

    是的,它可能依赖于各种各样的东西——但我怀疑它会有很大的不同。我倾向于选择16K或32K作为内存使用和性能之间的良好平衡。

    请注意,代码中应该有一个try/finally块,以确保流已关闭,即使引发异常也是如此。

        3
  •  7
  •   Adam Rosenfield    17 年前

    在大多数情况下,这真的没那么重要。只需选择一个好的尺寸,如4K或16K,并坚持它。如果你是 积极的 这是应用程序中的瓶颈,然后应该开始分析以找到最佳缓冲区大小。如果选择的大小太小,将浪费时间执行额外的I/O操作和额外的函数调用。如果您选择的大小太大,您将开始看到大量缓存未命中,这将真正减慢您的速度。不要使用大于二级缓存大小的缓冲区。

        4
  •  4
  •   Ovidiu Pacurar    17 年前

    在理想情况下,我们应该有足够的内存在一次读取操作中读取文件。 这将是最好的性能,因为我们让系统随意管理文件系统、分配单元和HDD。 实际上,您很幸运提前知道文件大小,只需使用四舍五入为4K的平均文件大小(NTFS上的默认分配单元)。 最重要的是:创建一个测试多个选项的基准。

        5
  •  4
  •   John Gardner    17 年前

    您可以使用缓冲流/读卡器,然后使用它们的缓冲区大小。

    我相信BufferedXstream使用8192作为缓冲区大小,但正如Ovidiu所说,您可能应该对所有选项运行测试。它真正将取决于文件系统和磁盘配置,以确定最佳大小。

        6
  •  4
  •   Alexander    17 年前

    使用Java NIO的文件通道和MappedByteBuffer读取文件将最有可能产生比任何涉及FieldPixSt流的解决方案快得多的解决方案。基本上,内存映射大文件,对小文件使用直接缓冲区。

        7
  •  1
  •   Maglob    17 年前

    如其他答案中所述,使用BufferedInputStreams。

    之后,我想缓冲区的大小并不重要。程序要么是I/O绑定的,缓冲区大小比bis默认值大,不会对性能产生任何大的影响。

    或者该程序是在messagedigest.update()中由CPU绑定的,并且大部分时间不花在应用程序代码中,因此调整它不会有帮助。

    (Hmm.)对于多核,线程可能会有所帮助。)

        8
  •  1
  •   GoForce5500    9 年前

    在bufferedInputstream_s源中,您将找到:private static int default_buffer_size=8192;
    所以你可以使用这个默认值。
    但如果你能找出更多的信息,你会得到更有价值的答案。
    例如,您的ADSL可能会预先传递1454字节的缓冲区,这是因为TCP/IP的有效负载。对于磁盘,可以使用与磁盘块大小匹配的值。

        9
  •  0
  •   Adrian Krebs    9 年前

    1024适用于各种情况,尽管在实践中,您可能会看到缓冲区大小越大或越小,性能越好。

    这取决于许多因素,包括文件系统块 大小和CPU硬件。

    对于缓冲区大小,通常选择2的幂,因为大多数底层 硬件是由fle块和缓存大小构成的,它们是2的幂次方。缓冲的 类允许您在构造函数中指定缓冲区大小。如果没有提供,则 使用默认值,在大多数JVM中是2的幂。

    无论您选择哪种缓冲区大小,最大的性能提高将是 请参阅正在从非缓冲文件访问移动到缓冲文件访问。调整缓冲区大小可以 稍微提高性能,但除非使用非常小或非常大的 缓冲区大,不太可能有明显的影响。