V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
lzjamao
V2EX  ›  程序员

基于 mmap 相比于 fwrite 写日志,是否有性能优势?

  •  1
     
  •   lzjamao · Jul 25, 2021 · 5143 views
    This topic created in 1748 days ago, the information mentioned may be changed or developed.

    看到一些日志库和文章说,声称基于 mmap 可以增强性能,如腾讯 xloglog4a

    微信 mars 的高性能日志模块 xlog

    log4a readme

    《高性能日志记录方式 - mmap 》

    不过在 stackoverflow 上看到了相反说法(这里),有人认为 mmap 和 fwrite 在顺序写性能上差别不太( 微信 mars 的高性能日志模块 xlog文章里的测试结果也是说相差无几),有人认为不恰当的使用 mmap 会使性能差别。

    27 replies    2021-07-26 21:06:54 +08:00
    ipwx
        1
    ipwx  
       Jul 25, 2021
    mmap 比 fwrite 快的主要原因,不是因为不需要一次内存拷贝么 2333 。fwrite 需要把内容拷贝到内核去,但是 mmap 直接就是内核的。

    然后 mmap 写回磁盘的最小数据单位是 page size,一般 linux 上面是 4096 。好处是 flush / cache 都是 linux 内核管的,和虚拟内存走相同的方式。而且就算进程崩溃了,因为 mmap 是虚拟内存块,所以该写回的还是会写回。那么坏处就很显然了,如果每次都只改某些 4096 块的某些小地方,要写回的东西还是蛮多了(因为写回一次是按 4096 分块的)。读也亦然。
    ipwx
        2
    ipwx  
       Jul 25, 2021
    然后楼主你举的那个日志的例子很特殊,是永远顺序往下写的。写满一个 4096 块就不会再动了,系统完全有机会每次写满 4096 再刷新。而且因为是 mmap,每次写一条日志就不需要 flush (因为你不怕内存崩溃),相当于避免了频繁 << 4096 大小的文件写入,自然变快了。

    缺点也很明显了,日志不是完全实时的。
    ipwx
        3
    ipwx  
       Jul 25, 2021
    不怕内存崩溃 => 不怕进程崩溃

    话说回来依赖 mmap 如果系统宕机或者断电了,那也就没机会写回磁盘了。这点和 flush 就不一样了
    sagaxu
        4
    sagaxu  
       Jul 25, 2021 via Android
    顺序写入时,mmap 不一定比 write 快,内存复制比磁盘 IO 快一个数量级,省掉这一步收效甚微,内核还要维护 mmap 的映射关系,这也是有成本的。我觉得顺序写入日志完全没必要用 mmap,性能差不多,代码更复杂,跨平台性还更差。
    ipwx
        5
    ipwx  
       Jul 25, 2021
    @sagaxu 主要是省了 flush 啊。你普通写入为了避免程序崩溃看不到最后的日志,很多时候要频繁 flush 的。
    billlee
        6
    billlee  
       Jul 25, 2021
    谁会像微信 xlog 那样写个几个 G 的日志啊
    BiteTheDust
        7
    BiteTheDust  
       Jul 25, 2021
    按照自己的经历 在顺序读写几十个 g 的情况下 在外面自己管理了一层缓存 把 fwrite 改成 mmap 似乎没有什么性能提升(甚至有下降)
    ipwx
        8
    ipwx  
       Jul 25, 2021
    @BiteTheDust 毕竟 fwrite 到 mmap 还得复制一份。mmap 的正确操作不是直接写入么 2333
    ryd994
        9
    ryd994  
       Jul 25, 2021 via Android
    @ipwx 问题是你内存里那一份又是哪来的?
    要记录的数据那肯定是软件运行时本身就用的数据。那为什么不 fprintf 直接格式化进文件呢?
    如果你是怕储存速度跟不上的话,那比起写 mmap 然后指望不知道什么时候会 flush,还不如加大文件 write buffer 和使用 nonblocking 然后加逻辑处理满了的情况。
    mmap 更多的时候是用于直接映射到 struct 指针这种邪教玩法。

    对于顺序写来说,mmap 只不过是隐藏了储存性能不足和 file write buffer 设置不当的问题。副作用是你彻底失去了对上述两者的控制。

    而且以一般的持久储存媒介的性能,少一两次拷贝不会有实质性的影响。
    nuk
        10
    nuk  
       Jul 25, 2021
    fwrite 内置 buffer,所以肯定是要加锁的,单线程写和 mmap 比性能可能差别不大,但是换到多线程场景,可能情况就不一样了,反正我认同内存 copy 肯定不是瓶颈,当然我就是猜猜而已。
    BBCCBB
        11
    BBCCBB  
       Jul 25, 2021
    不一定吧.. 你看 java 实现的 mq, 写入有用 mmap 的, 也有用 FileChannel write 的. fileChannel 这种批量 write 性能不一定比 mmap 差.. 看场景.

    可以参考 https://www.jianshu.com/p/d0b4ac90dbcb
    sagaxu
        12
    sagaxu  
       Jul 25, 2021 via Android
    @ipwx fwrite 才要 fflush,write 返回已经提交到内核了,就算进程奔溃,内核不知道该往哪里写了吗?

    @nuk posix 要求 write 写入不超过 PIPE_BUF 字节数时原子性,如果被写入的是 regular file,每次 write 都是原子性的,哪怕一次写 1GB 。所以只要 append 方式打开,尽管写入就是了,不用考虑并发。
    nuk
        13
    nuk  
       Jul 25, 2021
    @sagaxu 你说的是 write,不是 fwrite
    nuk
        14
    nuk  
       Jul 25, 2021
    ryd994
        15
    ryd994  
       Jul 25, 2021 via Android
    @nuk 那你多线程写 mmap 就不用锁了?
    ipwx
        16
    ipwx  
       Jul 25, 2021
    @ryd994 因为 mmap 内核管啊,内存哪怕崩溃了 mmap 刚放进去的东西还在,系统会记得把东西放进磁盘的。

    https://stackoverflow.com/questions/5902629/mmap-msync-and-linux-process-termination

    顺便 fprintf 要是没缓存不就更频繁写入文件了么。要是有缓存不就又二次缓冲了么(蛋疼)
    ipwx
        17
    ipwx  
       Jul 25, 2021
    @ryd994 另外什么少一两次拷贝影响的问题。flush 真正影响的是,机械磁盘的寻道很慢好不好。能缓冲写入的,就得缓冲写入,不然频繁小数据写入,拖累整个系统其他进程的速度。
    nuk
        18
    nuk  
       Jul 25, 2021
    @ryd994 mmap 可以做 lockless 的,如果你硬要说做日志队列的话,当我没说
    ryd994
        19
    ryd994  
       Jul 25, 2021 via Android
    @ipwx 你是不是忘了文件缓存也是由操作系统管理的?你 write 的时候是写入 page cache,flush 才是物理写入。如果你说的是进程挂了,那 write 进去的会在文件被关闭的时候自动 flush 。挂了也会由系统关闭所有文件。

    如果你说的是系统挂了,那不管 mmap 还是 write 都得挂。
    fwrite 是另一回事,因为 libc 可能另外有缓冲,但是你大可以不用 fwrite 。

    fprintf 也是这样。只是写入 fd,并不会物理写入。建议你再看看 write 的文档。

    @nuk 1.这里讨论的就是日志。2. 请解释 mmap 比 write 性能更好的理由。write 和 mmap 实际上都是操作 page cache 。除了 write 多一次拷贝之外,最终都是由系统管理何时写入物理媒介。
    其实多线程还要高性能写入,最好的一个线程一个
    ryd994
        20
    ryd994  
       Jul 25, 2021 via Android
    其实多线程还要高性能写入日志,最好的一个线程一个文件。事后再归并。大部分情况下系统时钟或者 monotonic 时钟就足够精度了。
    如果你要求绝对的时间顺序,那就最好用无锁队列或其他方式,然后把日志写入交给专门的线程。
    也可以开独立的日志进程,比如 syslog 。
    ryd994
        21
    ryd994  
       Jul 25, 2021 via Android
    @ipwx write 只保证写入 page cache 。vfs 还有 IO scheduler 都可以对操作进行重排或者合并。所以你就算随机写,只要范围不大而且最终结果是连续的,那操作系统就能够合并操作,结果还是连续的
    平时 write 会 block 是因为有缓存限制。但是这是可以调整的。
    而且还可以用 non blocking write 。缓存满了就失败,你自己再想办法处理。
    again,这些都是 page cache,由操作系统管理。

    如果你认真了解过 mmap 的实现机制,就会知道,mmap 实际上就是把 page cache map 给你了。既然 write 也是写入 page cache,那写入之后的事情就没有区别了。
    nuk
        22
    nuk  
       Jul 25, 2021
    @ryd994 老哥别揪着我不放了,我承认你说的都对
    swulling
        23
    swulling  
       Jul 25, 2021
    日志顺序写场景的话,mmap 的优势只不过是比内核少了一次拷贝,所以只有在极大数据量的情况下才有优势。
    Brentwans
        24
    Brentwans  
       Jul 25, 2021
    其它场景不太清楚,但是在数据处理的领域。如果单论写入性能,结论是 mmap 不会直接明显提升写入性能。因为不管什么写法瓶颈在 IO,只要写入速度到了磁盘 IO 上限,就再也无法提升。看了好多上面测试结论都有不准的可能,IO 性能的 benchmark 是不太容易的,需要用一定的技巧才行,因为系统数据缓存的原因,很容易最终测出的不是磁盘 IO 速度而是内存的速度。
    ryd994
        25
    ryd994  
       Jul 25, 2021 via Android
    其实你仔细看文一文二,说的都是安卓系统或者移动平台,也就是说,没有 root 权限,不一定能修改 page cache 回写的时机。那这种情况下用 mmap 就是不是办法的办法。因为 mmap 必定不阻塞,所以可以突破系统的限制。
    但是这说白了是没有权限的情况下的投机取巧。而且这种情况下对 mmap 也是有限制的。反复 map unmap 还有 page fault 的开销也不小。所以实际效果如何还有待商榷。
    julyclyde
        26
    julyclyde  
       Jul 26, 2021
    腾讯的问题在于:
    1 热爱 mmap
    2 立场高于实际
    codehz
        27
    codehz  
       Jul 26, 2021 via Android
    @ryd994 谁说 mmap 不阻塞的。只是内存足够给你浪而已。。。
    是可以卡在缺页中断那很久的,然后特定条件下可以影响业务稳定运行(你甚至没有任何办法打断一个处在在缺页状态的线程)。。
    无阻塞的文件读写接口只有 io uring 那里有,但是显然不够安全,进程炸了就没了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   958 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 55ms · UTC 19:55 · PVG 03:55 · LAX 12:55 · JFK 15:55
    ♥ Do have faith in what you're doing.