以文本方式查看主题 - 咿思舞论坛 (http://bbs.145829.com/index.asp) -- 『网站资源』 (http://bbs.145829.com/list.asp?boardid=8) ---- 用Windows的文件映射机制,实现大批量数据的快速存储-ASP教程,系统相关 (http://bbs.145829.com/dispbbs.asp?boardid=8&id=164) |
-- 作者:ysw829 -- 发布时间:2009/8/21 15:59:43 -- 用Windows的文件映射机制,实现大批量数据的快速存储-ASP教程,系统相关 上次做的电子相册软件,为了解决大文件读取速度慢的问题,使用了windows下的文件映射功能,使文件读取效率顿时得到了大幅度提升。(具体见:一个读取速度超快的filestream!) 最近在做的一款软件,又使用到了这个函数,不过这次的要求是这样的: 系统的主程序会持续的从网络端口上接收数据,这些数据需要变为实时的图像显示,但是同时图像显示部分又需要有回顾功能,也就是说能够任意将历史的数据调出来显示,为此就必须将所有历史数据保存下来,同时在需要的时候又能够快速从历史数据的指定位置将数据读出来。 针对此,有两种方案: 1)在主程序所在的机器接收数据前,使用另一台电脑,配合一个转发数据的程序,来达到数据的截取功能,由这个转发数据的程序将所有数据存储下来,并在主程序需要使用时,再用网络发送给主程序。 2)使用主程序来进行数据存储,提高主程序存储数据的性能。 不管采用何种方案,最终的瓶颈都将是大数据量的快速保存。由于整个系统内存使用和速度上的要求都很高,因此不可能将这样的数据放在程序内存里,也不可能使用普通的文件方式来记录数据。最终看来只有求助于windows的文件映射功能了,它的优点是不会将要操作的文件读入内存,而是直接在系统层对文件进行读写,因此省去了文件的复制过程,特别是在读写大文件时,它能带来的速度提升是非常显著的。这样就可以将文件当作大内存来用了。 新的程序写完,才发现原来以前用delphi实现的那个版本根本不能够访问大文件,只是在读取文件速度上有些优势而已,因为我过去的做法是在createfilemapping()之后,将整个文件的内存都mapviewoffile()到程序内存里,这样文件一大,程序仍然无法打开文件。现在才知道,mapviewoffile()函数是可以指定从文件的哪个部分进行map,同时map多少进入内存的。对于真正的大文件(几个g的)的访问,因该是一块一块的map,然后进行读写。同时map的起始地址和map的内存大小都必须是64k的整数倍,不然mapviewoffile()函数会失败。 最初的一个简单的程序版本花了1个小时不到就写完了,但是一测试却发现有个严重的问题:没有考虑数据在map内存的边界上时的问题。当要读写的数据正好在map的内存的边界上时,从当前map的内存中就只能取到数据的前半部分,另一部分的数据必须map文件的下一块地址才可能取到。因此对程序又进行了彻底的改造,让其能够在读取一个map内存发现不够时,自动打开下一个map内存。 总算大功告成,写了一个简单的测试程序对其进行测试,速度和正确性都都非常理想。 最后,贴上程序代码: #ifndef enstfilecache_h #define enstfilecache_h #include <qtcore/qvariant> #include <qtcore/qobject> #include <windows.h> #include "../enstdefine.h" /*! \\brief sampler::enstfilecache \\author tony (http://www.tonixsoft.com) \\version 0.08 \\date 2006.05.10 基于文件镜像的快速大容量数据存储类。该类使用windows下的 createfilemapping() 函数实现,不支持其它系统。\\n 该类中的文件镜像原理可以参考:http://www.juntuan.net/hkbc/winbc/n/2006-04-19/14320.html \\n 当要读取或写入的数据跨多个mapview的时候,该类会自动处理mapview的切换。 */ class enstfilecache : public qobject { q_object public: /*! construct the class. */ enstfilecache(); /*! destruct the class. */ ~enstfilecache(); /*! 打开镜像文件。 @return 当打开镜像文件失败时返回false,比如磁盘空间不够。 */ bool createfilecache(const qstring &pfilename); /*! 向镜像文件中追加数据。 */ bool appenddata(t_int8* pdata, int pdatalength); /*! 从镜像文件的指定位置读取数据。 */ bool readdata(t_int64 paddressoffset, t_int8* pdata, int pdatalength); protected: void dumpwindowserrormessage(); private: t_int64 mmappingviewsize; handle mfilehandle; handle mmappinghandle; t_int64 mwritemappingoffset; lpvoid mwritebuffer; t_int64 mwritebufferoffset; t_int64 mreadmappingoffset; lpvoid mreadbuffer; }; #endif //enstfilecache_h ===================================================== |
-- 作者:ysw829 -- 发布时间:2009/8/21 16:00:18 -- #include "enstfilecache.h" #include "../enstsvcpack.h" //#define file_cache_size 0x40000000 /* = 1gb */ #define file_cache_size 0x01e00000 /* = 30mb */ enstfilecache::enstfilecache() { mmappingviewsize = 1024*(64*1024); //windows default block size is 64kb mfilehandle = invalid_handle_value; mmappinghandle = null; mwritemappingoffset = 0; mwritebuffer = null; mwritebufferoffset = 0; mreadmappingoffset = 0; mreadbuffer = null; } enstfilecache::~enstfilecache() { if (mwritebuffer != null) { unmapviewoffile(mwritebuffer); } if (mreadbuffer != null) { unmapviewoffile(mreadbuffer); } if (mmappinghandle != null) { closehandle(mmappinghandle); } if (mfilehandle != invalid_handle_value) { closehandle(mfilehandle); } } bool enstfilecache::createfilecache(const qstring &pfilename) { enstlogservice *logservice = enstlogservice::getmyaddr(); mfilehandle = createfile(pfilename.toascii(), generic_read | generic_write, file_share_read, null, open_always, file_attribute_normal, 0); if (mfilehandle == invalid_handle_value) { dumpwindowserrormessage(); logservice->appendlog(this, "error when create file."); return false; } mmappinghandle = createfilemapping(mfilehandle, null, page_readwrite, (dword)(file_cache_size>>32), (dword)(file_cache_size & 0xffffffff), null); if (mmappinghandle == null) { if (getlasterror() == 112) { logservice->appendlog(this, "looks like its not enough space on the disk."); } dumpwindowserrormessage(); logservice->appendlog(this, "error when create file mapping."); return false; } mwritemappingoffset = 0; mwritebuffer = null; mwritebufferoffset = 0; mreadmappingoffset = 0; mreadbuffer = null; return true; } bool enstfilecache::appenddata(t_int8* pdata, int pdatalength) { int datawrote = 0; do { int datacanwrite = mmappingviewsize - mwritebufferoffset; if (mwritebuffer && datacanwrite <= 0) { unmapviewoffile(mwritebuffer); mwritebuffer = null; mwritemappingoffset += mmappingviewsize; } if (mwritebuffer == null) { mwritebuffer = mapviewoffile(mmappinghandle, file_map_write, (dword)(mwritemappingoffset>>32), (dword)(mwritemappingoffset & 0xffffffff), mmappingviewsize); mwritebufferoffset = 0; datacanwrite = mmappingviewsize - mwritebufferoffset; if (! mwritebuffer) { dumpwindowserrormessage(); enstlogservice *logservice = enstlogservice::getmyaddr(); logservice->appendlog(this, "error when map view of file."); return false; } } int datatowrite = pdatalength - datawrote; int actualdatatowrite = (datacanwrite >= datatowrite)?datatowrite:datacanwrite; memcpy((pbyte)mwritebuffer+mwritebufferoffset, (pbyte)pdata+datawrote, actualdatatowrite); mwritebufferoffset += actualdatatowrite; datawrote += actualdatatowrite; } while (datawrote < pdatalength); return true; } |
-- 作者:ysw829 -- 发布时间:2009/8/21 16:00:41 -- bool enstfilecache::readdata(t_int64 paddressoffset, t_int8* pdata, int pdatalength) { int datareaded = 0; do { int datacanread = mreadmappingoffset + mmappingviewsize - paddressoffset - datareaded; if (mreadbuffer && (datacanread <= 0 || datacanread > mmappingviewsize)) { unmapviewoffile(mreadbuffer); mreadbuffer = null; } if (mreadbuffer == null) { mreadmappingoffset = (paddressoffset + datareaded) / mmappingviewsize * mmappingviewsize; mreadbuffer = mapviewoffile(mmappinghandle, file_map_read, (dword)(mreadmappingoffset>>32), (dword)(mreadmappingoffset & 0xffffffff), mmappingviewsize); datacanread = mreadmappingoffset + mmappingviewsize - paddressoffset - datareaded; if (! mreadbuffer) { dumpwindowserrormessage(); enstlogservice *logservice = enstlogservice::getmyaddr(); logservice->appendlog(this, "error when map view of file."); return false; } } int datatoread = pdatalength - datareaded; int actualdatatoread = (datacanread >= datatoread)?datatoread:datacanread; memcpy((pbyte)pdata+datareaded, (pbyte)mreadbuffer+paddressoffset-mreadmappingoffset+datareaded, actualdatatoread); datareaded += actualdatatoread; } while (datareaded < pdatalength); return true; } void enstfilecache::dumpwindowserrormessage() { lpvoid lpmsgbuf; dword dw = getlasterror(); formatmessage( format_message_allocate_buffer | format_message_from_system, null, dw, makelangid(lang_neutral, sublang_default), (lptstr) &lpmsgbuf, 0, null ); enstlogservice *logservice = enstlogservice::getmyaddr(); logservice->appendlog(this, (lptstr)lpmsgbuf); //puts((lptstr)lpmsgbuf); localfree(lpmsgbuf); } #ifdef win32 #include "moc_enstfilecache.cpp" #endif 由于系统是用qt开发的,因此类中使用了不少qt的类,同时也使用了系统中的部分工具类,不过基本工作原理相信你是能够看懂的。另外,整个系统是计划要跨平台使用的,因此今后还需要实现linux下的类似功能,目前这个版本被绑定在windows平台上了,不幸。 |