设为首页 加入收藏

TOP

VC读取大文件之创建文件映射及文件写入效率
2015-07-22 17:34:52 】 浏览:993
Tags:读取 文件 创建 映射 写入 效率

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

从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么呢?原因是read()是系统调用,其中进行了数据拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,如图2中过程1,然后再将这些数据拷贝到用户空间,如图2中过程2,在这个过程中,实际上完成了 两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。因此,内存映射的效率要比read/write效率高。(引用自http://blog.csdn.net/mg0832058/article/details/5890688)

本文主要以代码的方式演示读取大文件的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自己的扩容机制浪费CPU
	vecTime.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
  
   0 )
			{
				dwDataLen = qwFileSize
   
    >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

统计绘制效率:

//一次冒泡排序找到最小的那个数及其索引,索引很重要,我们可以知道每次写入多大时效率最高
	DWORD dwMinTime = vecTime[0];
	size_t nIndex = 0;
	for ( size_t i=1; i
   
    
绘制效率图:

	case WM_SIZE:
		{
			g_bSizeChange = true;
			break;
		}
	case WM_PAINT:
		{
			hdc = BeginPaint(hWnd, &ps);
			// TODO: 在此添加任意绘图代码...
			RECT rcClient;
			GetClientRect(hWnd, &rcClient);
			if ( g_bSizeChange )
			{//窗口大小改变了,需要我们重新创建对应大小的缓冲DC
				if ( 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
     
      
由于循环执行200次,I/O操作相对耗时,为了防止把电脑卡死了,就在每次写完Sleep(100);绘制部分的视图大小有限有的区域无法绘制出来就会出现断线,绘制结果图:


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

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇vc在指定目录生成快捷方式 下一篇VC点击按键弹出文件路径选择对话框

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目