使用記憶體對映檔案來提高你程式的效能
阿新 • • 發佈:2019-02-04
本人在學習《WINDOWS核心程式設計》的時候對JEFFREY大師提到的一個小程式寫了兩個版本來比較效能,該程式的原始需求是這樣的:對一個大檔案進行倒序,也就是將一個檔案頭變成尾,尾變成頭。
使用的方法有很多種,這裡使用兩個方法來比較,主要是突出使用記憶體對映檔案好處;兩種方法為:記憶體對映檔案方法,I/O讀寫的快取辦法。
第一種辦法是建立記憶體對映檔案物件,然後將該物件對映到程序的地址空間中,再讀取檔案內容,然後倒序,再寫入檔案。
第二中方法是,將檔案內容讀入一個大的緩衝區,然後倒序,再寫入檔案,中間對原來的檔案刪除,然後重新寫入。
程式編寫如下
第一種方法,記憶體對映檔案方式:
BOOL FileReverse(PCTSTR pszPathName)
{
HANDLE hFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("File could not be opened.");
return FALSE;
}
DWORD dwFileSize = GetFileSize(hFile,NULL);
HANDLE hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,
dwFileSize+sizeof(char),NULL);
if(hFileMap == NULL){
CloseHandle(hFile);
return FALSE;
}
PVOID pvFile = MapViewOfFile(hFileMap,FILE_MAP_WRITE,0,0,0);
if(pvFile == NULL){
CloseHandle(hFileMap);
CloseHandle(hFile);
return FALSE;
}
PSTR pchAnsi = (PSTR)pvFile;
pchAnsi[dwFileSize/sizeof(char)]=0;
_strrev(pchAnsi);
pchAnsi = strchr(pchAnsi,'/n');
while(pchAnsi != NULL){
*pchAnsi++ ='/r';
*pchAnsi++ ='/n';
pchAnsi = strchr(pchAnsi,'/n');
}
UnmapViewOfFile(pvFile);
CloseHandle(hFileMap);
SetFilePointer(hFile,dwFileSize,NULL,FILE_BEGIN);
SetEndOfFile(hFile);//實際上不需要寫入了。
CloseHandle(hFile);
return TRUE;
}
第二中方法,使用快取的方式:
BOOL FileReverseNoMap(PCTSTR pszPathName)
{
HANDLE hFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("File could not be opened.");
return FALSE;
}
DWORD dwFileSize = GetFileSize(hFile,NULL);
//CloseHandle(hFile);
char *readBuf = new char[dwFileSize+1];
DWORD nRead = 0,nRet =0;
while(nRead if(ReadFile(hFile,readBuf+nRead,dwFileSize-nRead,&nRet,NULL) ==TRUE)
{
nRead+= nRet;
}
else
{
printf("Can read the file!");
CloseHandle(hFile);
}
}
PSTR pchAnsi = (PSTR)readBuf;
pchAnsi[dwFileSize/sizeof(char)]=0;
_strrev(pchAnsi);
pchAnsi = strchr(pchAnsi,'/n');
while(pchAnsi != NULL){
*pchAnsi++ ='/r';
*pchAnsi++ ='/n';
pchAnsi = strchr(pchAnsi,'/n');
}
CloseHandle(hFile);
DeleteFile(pszPathName);
HANDLE hWriteFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
WriteFile(hWriteFile,readBuf,dwFileSize,&nRet,NULL);
CloseHandle(hWriteFile);
delete readBuf;
return TRUE;
}
我運行了幾次,比較結果如下:
本人測試機器的CPU是迅池1.5的筆記本,記憶體為712MB
通過上面的測試我們可以看到使用記憶體對映檔案的好處,在檔案記憶體越大這種優勢就體現的越明顯,其中主要的原因是:
記憶體對映檔案直接將檔案的地址對映到程序的地址空間中,那麼操作檔案就相當於在記憶體中操作一樣,省去了讀和寫I/O的時間;第二種方式是必須這麼做(READFILE,WRITEFILE),這個過程是很慢的。
使用的方法有很多種,這裡使用兩個方法來比較,主要是突出使用記憶體對映檔案好處;兩種方法為:記憶體對映檔案方法,I/O讀寫的快取辦法。
第一種辦法是建立記憶體對映檔案物件,然後將該物件對映到程序的地址空間中,再讀取檔案內容,然後倒序,再寫入檔案。
第二中方法是,將檔案內容讀入一個大的緩衝區,然後倒序,再寫入檔案,中間對原來的檔案刪除,然後重新寫入。
程式編寫如下
第一種方法,記憶體對映檔案方式:
BOOL FileReverse(PCTSTR pszPathName)
{
HANDLE hFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("File could not be opened.");
return FALSE;
}
DWORD dwFileSize = GetFileSize(hFile,NULL);
HANDLE hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,
dwFileSize+sizeof(char),NULL);
if(hFileMap == NULL){
CloseHandle(hFile);
return FALSE;
}
PVOID pvFile = MapViewOfFile(hFileMap,FILE_MAP_WRITE,0,0,0);
if(pvFile == NULL){
CloseHandle(hFileMap);
CloseHandle(hFile);
return FALSE;
}
PSTR pchAnsi = (PSTR)pvFile;
pchAnsi[dwFileSize/sizeof(char)]=0;
_strrev(pchAnsi);
pchAnsi = strchr(pchAnsi,'/n');
while(pchAnsi != NULL){
*pchAnsi++ ='/r';
*pchAnsi++ ='/n';
pchAnsi = strchr(pchAnsi,'/n');
}
UnmapViewOfFile(pvFile);
CloseHandle(hFileMap);
SetFilePointer(hFile,dwFileSize,NULL,FILE_BEGIN);
SetEndOfFile(hFile);//實際上不需要寫入了。
CloseHandle(hFile);
return TRUE;
}
第二中方法,使用快取的方式:
{
HANDLE hFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("File could not be opened.");
return FALSE;
}
DWORD dwFileSize = GetFileSize(hFile,NULL);
//CloseHandle(hFile);
char *readBuf = new char[dwFileSize+1];
DWORD nRead = 0,nRet =0;
while(nRead if(ReadFile(hFile,readBuf+nRead,dwFileSize-nRead,&nRet,NULL) ==TRUE)
{
nRead+= nRet;
}
else
{
printf("Can read the file!");
CloseHandle(hFile);
}
}
PSTR pchAnsi = (PSTR)readBuf;
pchAnsi[dwFileSize/sizeof(char)]=0;
_strrev(pchAnsi);
pchAnsi = strchr(pchAnsi,'/n');
while(pchAnsi != NULL){
*pchAnsi++ ='/r';
*pchAnsi++ ='/n';
pchAnsi = strchr(pchAnsi,'/n');
}
CloseHandle(hFile);
DeleteFile(pszPathName);
HANDLE hWriteFile = CreateFile(pszPathName,GENERIC_WRITE|GENERIC_READ,0,NULL
,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
WriteFile(hWriteFile,readBuf,dwFileSize,&nRet,NULL);
CloseHandle(hWriteFile);
delete readBuf;
return TRUE;
}
我運行了幾次,比較結果如下:
檔案大小(byte) | 1方法時間(ms) | 2方法時間(ms) |
25416 | 0 | 0 |
101664 | 0 | 0 |
406656 | 0 | 10 |
1219968 | 10 | 30 |
3202416 | 21 | 100 |
9607248 | 80 | 551 |
67250736 | 581 | 5568 |
本人測試機器的CPU是迅池1.5的筆記本,記憶體為712MB
通過上面的測試我們可以看到使用記憶體對映檔案的好處,在檔案記憶體越大這種優勢就體現的越明顯,其中主要的原因是:
記憶體對映檔案直接將檔案的地址對映到程序的地址空間中,那麼操作檔案就相當於在記憶體中操作一樣,省去了讀和寫I/O的時間;第二種方式是必須這麼做(READFILE,WRITEFILE),這個過程是很慢的。