看到一些日志库和文章说,声称基于 mmap 可以增强性能,如腾讯 xlog和log4a。
不过在 stackoverflow 上看到了相反说法(这里),有人认为 mmap 和 fwrite 在顺序写性能上差别不太( 微信 mars 的高性能日志模块 xlog文章里的测试结果也是说相差无几),有人认为不恰当的使用 mmap 会使性能差别。
1
ipwx 2021-07-25 16:43:45 +08:00
mmap 比 fwrite 快的主要原因,不是因为不需要一次内存拷贝么 2333 。fwrite 需要把内容拷贝到内核去,但是 mmap 直接就是内核的。
然后 mmap 写回磁盘的最小数据单位是 page size,一般 linux 上面是 4096 。好处是 flush / cache 都是 linux 内核管的,和虚拟内存走相同的方式。而且就算进程崩溃了,因为 mmap 是虚拟内存块,所以该写回的还是会写回。那么坏处就很显然了,如果每次都只改某些 4096 块的某些小地方,要写回的东西还是蛮多了(因为写回一次是按 4096 分块的)。读也亦然。 |
2
ipwx 2021-07-25 16:46:46 +08:00
然后楼主你举的那个日志的例子很特殊,是永远顺序往下写的。写满一个 4096 块就不会再动了,系统完全有机会每次写满 4096 再刷新。而且因为是 mmap,每次写一条日志就不需要 flush (因为你不怕内存崩溃),相当于避免了频繁 << 4096 大小的文件写入,自然变快了。
缺点也很明显了,日志不是完全实时的。 |
3
ipwx 2021-07-25 16:47:29 +08:00
不怕内存崩溃 => 不怕进程崩溃
话说回来依赖 mmap 如果系统宕机或者断电了,那也就没机会写回磁盘了。这点和 flush 就不一样了 |
4
sagaxu 2021-07-25 16:54:32 +08:00 via Android
顺序写入时,mmap 不一定比 write 快,内存复制比磁盘 IO 快一个数量级,省掉这一步收效甚微,内核还要维护 mmap 的映射关系,这也是有成本的。我觉得顺序写入日志完全没必要用 mmap,性能差不多,代码更复杂,跨平台性还更差。
|
6
billlee 2021-07-25 17:14:06 +08:00
谁会像微信 xlog 那样写个几个 G 的日志啊
|
7
BiteTheDust 2021-07-25 17:44:07 +08:00
按照自己的经历 在顺序读写几十个 g 的情况下 在外面自己管理了一层缓存 把 fwrite 改成 mmap 似乎没有什么性能提升(甚至有下降)
|
8
ipwx 2021-07-25 18:08:55 +08:00
@BiteTheDust 毕竟 fwrite 到 mmap 还得复制一份。mmap 的正确操作不是直接写入么 2333
|
9
ryd994 2021-07-25 19:20:47 +08:00 via Android
@ipwx 问题是你内存里那一份又是哪来的?
要记录的数据那肯定是软件运行时本身就用的数据。那为什么不 fprintf 直接格式化进文件呢? 如果你是怕储存速度跟不上的话,那比起写 mmap 然后指望不知道什么时候会 flush,还不如加大文件 write buffer 和使用 nonblocking 然后加逻辑处理满了的情况。 mmap 更多的时候是用于直接映射到 struct 指针这种邪教玩法。 对于顺序写来说,mmap 只不过是隐藏了储存性能不足和 file write buffer 设置不当的问题。副作用是你彻底失去了对上述两者的控制。 而且以一般的持久储存媒介的性能,少一两次拷贝不会有实质性的影响。 |
10
nuk 2021-07-25 19:35:29 +08:00
fwrite 内置 buffer,所以肯定是要加锁的,单线程写和 mmap 比性能可能差别不大,但是换到多线程场景,可能情况就不一样了,反正我认同内存 copy 肯定不是瓶颈,当然我就是猜猜而已。
|
11
BBCCBB 2021-07-25 19:58:58 +08:00
不一定吧.. 你看 java 实现的 mq, 写入有用 mmap 的, 也有用 FileChannel write 的. fileChannel 这种批量 write 性能不一定比 mmap 差.. 看场景.
可以参考 https://www.jianshu.com/p/d0b4ac90dbcb |
12
sagaxu 2021-07-25 20:00:37 +08:00 via Android
|
14
nuk 2021-07-25 20:18:51 +08:00
|
16
ipwx 2021-07-25 20:36:38 +08:00
@ryd994 因为 mmap 内核管啊,内存哪怕崩溃了 mmap 刚放进去的东西还在,系统会记得把东西放进磁盘的。
https://stackoverflow.com/questions/5902629/mmap-msync-and-linux-process-termination 顺便 fprintf 要是没缓存不就更频繁写入文件了么。要是有缓存不就又二次缓冲了么(蛋疼) |
17
ipwx 2021-07-25 20:37:31 +08:00
@ryd994 另外什么少一两次拷贝影响的问题。flush 真正影响的是,机械磁盘的寻道很慢好不好。能缓冲写入的,就得缓冲写入,不然频繁小数据写入,拖累整个系统其他进程的速度。
|
19
ryd994 2021-07-25 21:40:55 +08:00 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 多一次拷贝之外,最终都是由系统管理何时写入物理媒介。 其实多线程还要高性能写入,最好的一个线程一个 |
20
ryd994 2021-07-25 21:44:37 +08:00 via Android
其实多线程还要高性能写入日志,最好的一个线程一个文件。事后再归并。大部分情况下系统时钟或者 monotonic 时钟就足够精度了。
如果你要求绝对的时间顺序,那就最好用无锁队列或其他方式,然后把日志写入交给专门的线程。 也可以开独立的日志进程,比如 syslog 。 |
21
ryd994 2021-07-25 21:55:34 +08:00 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,那写入之后的事情就没有区别了。 |
23
swulling 2021-07-25 22:59:51 +08:00
日志顺序写场景的话,mmap 的优势只不过是比内核少了一次拷贝,所以只有在极大数据量的情况下才有优势。
|
24
Brentwans 2021-07-25 23:23:27 +08:00
其它场景不太清楚,但是在数据处理的领域。如果单论写入性能,结论是 mmap 不会直接明显提升写入性能。因为不管什么写法瓶颈在 IO,只要写入速度到了磁盘 IO 上限,就再也无法提升。看了好多上面测试结论都有不准的可能,IO 性能的 benchmark 是不太容易的,需要用一定的技巧才行,因为系统数据缓存的原因,很容易最终测出的不是磁盘 IO 速度而是内存的速度。
|
25
ryd994 2021-07-25 23:47:06 +08:00 via Android
其实你仔细看文一文二,说的都是安卓系统或者移动平台,也就是说,没有 root 权限,不一定能修改 page cache 回写的时机。那这种情况下用 mmap 就是不是办法的办法。因为 mmap 必定不阻塞,所以可以突破系统的限制。
但是这说白了是没有权限的情况下的投机取巧。而且这种情况下对 mmap 也是有限制的。反复 map unmap 还有 page fault 的开销也不小。所以实际效果如何还有待商榷。 |
26
julyclyde 2021-07-26 11:27:58 +08:00
腾讯的问题在于:
1 热爱 mmap 2 立场高于实际 |