zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

使用内存映射文件来提高你程序的性能

2023-09-27 14:27:56 时间

本人在学习《WINDOWS核心编程》的时候对JEFFREY大师提到的一个小程序写了两个版本来比较性能,该程序的原始需求是这样的:对一个大文件进行倒序,也就是将一个文件头变成尾,尾变成头。

使用的方法有很多种,这里使用两个方法来比较,主要是突出使用内存映射文件好处;两种方法为:内存映射文件方法,I/O读写的缓存办法。

第一种办法是创建内存映射文件对象,然后将该对象映射到进程的地址空间中,再读取文件内容,然后倒序,再写入文件。

第二中方法是,将文件内容读入一个大的缓冲区,然后倒序,再写入文件,中间对原来的文件删除,然后重新写入。

程序编写如下

第一种方法,内存映射文件方式:
None.gifBOOL FileReverse(PCTSTR pszPathName)
ExpandedBlockStart.gif{
InBlock.gifHANDLE hFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
InBlock.gif,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
InBlock.gifif(hFile == INVALID_HANDLE_VALUE)
ExpandedSubBlockStart.gif{
InBlock.gifprintf("File could not be opened.");
InBlock.gifreturn FALSE;
ExpandedSubBlockEnd.gif}
InBlock.gif
InBlock.gifDWORD dwFileSize = GetFileSize(hFile,NULL);
InBlock.gif
InBlock.gifHANDLE hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,
InBlock.gifdwFileSize+sizeof(char),NULL);
InBlock.gif
ExpandedSubBlockStart.gifif(hFileMap == NULL){
InBlock.gifCloseHandle(hFile);
InBlock.gifreturn FALSE;
ExpandedSubBlockEnd.gif}
InBlock.gif
InBlock.gifPVOID pvFile = MapViewOfFile(hFileMap,FILE_MAP_WRITE,0,0,0);
InBlock.gif
ExpandedSubBlockStart.gifif(pvFile == NULL){
InBlock.gifCloseHandle(hFileMap);
InBlock.gifCloseHandle(hFile);
InBlock.gifreturn FALSE;
ExpandedSubBlockEnd.gif}
InBlock.gif
InBlock.gifPSTR pchAnsi = (PSTR)pvFile;
InBlock.gifpchAnsi[dwFileSize/sizeof(char)]=0;
InBlock.gif_strrev(pchAnsi);
InBlock.gif
InBlock.gifpchAnsi = strchr(pchAnsi,\n);
ExpandedSubBlockStart.gifwhile(pchAnsi != NULL){
InBlock.gif*pchAnsi++ =\r;
InBlock.gif*pchAnsi++ =\n;
InBlock.gifpchAnsi = strchr(pchAnsi,\n);
ExpandedSubBlockEnd.gif}
InBlock.gif
InBlock.gifUnmapViewOfFile(pvFile);
InBlock.gifCloseHandle(hFileMap);
InBlock.gif
InBlock.gifSetFilePointer(hFile,dwFileSize,NULL,FILE_BEGIN);
InBlock.gifSetEndOfFile(hFile);//实际上不需要写入了。
InBlock.gifCloseHandle(hFile);
InBlock.gif
InBlock.gifreturn TRUE;
ExpandedBlockEnd.gif}
None.gif
第二中方法,使用缓存的方式:
None.gifBOOL FileReverseNoMap(PCTSTR pszPathName)
ExpandedBlockStart.gif{
InBlock.gifHANDLE hFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
InBlock.gif,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
InBlock.gifif(hFile == INVALID_HANDLE_VALUE)
ExpandedSubBlockStart.gif{
InBlock.gifprintf("File could not be opened.");
InBlock.gifreturn FALSE;
ExpandedSubBlockEnd.gif}
InBlock.gif
InBlock.gifDWORD dwFileSize = GetFileSize(hFile,NULL);
InBlock.gif//CloseHandle(hFile);
InBlock.gifchar *readBuf = new char[dwFileSize+1];
InBlock.gifDWORD nRead = 0,nRet =0;
ExpandedSubBlockStart.gifwhile(nRead dwFileSize){
InBlock.gifif(ReadFile(hFile,readBuf+nRead,dwFileSize-nRead, nRet,NULL) ==TRUE)
ExpandedSubBlockStart.gif{
InBlock.gifnRead+= nRet;
ExpandedSubBlockEnd.gif}
InBlock.gifelse
ExpandedSubBlockStart.gif{
InBlock.gifprintf("Can read the file!");
InBlock.gifCloseHandle(hFile);
ExpandedSubBlockEnd.gif}
ExpandedSubBlockEnd.gif}
InBlock.gif
InBlock.gifPSTR pchAnsi = (PSTR)readBuf;
InBlock.gifpchAnsi[dwFileSize/sizeof(char)]=0;
InBlock.gif_strrev(pchAnsi);
InBlock.gif
InBlock.gifpchAnsi = strchr(pchAnsi,\n);
ExpandedSubBlockStart.gifwhile(pchAnsi != NULL){
InBlock.gif*pchAnsi++ =\r;
InBlock.gif*pchAnsi++ =\n;
InBlock.gifpchAnsi = strchr(pchAnsi,\n);
ExpandedSubBlockEnd.gif}
InBlock.gifCloseHandle(hFile);
InBlock.gifDeleteFile(pszPathName);
InBlock.gif
InBlock.gifHANDLE hWriteFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
InBlock.gif,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
InBlock.gifWriteFile(hWriteFile,readBuf,dwFileSize, nRet,NULL);
InBlock.gifCloseHandle(hWriteFile);
InBlock.gif
InBlock.gifdelete readBuf;
InBlock.gif
InBlock.gifreturn TRUE;
ExpandedBlockEnd.gif}
我运行了几次,比较结果如下:
文件大小(byte) 1方法时间(ms) 2方法时间(ms)

本人测试机器的CPU是迅池1.5的笔记本,内存为712MB

通过上面的测试我们可以看到使用内存映射文件的好处,在文件内存越大这种优势就体现的越明显,其中主要的原因是:

内存映射文件直接将文件的地址映射到进程的地址空间中,那么操作文件就相当于在内存中操作一样,省去了读和写I/O的时间;第二种方式是必须这么做(READFILE,WRITEFILE),这个过程是很慢的。 
存储使者,内存驿站-文件操作 磁盘上的文件是文件 但是在程序设计中,我们一般谈的文件有两种:程序文件,数据文件(从文件功能的角度来分类的)
(理论篇)从基础文件IO说起虚拟内存,内存文件映射,零拷贝 新IO采用了内存映射的方式来处理输入/输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件了,通过这种方式比传统的输入/输出要快的多。通过内存映射机制操作文件比使用常规方法和使用FileChannel读写高效的多。
理解程序内存 内存对很多人来说感觉是个很熟悉的东西,因为我们在用VC调试程序时,很多时候都会察看内存中变量的值。但是,很多时候我们的思维也就因此局限在有源码的模块,当遇到一些跨模块或是没有源代码的Bug,我们还是无从下手。