VC读取大文件之创建文件映射及文件写入效率

文件太大,没法一次读取到内存进行操作?Windows提供了内存映射API来读取大文件,与普通文件读取相比,内存映射效率比较高。

本文主要以代码的方式演示读取大文件的API使用,顺带测试了缓冲区大小与写文件速度的关系,以及绘制文件写入速率图。

HANDLE hFile = NULL;HANDLE hFileMap = NULL;LARGE_INTEGER liResult;hFile = CreateFile(L"e:\\1.zip", GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if ( INVALID_HANDLE_VALUE == hFile ){goto __TestEnd;}//创建文件映射hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);if ( NULL == hFileMap ){goto __TestEnd;}//得到系统分配粒度SYSTEM_INFO si;GetSystemInfo(&si);DWORD dwSysGran = si.dwAllocationGranularity;//得到文件大小LARGE_INTEGER lFileSize;GetFileSizeEx(hFile, &lFileSize);CloseHandle(hFile);hFile = INVALID_HANDLE_VALUE;//性能方面,都知道容器大小了就先初始化大小,免得用vector自己的扩容机制浪费CPUvecTime.resize(200, 0);char szPath[MAX_PATH] = {0};for ( int i=1; i<=200; ++i ){__int64 qwFileOffset = 0;__int64 qwFileSize = lFileSize.QuadPart;DWORD dwDataLen = 0;FILE* fp = NULL;DWORD dwBlockBytes = i*dwSysGran;if ( lFileSize.QuadPart<dwBlockBytes )dwBlockBytes = lFileSize.QuadPart;sprintf(szPath, "d:\\test\\%d.zip", i);//DeleteFileA(pFile);{//PERFOR_TEST("复制文件测试4KB_1");CPerforTest test(&liResult);fp = fopen(szPath, "ab+");while( qwFileSize>0 ){dwDataLen = qwFileSize<dwBlockBytes? qwFileSize : dwBlockBytes;LPBYTE lpData = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_READ|FILE_MAP_WRITE, (DWORD)(qwFileOffset>>32),(DWORD)(qwFileOffset&0xffffffff), dwDataLen);if ( NULL == lpData )break;//把文件复制到另一个目录下,写文件操作//fp = fopen(szPath, "ab+");//追加方式写入文件,不存在则创建fwrite(lpData, dwDataLen, 1, fp);//fclose(fp);UnmapViewOfFile(lpData);qwFileOffset += dwDataLen;qwFileSize -= dwDataLen;}fclose(fp);}vecTime[i-1] = liResult.LowPart/300;Sleep(100);}__TestEnd:DWORD dwError = GetLastError();//内核句柄清理工作if ( hFile != INVALID_HANDLE_VALUE ){CloseHandle(hFile);hFile = INVALID_HANDLE_VALUE;}if ( hFileMap ){CloseHandle(hFileMap);hFileMap = NULL;}值得注意的是,,内存映射大小必须是系统分配大小基数的倍数。每次读完一段,我们就把这个文件指针位置qwFileOffset后移一段直到读完。还有就是,必须是有多少读多少,最后一次往往其空间比正常分配的小,我们需要计算分配空间:dwDataLen = qwFileSize<dwBlockBytes? qwFileSize : dwBlockBytes;不然的话,分配空间大于文件剩余大小,MapViewOfFile就会失败。

统计绘制效率:

//一次冒泡排序找到最小的那个数及其索引,索引很重要,我们可以知道每次写入多大时效率最高DWORD dwMinTime = vecTime[0];size_t nIndex = 0;for ( size_t i=1; i<vecTime.size(); ++i ){if ( vecTime[i]<dwMinTime ){dwMinTime = vecTime[i];nIndex = i;}}g_dwMinTime = dwMinTime;g_nBuffSize = (nIndex+1)*dwSysGran/1024;//换算成KB//通知窗口刷新绘制g_bInit = true;InvalidateRect(hWnd, NULL, TRUE);BringWindowToTop(hWnd);return 0;绘制效率图:

case WM_SIZE:{g_bSizeChange = true;break;}case WM_PAINT:{hdc = BeginPaint(hWnd, &ps);// TODO: 在此添加任意绘图代码…RECT rcClient;GetClientRect(hWnd, &rcClient);if ( g_bSizeChange ){//窗口大小改变了,需要我们重新创建对应大小的缓冲DCif ( g_hMemDC ){DeleteDC(g_hMemDC);DeleteObject(g_hMemBmp);}g_hMemDC = CreateCompatibleDC(hdc);g_hMemBmp= CreateCompatibleBitmap(hdc, rcClient.right-rcClient.left, \rcClient.bottom-rcClient.top);SelectObject(g_hMemDC, g_hMemBmp);g_bSizeChange = false;}if ( g_bInit ){HPEN hOldPen = (HPEN)SelectObject(g_hMemDC, g_hPen);POINT pt;MoveToEx(g_hMemDC, 0, 0, &pt);for ( size_t i=0; i<vecTime.size(); ++i ){LineTo(g_hMemDC, (i+1)*5, rcClient.bottom-vecTime[i]);}::SelectObject(g_hMemDC, hOldPen);wchar_t szText[100] = {0};swprintf(szText, L"缓冲区为:%u KB时,写入文件用时最短:%u", g_nBuffSize, g_dwMinTime);SetTextColor(g_hMemDC, RGB(255,0,0));SetBkMode(g_hMemDC, TRANSPARENT);RECT rcText = {10,0,600,40};DrawText(g_hMemDC, szText, wcslen(szText), &rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE);}::BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, g_hMemDC, 0, 0, SRCCOPY);EndPaint(hWnd, &ps);}break;由于循环执行200次,I/O操作相对耗时,为了防止把电脑卡死了,就在每次写完Sleep(100);绘制部分的视图大小有限有的区域无法绘制出来就会出现断线,绘制结果图:

我的测试文件是一个大小为15M左右的文件,缓冲区变化范围:1×63KB—-200×63KB,这里最优的竟然是每次写入大约1.7M数据时。

有时不但是必要的,而且是很有必要的。

VC读取大文件之创建文件映射及文件写入效率

相关文章:

你感兴趣的文章:

标签云: