文件IO操作开发笔记(一):使用Qt的QFile对磁盘文件存储进行性能测试以及测试工具

本文涉及的产品
性能测试 PTS,5000VUM额度
简介: 在做到个别项目对日志要求较高,要求并行写入的数据较多,尽管写入数据的线程放在子线程,仍然会造成界面程序的假死(实际上Qt还是在跑,只是磁盘消耗超过瓶颈,造成假死(注意:控制台还能看到打印输出,linux则能看到打印输出)。  本篇开发了测试工具,并且测试了QFile在USB3.0和M.2SSD上的写入性能。

前言

  在做到个别项目对日志要求较高,要求并行写入的数据较多,尽管写入数据的线程放在子线程,仍然会造成界面程序的假死(实际上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的性能本身并不高。

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
2月前
|
缓存 运维 数据库
【测试人员兼职指南】利用专业技能:如何从测试转向开发赚钱
本文分享了作者作为测试人员如何利用专业技能转向开发来兼职赚钱的经验,包括分析和解决登录页面跳转、避免重复账号注册、用户登录后首页显示用户名以及添加退出功能等问题,并提供了Django项目中使用sqlite3数据库和后台管理的扩展技巧。
47 1
【测试人员兼职指南】利用专业技能:如何从测试转向开发赚钱
|
11天前
|
测试技术 持续交付 UED
软件测试的艺术与科学:平衡创新与质量的探索在软件开发的波澜壮阔中,软件测试如同灯塔,指引着产品质量的方向。本文旨在深入探讨软件测试的核心价值,通过分析其在现代软件工程中的应用,揭示其背后的艺术性与科学性,并探讨如何在追求技术创新的同时确保产品的高质量标准。
软件测试不仅仅是技术活动,它融合了创造力和方法论,是软件开发过程中不可或缺的一环。本文首先概述了软件测试的重要性及其在项目生命周期中的角色,随后详细讨论了测试用例设计的创新方法、自动化测试的策略与挑战,以及如何通过持续集成/持续部署(CI/CD)流程优化产品质量。最后,文章强调了团队间沟通在确保测试有效性中的关键作用,并通过案例分析展示了这些原则在实践中的应用。
29 1
|
9天前
|
测试技术 UED 开发者
软件测试的艺术:从代码审查到用户反馈的全景探索在软件开发的宇宙中,测试是那颗确保星系正常运转的暗物质。它或许不总是站在聚光灯下,但无疑是支撑整个系统稳定性与可靠性的基石。《软件测试的艺术:从代码审查到用户反馈的全景探索》一文,旨在揭开软件测试这一神秘面纱,通过深入浅出的方式,引领读者穿梭于测试的各个环节,从细微处着眼,至宏观视角俯瞰,全方位解析如何打造无懈可击的软件产品。
本文以“软件测试的艺术”为核心,创新性地将技术深度与通俗易懂的语言风格相结合,绘制了一幅从代码审查到用户反馈全过程的测试蓝图。不同于常规摘要的枯燥概述,这里更像是一段旅程的预告片,承诺带领读者经历一场从微观世界到宏观视野的探索之旅,揭示每一个测试环节背后的哲学与实践智慧,让即便是非专业人士也能领略到软件测试的魅力所在,并从中获取实用的启示。
|
2月前
|
测试技术 C# 开发者
“代码守护者:详解WPF开发中的单元测试策略与实践——从选择测试框架到编写模拟对象,全方位保障你的应用程序质量”
【8月更文挑战第31天】单元测试是确保软件质量的关键实践,尤其在复杂的WPF应用中更为重要。通过为每个小模块编写独立测试用例,可以验证代码的功能正确性并在早期发现错误。本文将介绍如何在WPF项目中引入单元测试,并通过具体示例演示其实施过程。首先选择合适的测试框架如NUnit或xUnit.net,并利用Moq模拟框架隔离外部依赖。接着,通过一个简单的WPF应用程序示例,展示如何模拟`IUserRepository`接口并验证`MainViewModel`加载用户数据的正确性。这有助于确保代码质量和未来的重构与扩展。
30 0
|
27天前
|
测试技术 数据库 UED
Python 性能测试进阶之路:JMeter 与 Locust 的强强联合,解锁性能极限
【9月更文挑战第9天】在数字化时代,确保软件系统在高并发场景下的稳定性至关重要。Python 为此提供了丰富的性能测试工具,如 JMeter 和 Locust。JMeter 可模拟复杂请求场景,而 Locust 则能更灵活地模拟真实用户行为。结合两者优势,可全面评估系统性能并优化瓶颈。例如,在电商网站促销期间,通过 JMeter 模拟大量登录请求并用 Locust 模拟用户浏览和购物行为,可有效识别并解决性能问题,从而提升系统稳定性和用户体验。这种组合为性能测试开辟了新道路,助力应对复杂挑战。
51 2
|
2月前
|
测试技术 持续交付 Apache
深度挖掘:Python性能测试中JMeter与Locust的隐藏技能🔍
【8月更文挑战第5天】随着软件规模扩大,性能测试对系统稳定性至关重要。Apache JMeter和Locust是两大主流工具,各有千秋。本文探索它们在Python环境下的进阶用法,挖掘更多性能测试潜力。JMeter功能强大,支持多种协议,可通过命令行模式执行复杂测试计划,并与Python集成实现动态测试数据生成。Locust基于Python,通过编写简洁脚本模拟HTTP请求,支持自定义请求及与Python库深度集成。掌握这些技巧可实现高度定制化测试场景,有效识别性能瓶颈,提升应用稳定性。
115 1
|
2月前
|
缓存 测试技术 Apache
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
【8月更文挑战第5天】性能测试确保应用高负载下稳定运行。Apache JMeter与Locust是两大利器,助力识别解决性能瓶颈。本文介绍这两款工具的应用与优化技巧,并通过实战示例展示性能测试流程。首先,通过JMeter测试静态与动态资源;接着,利用Locust的Python脚本模拟HTTP请求。文中提供安装指南、命令行运行示例与性能优化建议,帮助读者掌握性能测试核心技能。
94 0
|
2月前
|
消息中间件 Java 测试技术
Python性能测试全攻略:JMeter与Locust,双剑合璧斩断性能瓶颈🗡️
【8月更文挑战第4天】在软件开发中,性能至关重要。对Python开发者来说,掌握高效性能测试方法尤为关键。本文将带您探索性能测试工具JMeter与Locust的强大功能。JMeter作为Java世界的巨擘,以其强大功能和灵活性在性能测试领域占有一席之地,不仅适用于Java应用,也能测试Python Web服务。
86 0
|
22天前
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
|
26天前
|
测试技术 持续交付 Apache
Python性能测试新风尚:JMeter遇上Locust,性能分析不再难🧐
【9月更文挑战第10天】随着软件应用的不断扩展,性能测试成为确保系统稳定运行的关键环节。本文通过对比Apache JMeter和Locust,探讨了如何在Python环境中利用这两款工具挖掘更多性能测试潜力。JMeter是一款成熟且功能强大的开源工具,支持多种协议,适用于各种应用的测试;而Locust则基于Python,通过简单脚本模拟HTTP请求,更适合Web应用测试。
59 2
下一篇
无影云桌面