IO
作者:革命觉悟 链接:https://www.zhihu.com/question/265246208/answer/2391613327 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
mmap 和 direct io,两者的使用方式完全不同,没多少可比性。使用方式page cachemmapmemcpy将 page cache 映射到用户空间的虚存地址,用户代码直接以内存的方式访问,通过 page fault 与操作系统交互standard ioread/write读写文件时,操作系统要在 page cache 和用户内存之间 memcpy,read page miss 时会发生 io,write 几乎总是不会发生 io,实际的 write io 由操作系统异步完成direct ioread/write不通过 page cache,用户提供的 buf 必须对齐到 pagedirect io 一般很少使用,甚至有些文件系统干脆就不支持 direct io。mmap 可以直接访问 page cache 内存,相比 read/write,免去了 memcpy 的开销,似乎要更高效,但这只是表面现象!实际上,因为使用 mmap 会引发 page fault,而 page fault 的开销可比 memcpy 大多了,举个简单的例子:假定我们为 mmap 的内存设置了 madv_random,现在我们通过 mmap 读取一段连续的 10 个 page,并且这 10 个 page 都还未加载到 page cache,那么这期间会发生 10 个 major page fault(相应地引发 10 个 IO)!再要继续深入一点,如果没有设置 madv_random,操作系统会进行预读,例如一次性读 32 个 page 进来(包含我们要的 10 个 page),但是,仍然可能发生 10 次 page fault,只是,第一个 page fault 是 major page fault,后续的 9 个 page fault 是 minor page fault(不引发 io 操作的 page fault),这仍然比 memcpy 10 个 page 昂贵得多。现在换一种方式,我们为文件 fd 设置了 fadv_random,使用 read 来读取这 10 个 page,那么操作系统明确地知道你这一点,它就可以通过一次 io 请求,把这 10 个 page 都读进来!如果没有为 fd 设置 fadv_random,那么 read 时除了会引发预读,没有其它区别。所以,在发生 page fault 时,如果我们访问的内存不会跨越 page 边界,mmap 也许会比 read 高效,说“也许”,是因为 mmap 时操作系统需要建立虚拟地址映射,并且 page fault 是中断处理,调用链开销比 syscall 要更高。只有不发生 page fault,mmap 才一定更高效,而要确定不让它发生 page fault,就要使用 mlock(请谨慎使用,后果自负)。当然,还有一种情况,不需要 mlock,也一定不会 page fault,那就是 DAX 文件系统!然而,即便是在 DAX 文件系统中,mmap 的系统开销仍然非常巨大,其中主要是 PTE(Page Table Entry)构建的开销:
https://www.usenix.org/system/files/conference/hotstorage17/hotstorage17-paper-choi.pdf
随着新型硬件,RDMA技术,闪存存储技术的不断发展和普及,访问网络的延迟和磁盘IO延迟相比之前大大降低了。这时,设计系统中高效的IO软件栈变得越来越重要,因为软件栈CPU开销导致的latency占整个IO延迟的比例越来越不可忽视。
在设计基础软件时,比如全闪存储系统,HPC系统,数据库软件等对性能要求高的基础软件时,如何实现整个IO软件栈高性能,有哪些设计思想可以考虑呢,目前想到有以下几点设计可以参考。
RTC(Run to Completion)运行模式 设计系统线程模型时,可以设计线程独占CPU核运行,减少CPU cache missing,减少CPU之间的缓存同步,保证IO在线程上RTC,同时,设计上需要考虑避免线程跨numa访问系统资源,内存、磁盘、网卡等。
IO路径无锁化 保证IO路径无锁,做好资源规划,把系统资源做到线程级别专用可以避免IO处理时资源加锁,无锁队列,双buffer设计都是可以避免加锁的设计。
IO路径zero-copy/bypass kernal 通过RDMA,nvme等新型硬件访问协议实现软件栈zero-copy,同时,减少用户态和内核态切换,减少数据拷贝,在高性能的场景下,上下文切换的开销(~100ns)变得不可忽视。
抽象资源池 系统初始化将系统IO资源池化,抽象内存池,对象池等进行内存和对象预分配,避免malloc/alloc/new等动态内存申请,动态对象申请操作。
大页内存 大页内存是优化程序性能的一种方式,通过进程独占大页内存,PIN固定VA-PA映射关系,避免页面淘汰换入换出带来的性能损耗。
软件层batch 通过batch减少磁盘IO次数,比如做顺序IO合并等,batch RPC减少网络IO次数可以显著提高系统的吞吐量带来性能提升。
暂无评论,欢迎留下第一条评论。