文件IO操作开发笔记(一):使用Qt的QFile对磁盘文件存储进行性能测试以及测试工具
前言
在做到个别项目对日志要求较高,要求并行写入的数据较多,尽管写入数据的线程放在子线程,仍然会造成界面程序的假死(实际上Qt还是在跑,只是磁盘消耗超过瓶颈,造成假死(注意:控制台还能看到打印输出,linux则能看到打印输出)。
本篇开发了测试工具,并且测试了QFile在USB3.0和M.2SSD上的写入性能。
补充
在海思Hi3559AV100,Hi3516DV300以及海思的开发过程中,也发现Qt会假死,后台仍然在继续打印,海思板上的Qt界面假死的原因并不是因为磁盘性能问题,可以解决但涉及到一些关键技术了,此处不提。
第一版本测试v1.0.0
日志的操作,多半写入都是几十上百字节一条,特殊的项目要求写入不同的文件,分类保存,于是产出了第一版本的,用于测试Qt的。

关于对于“文件打开次数属性”的忽略
理论上也可以忽略,测试跟理论结果一致,因为本身程序的文件打开次数,是新建一个然后写入操作完成后关闭,然后另外新建一个继续重复操作,是流水线排序的,所以这个对单线程写入影响不大。
因为测试是获取了系统时间,次数少了测不出,次数多了越来越小,偶尔增大,所以可以判断,主要影响时间的还是QDateTime获取时间,然后计算的过程。



选取1000次作为标准,测试文件打开次数的影响:


打开次数基本无影响,但是一次测试可以利用这个来一次性测多次每个文件单独写入的耗时。
使用QFile测试结果


太小了看不出:

修改程序至v1.0.1版本,只看最终结果(为了模拟日志多线程写入不同文件),下面开始测试。
USB3.0移动硬盘测试结果


所以,线程越开越多,在某一个阈值线程数(实际打开操作的文件数)会导致性能大幅下降,而且会持续有多个阈值类似的。
M.2主板上SSD测试结果


使用QFile(每次写后用flush)测试结果
USB3.0移动硬盘测试结果


M.2主板上SSD测试结果


结论:这个明显收到硬盘数据传输的影响。
关键源码
void FileIoTestManager::slot_optFileUseQtQFile(int loopTime, int loopWrite, int dataSize, bool flush)
{
QDir dir;
QString dirPath = QString("%1/%2")
.arg(QApplication::applicationDirPath())
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh_mm_ss_zzz"));
if(dir.mkpath(dirPath))
{
message(QString("创建文件夹成功: %1").arg(dirPath));
}else{
message(QString("创建文件夹失败: %1").arg(dirPath));
}
// 生成数据
message(QString("生成测试数据,数据长度: %1").arg(dataSize));
QByteArray byteArray;
byteArray.append(dataSize, 0xFF);
message(QString("==========================测试开始=============================="));
double totalTime = 0; // 总计时间
double fileTotalTime = 0; // 操作单个文件总时间
double writeFileTime = 0; // 单个文件单词写入时间
totalTime = QDateTime::currentDateTime().toMSecsSinceEpoch() * 1.0f;
for(int loopIndex = 0; loopIndex < loopTime; loopIndex++)
{
QString filePath = QString("%1/%2_%3")
.arg(dirPath)
.arg(QDateTime::currentDateTime().toString("hh_mm_ss_zzz"))
.arg(loopIndex, 6, 10, QChar('0'));
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
message(QString(" 第%1次创建文件失败").arg(loopIndex + 1));
continue;
}
writeFileTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
for(int writeIndex = 0; writeIndex < loopWrite; writeIndex++)
{
// message(QString(" 第%1次写入文件,写入长度%2字节").arg(writeIndex + 1).arg(dataSize));
int size = 0;
while(size < byteArray.size())
{
int len = file.write(byteArray.mid(size));
if(len < 0)
{
message(QString(" 第%1次写入文件,写入失败").arg(writeIndex + 1));
message(QString("==========================测试失败=============================="));
break;
}
// message(QString(" 第%1次写入文件,写入成功").arg(writeIndex + 1));
if(flush)
{
file.flush();
}
size += len;
if(_stop)
{
file.close();
message(QString("==========================测试手动停止==========================="));
_stop = false;
emit signal_finished();
return;
}
}
if(_stop)
{
file.close();
message(QString("==========================测试手动停止==========================="));
_stop = false;
emit signal_finished();
return;
}
}
writeFileTime = QDateTime::currentDateTime().toMSecsSinceEpoch() - writeFileTime;
writeFileTime = writeFileTime / loopWrite;
message(QString("每次写入数据平均耗时(不包含打开关闭文件): %1ms").arg(writeFileTime));
// message(QString(" 第%1次关闭文件").arg(loopIndex + 1));
file.close();
}
message(QString("==========================测试结果=============================="));
totalTime = QDateTime::currentDateTime().toMSecsSinceEpoch() - totalTime;
fileTotalTime = totalTime * 1.0f / loopTime;
message(QString("操作创建文件次数: %1, 单个文件循环写入次数: %2, 每次写入固定数据长度: %3, %4")
.arg(loopTime)
.arg(loopWrite)
.arg(dataSize)
.arg(flush ? "每次使用flush" : "不使用flush"));
message(QString("总耗时: %1ms").arg(totalTime));
message(QString("单个文件循环写入平均总耗时(包括打开关闭文件): %1ms").arg(fileTotalTime));
message(QString("每次写入数据平均耗时(包括打开关闭文件: %1ms").arg(fileTotalTime * 1.0f / loopWrite));
message(QString("==========================测试结束=============================="));
emit signal_finished();
return;
}
工程模板

后续
会持续补充测试其他方式,QFile的性能本身并不高。
文件IO操作开发笔记(一):使用Qt的QFile对磁盘文件存储进行性能测试以及测试工具的更多相关文章
- 树莓派学习笔记——使用文件IO操作GPIO SysFs方式
0 前言 本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...
- 文件IO操作
前言 本文介绍使用java进行简单的文件IO操作. 操作步骤 - 读文件 1. 定义一个Scanner对象 2. 调用该对象的input函数族进行文件读取 (参见下面代码) 3. 关闭输入流 说明:其 ...
- Linux学习记录--文件IO操作相关系统编程
文件IO操作相关系统编程 这里主要说两套IO操作接口,各自是: POSIX标准 read|write接口.函数定义在#include<unistd.h> ISO C标准 fread|fwr ...
- linux文件IO操作篇 (一) 非缓冲文件
文件IO操作分为 2 种 非缓冲文件IO 和 缓冲文件IO 它们的接口区别是 非缓冲 open() close() read() write() 缓冲 fopen() fclose() fread() ...
- imx6用文件io操作gpio
具体请参考: http://blog.csdn.net/u014213012/article/details/53140781 这里要注意的是: 要让linux支持文件io方式操作gpio,首先驱动必 ...
- 9.2 Go 文件IO操作
9.2 Go 文件IO操作 1.1.1. bufio包 带有缓冲区的IO读写操作,用于读写文件,以及读取键盘输入 func main() { //NewReader返回一个结构体对象指针 reader ...
- Linux文件IO操作
来源:微信公众号「编程学习基地」 目录 文件操作 Linux文件类型 Linux文件权限 修改文件权限 Linux error 获取系统调用时的错误描述 打印错误信息 系统IO函数 open/clos ...
- Java 文件 IO 操作
window 路径分割符: \ 表示 windows 系统文件目录分割符 java 代码在 windows 下写某个文件的话需要下面的方式 D:\\soft\\sdclass.txt 其中一个单斜杠 ...
- 文件IO操作..修改文件的只读属性
文件的IO操作..很多同行的IO工具类都是直接写..但是如果文件有只读属性的话..则会写入失败..所以附加了一个只读的判断和修改.. 代码如下: /// <summary> /// 创建文 ...
- Linux系统编程--文件IO操作
Linux思想即,Linux系统下一切皆文件. 一.对文件操作的几个函数 1.打开文件open函数 int open(const char *path, int oflags); int open(c ...
随机推荐
- [转帖]Skywalking学习及整合springboot
目录 1. Skywalking概述 2. Skywalking主要功能 3. Skywalking主要特性 4. Skywalking架构简介 5. Spring Cloud与Skywalking实 ...
- [转帖]iptables 执行清除命令 iptables -F 要非常小心的
使用 /sbin/iptables -F 要小心,搞不好,你就马上同服务器断开连接了 以下是来自 http://wiki.ubuntu.org.cn/IptablesHowTo 上的说明 可以通过/s ...
- [转帖]oracle如何删除datafile,误删除Oracle datafile 无法打开数据库
1.在Oracle open方式下,直接从OS上删除了datafile文件. rm /u02/rmants.dbf 2.数据库关闭后,无法打开数据库,只能到mount状态. SQL> alter ...
- Redis-rdb-tools与rdr工具学习与使用
Redis-rdb-tools与rdr工具学习与使用 简要说明 rdb工具是python写的一套工具,可以分析dump文件,获取key等信息. rdb其实有一套rdb-profiler工具, 能够导出 ...
- 什么是根号?什么是 log ?
生日悖论是 \(O(\sqrt{n})\) 随机序列 LIS 是 \(O(\sqrt{n})\) 随机 \(\pm1\) 序列前缀和最大绝对值是 \(O(\sqrt{n})\) 证明 随机 Prufe ...
- web字体小于12px的解决办法
大家都知道,web端的字体在正常情况下,最小只能够是12px; 但是有些时候,可能需要字体小于12px 那么如何解决这个办法了 可以使用css3的缩放属性scale 如果字体的大小是10px; 那么我 ...
- Python 潮流周刊#22:Python 3.12.0 发布了!!
你好,我是猫哥.这里每周分享优质的 Python.AI 及通用技术内容,大部分为英文.标题取自其中一则分享,不代表全部内容都是该主题,特此声明. 本周刊由 Python猫 出品,精心筛选国内外的 25 ...
- C/C++ 提权与强制卸载DLL
权限提升 #include <Windows.h> #include <stdio.h> BOOL SetPrivilege(LPCTSTR lpszPrivilege, BO ...
- JavaScript快速入门(一)
JavaScript快速入门(二) 语句 只需简单地把各条语句放在不同的行上就可以分隔它们 var a = 1 var b = 2 如果想把多条语句放在同一行上,就需要用分号隔开 var a = 1; ...
- 【链表】【python】力扣24. 两两交换链表中的节点【超详细的注释和解释】
目录 说在前面的话 前言 一.题目(力扣24. 两两交换链表中的节点) 二.题目分析 实现完整代码(Python实现) 总结 说在前面的话 博主也好长一段时间没有更新力扣的刷题系列了,今天给大家带来一 ...