1 背景
文件读写(本文只说明读操作,写操作类同)涉及到I/O操作,而频繁的I/O操作会给系统增加负担,导致性能变差。 对于文本文件(text stream),通常用读取行的形式进行解析,这在文件较小时(几十MB左右)性能瓶颈不明显;但对于大文件(百MB~几十GB)的解析时,性能会急剧下降,延时卡顿显著。 基于上述现象,可对大文件采用内存映射和并发读的方式进行性能优化处理。
2 方案设计
功能包含内存映射和并发读文件的实现。 ### 2.1 内存映射 如下图所示,内存、磁盘的访问速度相差巨大。读写文件就是一次对磁盘的访问(简称I/O),而频繁的磁盘访问势必带来性能问题。
定义:内存映射(memory mapping)是一种将文件或设备映射到进程的虚拟地址空间的技术,使得文件内容可以像内存中的数组一样访问。
实现:采用Qt库的QFile对象,如下所示的伪代码块:
// 打开文件
QFile file;
file.open();
// 映射整个文件内容(返回块地址)
uchar* mappedData = file.map(0, file.size());
file.close();
// 访问mappedData开始,size大小的文件块内容
QByteArray byteArray = QByteArray::fromRawData(reinterpret_cast<char*>(mappedData), size);
// 使用文本流逐行解析
QTextStream in(&byteArray);
QString line;
while(!in.atEnd() && in.readLineInto(&line)){
// 解析行文本
......
}
file.unmap(mappedData);
2.2 并发读文件
在上述内存映射的基础上,可对文件分块读取,然后对不同的块并发操作,进一步提升读文件性能。具体实现流程如下图所示:
- 查找关键字段:并发搜索起始、结束标志(强业务相关);
- 文件分块:计算最佳块大小,调整块的结束位置(完整行结束);
- 并发处理块:根据分块,创建任务加入线程池,并发执行。
3 附录
分块读也可以使用QFile的read接口实现,以下是其与map接口的对比结果。