当前位置: 首页 > 知识库问答 >
问题:

是什么使得读取没有缓冲区的文件如此昂贵?[副本]

花品
2023-03-14

最近,我创建了一个界面,强制用户实现单个fromStream(OutputStream),其默认方法如下所示:

public default T fromFile(File file) throws IOException {
    try (InputStream stream = new FileInputStream(file)) {
        return fromStream(stream);
    }
}

不久之后,由于直接从FileInputStream读取单个字节,这非常昂贵(每MB数秒)。

将其包装在BufferedInputStream中解决了我的问题,但它给我留下了一个问题:为什么FileInputStream的价格如此昂贵。

文件通道在读取字节时不会关闭或打开,那么为什么首先需要缓冲区呢?

共有2个答案

许俊晤
2023-03-14

当您从文件中读取时,您必须一次读取一个块,因为这是硬件支持的唯一数量。如果您一次读取一个字符而不缓冲,那么假设512B块,您将读取同一个块512次以读取整个块。如果您读取并缓冲,您将访问磁盘一次,然后从内存中读取。

访问磁盘比访问内存慢几个数量级,这不是一个好主意。

施俊远
2023-03-14

如果您使用read()方法从无缓冲流中读取字节,JVM将最终对操作系统进行重复读取系统调用以从文件中读取单个字节。(在引擎盖下,JVM可能正在调用read(addr,偏移量,计数),计数为1。)

进行系统调用的成本很高。至少比常规方法调用多几个数量级。这是因为在以下方面存在大量的管理费用:

  • 在应用程序(非特权)安全域和系统(特权)安全域之间切换上下文。寄存器集需要保存,虚拟内存映射需要更改,TLB条目需要刷新等。
  • 操作系统必须做各种额外的事情来确保syscall请求的内容是合法的。在这种情况下,操作系统必须确定给定当前文件位置和大小,请求的偏移量和计数是否确定,地址是否在应用程序的地址空间内,并映射为可写的。等等。

相反,如果您使用缓冲流,则该流将尝试从操作系统中读取大块文件。这通常会导致系统调用数量减少数千倍。

事实上,这与文件在磁盘上的存储方式无关。的确,数据最终必须一次读取一个块,等等。然而,操作系统足够聪明,可以自己进行缓冲。它甚至可以提前读取文件的某些部分,以便在(内核)内存中为应用程序进行系统调用以读取它们做好准备。

多个单字节read()调用极不可能导致额外的磁盘流量。唯一可行的情况是,如果您在每次读取()之间等待很长时间,。。。操作系统会重用缓存磁盘块的空间。

 类似资料:
  • 我正在学习Java I/O。因此,使用缓冲流可以减少读取或写入所需的时间,因为如果使用普通的FileInputStream,每次调用读取时都会获取一个字节,但如果使用缓冲区,则会获取指定大小的数据并将其存储在内存中。所以我试着在实践中看到这一点。 我已将BufferedInputStream的缓冲区大小设置为512,8192,65536。每次需要87秒才能完成执行。所以我尝试使用FileInput

  • 我的目标是用扩展名解析协议缓冲区文件。pb。一串在Mac上使用自制软件下载Protobuff。运行protoc--版本,并具有libprotoc 3.1.0版本。 但当我运行Python时,它会说找不到模块。我改变了主意。pb文件名到\u pb2。py并在Python脚本中导入模块。 我正在使用谷歌文档,但仍然没有任何运气。我在编译Protobuf时也遇到了问题。so文件通过Python。我只是无

  • 问题内容: 我正在尝试从FTP服务器读取文件。该文件是一个文件。我想知道在套接字打开的情况下是否可以对此文件执行操作。我试图按照什么分两个问题,提到的阅读文件,而不写入磁盘和读取从FTP文件,而无需下载,但没有成功。 我知道如何提取下载文件上的数据/工作,但不确定是否可以即时执行。有没有一种方法可以连接到站点,在缓冲区中获取数据,还可以提取某些数据并退出? 尝试StringIO时出现错误: 我只需

  • 两者都将找到答案996没有问题。我们使用modulos来保持合理的输出大小,使用pair来避免指数分支。 对于n=5000,C++代码输出783,但Python会抱怨 如果我们加上几行

  • 我读到FileWriter和BufferedWriter的区别在于FileWriter直接写入文件(逐字符),white BufferedReader使用缓冲区。如果是,为什么FileWriter有缓冲区?例如,如果我创建一个FileWriter对象,如下所示: 而且,如果我在程序结束时不刷新或关闭写入器,它将不会向文件写入任何内容。这意味着它也使用缓冲区。拜托,解释一下?

  • 两者都是序列化库,由谷歌开发人员开发。他们之间有什么大的区别吗?将使用协议缓冲区的代码转换为使用FlatBuffers需要大量工作吗?