关于内存映射文件
本贴的主题是:到底为什么,用内存映射来处理大文件,会比直接使用文件IO更快?
------------
以前因为需要共享数据,用过内存映射文件。
但是由于时间仓促、自己懒惰,对于这个机制没有深究。反正开发任务搞定,就算完事。当时自己心里鲁莽的臆测:内存映射文件的APIs,无非是建立在基本文件IO APIs基础之上的,更高级的APIs而已。
前两天看到这里一篇帖子:《用C语言读取大文件的问题》。作者要读写很大的磁盘文件,单个文件上G(这在遥感图像处理领域很稀松平常)。他说用基本的文件IO APIs特别慢。随后有很多网友提出要用内存映射文件APIs来解。
我胡乱了骂了一通,结果大家还是给足了我面子——没理我。我的回帖的主要意思是,文件不管大小都要用基本文件IO APIs来读写,与内存映射无关。当然,暗含的意思就是内存映射APIs最终也使用文件IO APIs来完成工作。
这是我一直以来的一个认识。但看来不太对头。
有人建议我看《Windows核心编程》相关内容。我看了,临时在网上抓的电子版,主要是着急想找到答案。但是看完后,发现并没有令我满意的答复。书中绝对没有详细讲,“到底为什么,用内存映射来处理大文件,会比直接使用文件IO更快”。绝对没有。
书中讲“可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I / O操作,并且可以不必对文件内容进行缓存”。这是被网友引用最多的一段话。
我想这段话是想说“使用内存映射APIs后,用户可以不再调用文件IO APIs,也能读写文件中的数据”。
但看来很多人便理解为“使用内存映射APIs后,(软件系统,甚至包括硬件)就可以不进行实际的文件读写操作,但是也能让用户读写文件数据”。因为他们推出结论“所以用内存映射操作文件,要比文件IO APIs更快”。
我并不是杜撰人们的想法。比如根据我google发现,网上有人说“内存映射文件直接将文件的地址映射到进程的地址空间中,那么操作文件就相当于在内存中操作一样,省去了读和写I/O的时间;第二种方式(指文件IO API方式)是必须这么做(READFILE,WRITEFILE),这个过程是很慢的。”(http://www.xiaozhou.net/ReadNews.asp?NewsID=953)
我暂时坚信这是错误并且可笑的。物理磁盘读写耗时,无论如何不可能省略。物理磁盘上的比特,不可能穿越一个时空奇点直接跳到用户的Buffer中。是啊,在不看鬼片时,我还是个唯物主义者,不相信神话。
那么,“到底为什么,用内存映射来处理大文件,会比直接使用文件IO更快”?
我能想出唯一鲁莽臆测是:人们使用文件IO APIs时,一般是使用默认的系统缓存方式(即 不 指定“FILE_FLAG_NO_BUFFERING”选项),这样一来,磁盘数据实际是先到达一个系统缓存,然后才被复制到用户缓冲区里。你看看FILE_FLAG_NO_BUFFERING的限制就可以猜出,对于缓存方式顺序读写文件来说,假如用户不按照FILE_FLAG_NO_BUFFERING的限制来读写,那么系统势必会采用算法,来将用户的“无礼要求”转换为系统更偏爱的“合理要求”。有了这个“转换-复制”过程的文件读写,之于纯粹的磁盘IO,必将占用更多时间。而内存映射,恐怕是使用非缓冲模式的,设置映射窗口的位置、大小限制与FILE_FLAG_NO_BUFFERING的限制有些类似,所以我猜想内存映射是非缓冲的。所以才会更快。而且不但非缓冲,也没有“系统缓冲到用户缓冲”的复制过程,所以显得快一些吧。
有没有高手可以给出正解?我还是不太相信自己的臆测。臆测毕竟是臆测。