【问题标题】:Buffered vs Unbuffered. How actually buffer work?缓冲与无缓冲。缓冲区实际上是如何工作的?
【发布时间】:2020-11-22 20:32:26
【问题描述】:

缓冲区实际上是如何优化读/写过程的?

每次我们读取一个字节时,我们都会访问该文件。我读到缓冲区减少了访问文件的次数。问题是如何?在图片的缓冲部分,当我们将字节从文件加载到缓冲区时,我们就像在图片的非缓冲部分一样访问文件,那么优化在哪里?
我的意思是......每次读取一个字节时,缓冲区都必须访问文件,所以 即使缓冲区中的数据被更快地读取,这也不会提高读取过程中的性能。我错过了什么?

【问题讨论】:

    标签: java optimization io buffer


    【解决方案1】:

    基本的误解是假设文件是​​逐字节读取的。大多数存储设备,包括硬盘驱动器和固态磁盘,都将数据组织在blocks 中。同样,网络协议以packets 传输数据,而不是单个字节。

    这会影响控制器硬件和底层软件(驱动程序和操作系统)的工作方式。通常,甚至不可能在此级别上传输单个字节。因此,请求读取单个字节最终会读取一个块并忽略除一个字节之外的所有内容。更糟糕的是,写入单个字节可能意味着读取整个块,改变它的一个字节,然后将块写回设备。对于网络传输,发送一个只有一个字节有效负载的数据包意味着将 99% 的带宽用于元数据,而不是实际的有效负载。

    请注意,有时需要立即响应,或者需要在某个时间点确定完成写入,例如为了安全。这就是为什么存在无缓冲 I/O 的原因。但对于大多数普通用例,无论如何您都希望传输一个字节序列,并且应该以适合底层硬件大小的块的形式传输它。

    请注意,即使底层系统自己注入缓冲,或者当硬件真正传输单个字节时,执行 100 operating system calls 以在每个上传输单个字节仍然比执行单个操作系统调用告诉它慢得多一次传输 100 个字节。


    但是您不应该将缓冲区视为文件和程序之间的东西,正如您图片中所建议的那样。您应该将缓冲区视为程序的一部分。就像您不会将 String 对象视为介于您的程序和字符源之间的东西,而是一种处理此类项目的自然方式。例如。当您使用具有足够大目标数组的the bulk read method of InputStream(例如FileInputStream)时,无需将输入流包装在BufferedInputStream 中;它不会提高性能。你应该尽可能远离single byte read method。

    作为另一个实际示例,当您使用InputStreamReader 时,它已经将字节读入缓冲区(因此不需要额外的BufferedInputStream)并且内部使用的CharsetDecoder 将对该缓冲区进行操作,写入将生成的字符放入目标字符缓冲区。当您使用时,例如Scanner,模式匹配操作将作用于字符集解码操作的目标字符缓冲区(当源是InputStream 或ByteChannel 时)。然后,当以字符串形式传递匹配结果时,它们将由另一个大容量复制操作从 char 缓冲区创建。所以分块处理数据已经是常态,而不是例外。

    这已被纳入 NIO 设计。因此,不像InputStream API 那样支持single byte read method 并通过提供缓冲装饰器来修复它,NIO 的ByteChannel 子类型仅提供使用application managed buffers 的方法。


    所以我们可以说,缓冲并没有提高性能,它是传输和处理数据的自然方式。相反,不缓冲会降低性能,因为需要将自然批量数据操作转换为单项操作。

    【讨论】:

      【解决方案2】:

      如您的图片中所述,缓冲文件内容保存在内存中,未缓冲文件不会直接读取,除非将其流式传输到程序中。

      File 仅表示路径。这里来自File Javadoc:

      文件和目录路径名的抽象表示。

      同时,像ByteBuffer 这样的缓冲流从文件中获取内容(取决于缓冲区类型,直接或间接)并将其作为堆分配到内存中。

      此方法返回的缓冲区通常比非直接缓冲区具有更高的分配和释放成本。 直接缓冲区的内容可能位于正常的垃圾收集堆之外,因此它们对应用程序内存占用的影响可能并不明显。因此建议将直接缓冲区主要分配给受底层系统的本机 I/O 操作影响的大型、长期存在的缓冲区。通常,最好仅在直接缓冲区对程序性能产生可衡量的增益时才分配它们。

      其实要看情况,如果文件被重复访问,那么缓冲比不缓冲是更快的解决方案。但是如果文件比主存大,并且被访问一次,无缓冲似乎是更好的解决方案。

      【讨论】:

        【解决方案3】:

        基本上,如果您请求 1 个字节,缓冲区将读取 1000 个字节并返回第一个字节,对于接下来的 999 次读取 1 个字节,它不会从文件中读取任何内容,而是使用其内部 缓冲区 在RAM。只有在您读取了所有 1000 个字节后,它才会实际从实际文件中读取另外 1000 个字节。

        写作也是如此,但相反。如果您写入 1 个字节,它将被缓冲,并且只有当您写入 1000 个字节时,它们才可能被写入文件。

        请注意,选择缓冲区大小会极大地改变性能,请参见例如https://stackoverflow.com/a/237495/2442804 了解更多详细信息,包括文件系统块大小、可用 RAM 等。

        【讨论】:

        • 哪个缓冲区大小最好?
        • 缓冲区读取1000字节,可以,但是缓冲区读取的成本必须小于无缓冲读取?为什么缓冲的成本更小,不知何故,将数据从文件传输到缓冲区然后再传输到程序更容易。是什么让作为临时内存区域的缓冲区如此特殊的 s.t 数据读取速度更快?
        • @IcaSandu 是的,RAM 访问比 IO / 磁盘访问更快。您可以搜索谷歌进行比较。
        • @IcaSandu 如果没有缓冲,您每次读取或写入字节时都必须调用存储设备(或其他 I/O 设备)。这是一种过度简化,因为在 I/O 设备和操作系统级别也可能存在缓冲区。关键是批量读写效率更高,读取/写入RAM更快。
        猜你喜欢
        • 1970-01-01
        • 2013-03-04
        • 1970-01-01
        • 1970-01-01
        • 2012-04-04
        • 2010-11-29
        • 2015-03-29
        • 2014-09-12
        相关资源
        最近更新 更多