内存映射处理大文件并实现逆序输出

上一篇介绍了一种常见的文件处理方法(可优化为:分次读取文件,但要满足根据行号能快速索引该行内容时会遇到麻烦),所以此片我将介绍另一种更高效,实用,并对本进程的内存空间地址消耗小的方法!


一. 预备知识

1). Windows编程处理文件的相关接口:

A)CreateFile();

B)ReadFile();

c)WriteFile();

d)CloseHandle();

2). 文件映射相关接口:

A)  CreateFileMapping()

B)MapFIleOfView()

C)OpenFileMapping()

D)CloseHandle()

3)注:

由于以上接口MSDN以及网上的牛人们已经给了详细的介绍了,这里我只累叙 一下MapViewOfFile()第四个参数 DWORD dwFileOffsetLow;

此偏移量必须64k对齐,可能是由于便于内存管理得快速访问,才做此限定,如下代码:

    LARGE_INTEGER liOffset = {};
    liOffset.QuadPart = lpLineInfo->liHeadCharOffset.QuadPart & (~MemmoryHex_64k);
    DWORD DValue = DWORD(lpLineInfo->liHeadCharOffset.QuadPart - liOffset.QuadPart);

    char *lpHeadSrc = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, liOffset.HighPart, liOffset.LowPart, (DValue + lpLineInfo->LineCount));

二.思想

1) 在处理文件过程中,我们一般采用的方法是分块(如每次512k)读取文件数据进行处理,这样做的目的如下

A)申请小块内存速度快

B)可以不用New动态创建存数据的Buffer,而是直接可以用栈存储次Buffer,降低内存泄漏的风险

C)若一次申请一块较大的地址空间(>512M),那你还让其他线程工作吗?一个进程中的多个线程是共用同一块地址空间的!

对于B:有朋友会说,那可以将进程中允许访问的栈空间调大(1G),但这样是有风险的,比如你在编写递归代码时出错,可能导致死循环,由于堆设得过大,导致要许久才会导致栈溢出,增加我们调式bug得难度!而当栈的空间地址范围较小时,函数调用栈很快就会溢出,Buf易复现,就便于修改了!


2)当文件大小在允许访问内(我的机器<24G),将文件内容映射到内存上,对文件的访问速率相当于内存的读取率;

A)注:我的公司开发机:Win7 64位,16G运行内存,15G虚拟内存)

B)由于映射的内存块可以被本机的其他进程访问,所以我们通常也称之为共享内存!


3)每次映射小块文件数据到次进程的地址空间上,进行处理(与上篇中处理方式一样)

        DWORD RelMapMemorySize = DWORD((liTotalFileSize.QuadPart - liHandleFileSize.QuadPart + DValue > EachMapMemorySzie) ? EachMapMemorySzie : (liTotalFileSize.QuadPart - liHandleFileSize.QuadPart + DValue));

        lpHeadSrc = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, liOffset.HighPart, liOffset.LowPart, RelMapMemorySize);

4)根据行号索引该行内容,相当于随机访问数组的速率

相交与上篇,此篇存储每一行信息的数据结构有所改变,如下

    typedef struct LINE_INFO
    {
        LARGE_INTEGER   liHeadCharOffset;
        INT32           LineCount;
    }LINE_INFO;

A)我们根据liHeadCharOffset算出映射时候的偏移量!

B)注意:我们必须保证此低字节的偏移量必须64K对其,所以这里必须先对liHeadCharOffset进行处理!

a)映射偏移量首先对此值取商

    LARGE_INTEGER liOffset = {};
    liOffset.QuadPart = lpLineInfo->liHeadCharOffset.QuadPart & (~MemmoryHex_64k);
    DWORD DValue = DWORD(lpLineInfo->liHeadCharOffset.QuadPart - liOffset.QuadPart);

b)处理时再加上我们做商时舍弃的差值

    char *lpHeadSrc = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, liOffset.HighPart, liOffset.LowPart, (DValue + lpLineInfo->LineCount));

    if (NULL == lpHeadSrc)
    {
        m_lpHLog->TraceLog("Error(%s.%d): MapViewOfFile Failed!\r\n", __FUNCTION__, __LINE__);
        return FALSE;
    }

    char *lpHead = lpHeadSrc + DValue;

	5)关于倒叙写文件相信聪明的朋友也能按照上述方案分块映射分块写了,就不在累叙了

	6)其余处理之处应与上一篇类似(实际已近不记得了,因为此代码是上个月初写的,我上一篇的代码是这个月初写的。。。),也不过多介绍了!

三. 最后上一下运行图
	注:本机是在是找不到大的文本文件,当明显速度比上一篇中的处理速度快,且我在开发机上测试时,Release模式下(不包含最后逆序写文件)文件处理速度达到100M/s
这数据也许不准,因为Windows操作系统对开机第一次读取文件比以后读取文件速率慢很多,据说与什么预读和磁盘命中率有关,总之就是比文件映射处理文件速率比较快!
	


	此文件80M左右,Debug模式下,给我的感觉基本是1s左右!(我的本本是5年前卖的)
	最后看了下时间,如右:2016/08/05 22:40:43.407   Time is 1357(单位是ms)

四)如果对源码有兴趣得朋友可以点击这里下载源码!
 
五)如发现我上述观点有错误的地方,希望朋友不啬指出,方便大家共同提升,谢谢!
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值