C/C++中使用内存映射技术浅谈

前言

前段时间, 研究了内存映射技术在C/C++中的实现. 当时到网上找文章, 发现相关的文章很多, 但是似乎很多都和我的情况不一样, 而且有些讲得很晦涩. 鉴于自己的经历, 想整理一篇关于C/C++中使用内存映射技术的文章, 供大家参考, 如果能帮到一些朋友的话, 那就真的很开心了.
原创文章, 如果觉得对你有帮助的话, 欢迎点赞, 收藏, 留言, 谢谢.

结论

为了直接了当反映本文想表达的观点, 就先说结论.

C/C++中, 内存映射技术与直接使用open, read等系统底层函数的方式相比, 优势越来越小了, 而且随着内存传输速度的提升, 这一优势会进一步缩小. 这与很多文章的观点不是很符合. 最开始的时候, 我也认为内存映射技术应该比直接使用这些函数好很多, 速度的提升应该很明显, 而且上网查资料(可能大部分资料都比较老了, 所以内存的速度还不是那么快), 都是这么认为的. 可是我自己实际在做的时候, 却发现优势并没有那么明显, 于是我就仔细分析了原因, 有了一些结论, 供大家参考.

内存映射原理

首先, 我们应该做的是分析内存映射的原理. 只有懂得原理, 才能去运用以及分析它. 当然, 由于本人水平有限, 并不能把这个原理讲得很透彻, 但我争取在自己能力范围内把它说得通俗易懂.

我们在读取文件的时候, 必须要做的事情就是把文件从磁盘拷贝到内存中. 这一点无论是内存映射技术也好, 或者使用open, read系统函数读取也好, 都不可避免. 因为CPU只能够处理内存中的数据. 实际上, 关于CPU工作原理, 也有很多的知识, 但是那是另外一个话题了, 我们暂且放下. 目前, 我们要有的一个认识是, 数据从磁盘到内存的这一过程是不可避免的, 而且实际上, 这也是最耗时的传输过程.

对于一个进程来说, 有两部分空间, 分别是内核空间和用户空间. 如果直接使用open, read系统函数去从文件中读取数据, 会先从磁盘到内核空间, 然后再从内核空间到用户空间. 但如果采用内存映射技术的话, 实际上会将磁盘中文件与进程的用户空间直接关联上, 也就是说, 我们可以在程序中直接使用指针的方式去读取对应的文件数据. 到这, 可能你会有疑问, 你刚才不是说从磁盘到内存的数据传输是必不可少的么?为什么这又说可以直接使用指针去读取文件数据. 这就涉及了逻辑内存与物理内存. 我们使用内存映射, 是将磁盘上的文件与进程逻辑地址一一对应, 关联的时候数据并没有真的拷贝到物理内存中, 只是建立了逻辑地址与磁盘文件的一一对应关系, 当要真正使用数据时, 发现物理内存中并没有数据, 会发生缺页中断等系统操作(具体怎么操作又是另外一门学问了, 暂且不管, 直奔我们的主题), 然后再从磁盘中交换所需要的数据到物理内存中. 所以, 其实还是发生了磁盘到物理内存的数据传输. 下面的图片也许对你理解这个过程有一定的帮助.
open, read方式读取数据与内存映射读取数据对比
相信此时, 你应该已经明白为什么内存映射在理论上应该比open, read等方式读取数据更快了. 因为少了从内核空间到用户空间这一次拷贝过程. 而从内核空间到用户空间的拷贝实际上就是发生在内存中的. 所以我们可以这么认为, 内存映射比普通open,read方式快, 时间差就是数据从内核空间拷贝到用户空间所花费的时间. 这也解释了为什么大部分老的资料都说内存映射比普通方式快很多, 提升效果很明显. 因为当时的内存速度还不够快, 内存中传输数据的时间很长, 在当时使用内存映射技术, 节省的这段传输时间, 占总时间的比重很大, 因此效果很明显. 但是随着内存速度越来越快, 内存中传输数据的时间已经越来越短了, 在这种情况下, 再使用内存映射技术去读取数据, 能感受到效率的提升变得没那么明显了. 同时, 也能解释为什么数据量越大, 使用内存映射优势越明显, 这个原因相信你也能明白. 好了, 原理分析了, 也有了理论上的解释, 接下来, 我们以一个C/C++小例子去验证我们的观点.

C/C++程序

在这里, 我们在Windows平台上使用内存映射, 和普通方式进行对比.

目的

验证之前分析的观点, 也就是内存映射和普通读取方式相比, 少了内存中(内核空间到用户空间)的数据拷贝这一时间.

方法

分别以两种方式读取2G, 3G文件. 然后分别在内存中传输2G, 3G文件. 验证两种方式的时间差是否与内存中传输对应数据所花费的时间一致.

数据

内存映射(ms)open, read(ms)时间差(ms)内存中传输时间(ms)
2G198662110012341114
3G312543317719232047

结论

从上面的表中, 可以看出内存映射与普通open, read方式读取数据的时间差与内存中传输相应数据的时间基本相等, 所以基本可以验证之前分析的结论.

总结

到此为止, 我们从原理上分析了内存映射, 并且做了相应的实验, 得出了结论. 说明一下, 实验中难免会有实验误差, 我是采用的多次测试的平均值, 而且每次跑完都清理了内存中缓存, 再进行下一次实验, 所以实验结果基本大致反映了事实.
当然, 还有一些不足. 我的电脑内存大小是8G. 所以无法进行更大数据的测试. 也许你会好奇, 你内存不是8G么, 为什么只测到了3G大小? 在此说明一下, 因为做内存中传输时间这一项验证的时候, 需要申请两个大小为3G的内存, 从而进行两者的数据传输. 所以如果数据量再大的话, 内存就不够用了.
好了, 综上的论述, 可以汇成一句话: 内存映射的优势越来越小, 而且由于普通open, read方式的简单, 我推荐大家还是就使用普通的方式读取数据就好了. 当然, 有两种特殊情况, 一个是数据量非常大, 几十上百G甚至一个T大小的时候, 目前的内存速度还没有那么快, 所以这种情况使用内存映射效率提升应该还是很明显的.第二种情况是如果只想读取或者修改文件中某个地方的时候, 使用内存映射还是挺方便的, 因为就相当于直接操纵指针. 除了这两种情况, 我建议大家还是用普通的方式就好, 如果没有特殊需求, 也不用折腾去研究内存映射了, 腾出更多的时间去做更有意义的事情. 这就是我这篇文章的目的.
原创文章, 欢迎点赞, 收藏, 留言.

注释:有时间再把C/C++程序写出来供大家参考.

  • 12
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值