原载于微信公众号:小轻666
前言
本人程序员,会日常分享一些技术笔记。尽量从程序员日常会接触到的东西开始,往深说那么一点。每篇不说多,说多了大家也看不进去。
上一篇聊了,我们的程序里调用 write (fwrite) 写入文件的时候,并没有直接写到磁盘。而是又经历了 2 层缓存,才到磁盘的。如果写程序时不加注意,运行完程序,一拔磁盘,可能会发现磁盘里可能并没有咱写入的内容。(详见上一篇~)
那我们写入文件的时候,有没有可能绕过缓存呢?可以的。今天就来对比下,这两种写入文件的方式。
普通写入方式(Buffered IO)
最常见的写入方式,都是有缓存的,因此也叫 Buffered IO (翻译: “缓冲输入输出”)。
大致如图:
在默认场景下,我们用的都是这种。
它有啥弊端?
- 数据额外经历了缓存,也就意味着多一次数据拷贝,消耗了额外的 CPU。
- 数据在写入磁盘前,都在缓存里,这就占用了额外的内存。
为了避免这些弊端,不用缓存不就行了?可以,那我们来介绍下 direct IO。
Direct IO(直接 IO)
如果没有中间的缓存,一切就很清爽:
这就是所谓的直接 IO(Direct IO)。
Direct IO 因为不再有缓存,所以:
- 不再需要将数据从用户空间拷贝到缓存,节省了 CPU。
- 不需要在内存中维护缓存了,节省了内存。
听起来 direct IO 很美好,而 buffered IO 很愚蠢?那为什么编程时默认都采用 buffered IO 呢?
为什么默认使用 buffered IO?
原因是,和磁盘每交互一次,相比于和内存的交互,都是很慢的。使用 direct IO 的时候,每次写入数据都要直接和磁盘交互,这个交互的频率太高了,这就会导致整体的写入速度很慢。而如果像 buffered IO 这样,把几次要写入的数据缓存起来,再统一和磁盘交互一次,就快得多。
(注:和磁盘的交互是毫秒级别的,和内存的交互是纳秒级别的,差了几个数量级)
请看图:
也就是说,buffered IO 通过引入缓存,降低了和磁盘交互的频率,提高了数据写入的速度。在绝大多数场景下,buffered IO 的性能都是优于使用 direct IO 的。
因此,它也就成为了默认的写入模式。
Direct IO 有什么用?怎么用?
那 Direct IO 既然慢,还有应用场景吗?
有的。
上面说的 direct IO 慢的原因,就在于其 “高频小量” 写入。
只要我们能通过程序逻辑,不 “高频小量” 的写入,而是每次都写入一块大的数据,就没有上面提到的,和磁盘交互次数过多的问题啦。
在一些场景下,我们的程序里已经攒好了大块大块的数据。这种场景下,就可以通过 direct IO 将数据整块整块的写入磁盘。这样,我们既绕过了 buffered IO 的缓存,节约了 CPU 和内存,同时也不会因为和磁盘交互过多而拖累速度。
举个例子:
一个日志系统,通常为了避免阻塞主程序,会进行 “异步写入”,也就是先把日志内容拷贝到一个缓冲区里,后续再写到磁盘。在这种场景下,在我们一次次调用 “写入日志” 接口的时候,一段一段短小的日志,就会积聚在这个大的缓冲区里。而日志系统,就可以把这些积聚好的内容,整块整块的放入磁盘。这时使用 direct IO,因为每次都放入一大块数据,就不会有速度慢的问题,同时可以比 buffered IO 更加节省 CPU 和内存。
后续
先总结一下:在大多数场景下,直接用 buffered IO 会比直接用 direct IO 性能更好,但在一些场景下,尤其是程序里已经维护了用户态缓存的情况下,可以通过 direct IO,绕过 page cache 直接写入文件。这样,可以节省 page cache 带来的 CPU 和内存开销。
本篇主要从概念上讲解了 direct IO,从编程实操的角度,还有一些需要注意的地方,请关注后续更新~
另外,除了 buffered IO 和 direct IO,还有其他写入文件的方式,这个也留在后面再聊~
大家有什么想了解的,欢迎留言~
