对于大量的读文件操作,如何优化效率?

dyycdyyc 2016-07-21 09:07:31
现在有一个需求是,在文件中做简单的数据搜索。
没想到什么特殊的算法,于是把它当做字符串搜索处理了;由于有其他要求,需要从左往右搜索,我选用了KMP算法。
问题在于,文件非常大,动不动就是几百M几G的这种,而且搜索的次数也很多。
虽然不要求零点几秒或者一两秒就算完,但是测试的时候,-O2优化下在一个64M的文件中搜索一次64字节的串,就用了100多秒。

由于文件太大不能直接载入内存,程序是直接fread(&ch,1,1,fp)一个个读取一个个比较的(总感觉是作死),因为有人做过总结,这个函数的效率还是很高的。
但是我也很疑惑,为什么我的效率比别人的测试低了五倍,算法都很精简了,唯一不是自己写的东西也就一个vector<int>。
算起来用fread似乎也不能达到要求,即使参考上面链接里的数值,耗时也是几十几百秒的。
想想其他的软件,比如数据库,比如winrar,面对超大文件,哪一个不是10s以内就能读完的。
请教各位大神,对于我这种情况,怎么样读取文件才是最有效率的?

这是KMP函数的代码:
typedef long FILEPOS
typedef unsigned char _byte

FILEPOS CSimilarCore::KMP(FILE * src, vector<_byte>& str, vector<FILEPOS>& next)
{
FILEPOS mode_pos = 0;
bool forRead = true;
_byte ch;
while (!feof(src) && mode_pos < str.size()) {
//读取标识,避免文件指针回溯
if (forRead) {
fread(&ch, 1, 1, src);
}else {
forRead = true;
}
if (ch != str.at(mode_pos)) {
if (mode_pos == 0) {
//第一项失配
continue;
}else{
//根据跳转表控制指针
mode_pos = next.at(mode_pos - 1) + 1;
//fseek(src, -1, SEEK_CUR);
forRead = false;
}
}else{
//匹配成功,指针后移
mode_pos++;
}
}
if (mode_pos < str.size()) {
return -1;
}else{
FILEPOS res = ftell(src) - mode_pos;
//回溯文件指针,为下一次搜索准备
fseek(src, -1 - next.at(mode_pos - 1), SEEK_CUR);
return res;
}
}
...全文
791 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
mLee79 2016-07-22
  • 打赏
  • 举报
回复
这个搜索的时候基本上就读一次文件, mmap 没有意义, 只是节约了一次内核空间到用户地址空间的拷贝. 要那种反复随机访问的才适合 mmap .
mLee79 2016-07-22
  • 打赏
  • 举报
回复
引用 6 楼 dyycdyyc 的回复:
感谢楼上各位,使用块读取的方式之后,读文件的耗时从47%降到了0.2% [quote=引用 3 楼 mLee79 的回复:] 一次读 64K - 4M 左右就够了, 我测试搜索 100把条正则表达式构成的一组模式 的速度大约是 200M/s, 你这搜索单条确定的模式不应该比这慢.
另外能否请教一下“一组模式”的搜索方法?我这里也需要做很多搜索,只是把它们简单分成单条单条的循环了,如果能一次同时搜索几个串,那效率能提高很多。 之前想过用AC自动机来匹配,但是看了半天没看懂……不知道有没有什么其他方法[/quote] 我一般构造 DFA , 如果用非压缩表, 并且不存在大量导致回溯的模式, 速度是非常快的, 用压缩的表的话稍微慢一些..
dyycdyyc 2016-07-22
  • 打赏
  • 举报
回复
感谢楼上各位,使用块读取的方式之后,读文件的耗时从47%降到了0.2%
引用 3 楼 mLee79 的回复:
一次读 64K - 4M 左右就够了, 我测试搜索 100把条正则表达式构成的一组模式 的速度大约是 200M/s, 你这搜索单条确定的模式不应该比这慢.
另外能否请教一下“一组模式”的搜索方法?我这里也需要做很多搜索,只是把它们简单分成单条单条的循环了,如果能一次同时搜索几个串,那效率能提高很多。 之前想过用AC自动机来匹配,但是看了半天没看懂……不知道有没有什么其他方法
小灸舞 2016-07-22
  • 打赏
  • 举报
回复
快速读写磁盘数据的方法:

1.块读取:一下子将数据读取到内存的(无论是文本还是二进制),而不是一行行的读取。
2.异步的IO,创建多线程,或者使用重叠IO,IO复用,异步的事件回调通知机制(可以用事件对象,信号驱动来实现)。

3.优化分析文件的算法和尽量延后分析,分析算法里面频繁的申请字符串内存和释放字符串内存,尽量用指针解析出来,分析文件的内容可以延期到使用的时候才分析。

如果不注意,不小心 ,读取文件的主要性能消耗将会在这里,所以需要特别高的重视。

4.使用内存文件映射, window是CreateFileMapping,MapViewOfFile,UnmapViewOfFile,CloseHandle;linux是用mmap,munmap,msync,free。
ri_aje 2016-07-22
  • 打赏
  • 举报
回复
fread 一个一个的肯定慢,在内存允许的条件下尽量开大写,然后 fread 一次填满 buffer。
paschen 版主 2016-07-21
  • 打赏
  • 举报
回复
使用操作系统提供的API,文件映射内存
躺着睡的蜗牛 2016-07-21
  • 打赏
  • 举报
回复
1. 不要一个字节一个字节读,开个2M缓存,每次读2M的数据,比较完后,再读下个2M的数据 根据以前做一工具的经验,2M缓存对速度的提高比较明显,再大好像就不明显了。 2. KMP对应无规律的匹配字符串好像效果不太好, 可以选择其它更高效的匹配算法。
mLee79 2016-07-21
  • 打赏
  • 举报
回复
一次读 64K - 4M 左右就够了, 我测试搜索 100把条正则表达式构成的一组模式 的速度大约是 200M/s, 你这搜索单条确定的模式不应该比这慢.

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧