Qt/C++ 加入轻便性能收集器

在做比较耗费计算资源或者存储资源的多线程程序时,往往需要分析每个环节耗费了多少时间。使用valgrind系列工具,在Linux下可以来做类似的工作,但是我们还是希望在所有平台下,以及最终发行

Release版本中(优化开关全开)完成评估。

实际上,只要能够有一个工具方便的记录每个关键位置的时刻,即可使用后期分析来计算每一步的成本。

  1. 预期需求

1.1 调用方法

按照轻量级、简单的需要,我们要求:

包含至多一个头文件。

只需要简单的初始化。

在1行内完成记录。

线程安全。

可以按需关闭或开启记录。

输出为CSV格式,以便直接用WPS打开。

1.2 理想记录内容

记录要包括:

一个用户自定义的专题名,用于后续处理时的排序、分类。

文件名、行号、函数名、线程ID

精确到毫秒的时间。

我们的报告应该类似:

Qt/C++ 加入轻便性能收集器
  1. 设计样例

我们使用一个Qt控制台程序,进行理想的日志操作测试。

 1 //main.cpp
 2 #include <qcoreapplication>
 3 #include <qthread>
 4 #include "profile_log.h"
 5 void foo()
 6 {
 7     //&#x4E00;&#x884C;&#x5B8C;&#x6210;&#x6807;&#x8BB0;
 8     LOG_PROFILE("FOO deal","Start (100 times)");
 9 #pragma omp parallel for
10     for (int i=0;i<100;++i) 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 { 模拟多线程耗时操作 qthread::msleep(rand()%20+40); log_profile("foo deal",qstring("t%1").arg(i)); } deal","finished "); int main(int argc, char *argv[]) qcoreapplication a(argc, argv); 直接初始化,使用uuid的唯一文件名,在当前路径下的log创建。 profile_log::init(); 开启日志。在发布时,可以设为false。 profile_log::set_log_state(true); 测试 foo(); return 0; }< code></100;++i)></qthread></qcoreapplication>

编译上述程序,需要开启openMP多线程并行,以便更好的测试线程。在VC下要在proj属性的语言特性页面设置,在Linux/GCC下,直接在Qt的工程文件中加入开关:

1 QMAKE_CXXFLAGS += -fopenmp
2 LIBS += -lgomp

就可以了。

3.具体实现

在Qt/C++下,可以仅用一个内联类完成上述功能。

3.1 实现代码

CSDN QT开发学习路线推荐:Qt开发必备技术栈学习路线和资料

  1 /**
  2   Class profile_log is a lite tool-kit for millsec multi-thread profile log.

  3   @author goldenhawking@163.com
  4   @date 2019-05-14
  5   */
  6 #ifndef PROFILE_LOG_H
  7 #define PROFILE_LOG_H
  8 #include <qdir>
  9 #include <qfile>
 10 #include <qiodevice>
 11 #include <qtextstream>
 12 #include <qthread>
 13 #include <qstring>
 14 #include <qdatetime>
 15 #include <qmutex>
 16 #include <quuid>
 17 #include <qcoreapplication>
 18 #include <memory>
 19
 20 #define LOG_PROFILE(SUBJECT,DETAILED) profile_log::log(\
 21     SUBJECT,DETAILED,__FILE__,__LINE__,__FUNCTION__)
 22
 23 /*!

 24      * \brief The profile_log class is a tool-class for lite profile log.

 25      * We can use this tool-kit simply by 3 steps:
 26      * 1. include profile_log.h
 27      * 2. Call profile_log::init at the very beginning of your program.

 28      * 3. Call LOG_PROFILE(Subject, Detailed) anywhere you want.

 29      */
 30 class profile_log{
 31 public:
 32     static inline bool init()
 33     {
 34         if (instance().get()!=nullptr)
 35             return false;
 36         instance() = std::shared_ptr<profile_log>(new profile_log());
 37         return instance()->write_title();
 38     }
 39     static inline bool init(const QString & filename)
 40     {
 41         if (instance().get()!=nullptr)
 42             return false;
 43         instance() = std::shared_ptr<profile_log>(new profile_log(filename));
 44         return instance()->write_title();
 45     }
 46     static inline bool init(QIODevice * dev)
 47     {
 48         if (instance().get()!=nullptr)
 49             return false;
 50         instance() = std::shared_ptr<profile_log>(new profile_log(dev));
 51         return instance()->write_title();
 52     }
 53     static inline std::shared_ptr<profile_log> & instance()
 54     {
 55         static std::shared_ptr<profile_log> plog;
 56         return plog;
 57     }
 58     static inline bool log_state()
 59     {
 60         if (!instance().get()) return false;
 61         return instance()->m_bLogOn;
 62     }
 63     static inline bool set_log_state(bool s)
 64     {
 65         if (!instance().get()) return false;
 66         return instance()->m_bLogOn = s;
 67     }
 68     static QString url(){
 69         if (!instance().get()) return "";
 70         return instance()->m_url;
 71     }
 72 protected:
 73     profile_log(){
 74         //m_url = QDir::tempPath()+"/"+QUuid::createUuid().toString()+".csv";
 75         m_url = QCoreApplication::applicationDirPath()+"/log/";
 76         QDir dir;
 77         dir.mkpath(m_url);
 78         m_url += "/" + QUuid::createUuid().toString()+".csv";
 79         QFile * fp  = new QFile(m_url);
 80         if(fp->open(QIODevice::WriteOnly))
 81         {
 82             m_pDev = fp;
 83             m_bOwnDev = true;
 84         }
 85     }
 86     profile_log(const QString & filename){
 87         QFile * fp  = new QFile(filename);
 88         if(fp->open(QIODevice::WriteOnly))
 89         {
 90             m_pDev = fp;
 91             m_bOwnDev = true;
 92             m_url = filename;
 93         }
 94     }
 95     profile_log(QIODevice * dev){
 96         m_pDev = dev;
 97         QFileDevice * fp = qobject_cast<qfiledevice *>(dev);
 98         if (fp)
 99             m_url = fp->fileName();
100         m_bOwnDev = false;
101     }
102 public:
103     ~profile_log()
104     {
105         if (m_bOwnDev && m_pDev)
106         {
107             if (m_pDev->isOpen())
108                 m_pDev->close();
109             m_pDev->deleteLater();
110         }
111         if (!m_bLogOn)
112             if (m_url.length())
113                 QFile::remove(m_url);
114
115
116     }
117     static inline bool write_title()
118     {
119         if (!instance().get()) return false;
120         if (instance()->log_state()==false)
121             return true;
122         instance()->m_mutex.lock();
123         QTextStream st_out(instance()->m_pDev);
124         st_out<<"subject,detailed,filename,linenum,functionname,thread,utc,clock\n"; 125 126 st_out.flush(); instance()->m_mutex.unlock();
127         return true;
128     }
129     static inline bool log(const QString & subject, const QString & detailed,
130                            const QString & filename,
131                            const int linenum,
132                            const QString & funcname)
133     {
134         if (!instance()->m_pDev) return false;
135         if (instance()->log_state()==false)
136             return true;
137         instance()->m_mutex.lock();
138         QTextStream st_out(instance()->m_pDev);
139         st_out    <<    "\"" << subject <<"\""; 140 141 142 143 144 145 146 147 st_out << ",\"" detailed <<"\""; filename linenum funcname qthread::currentthreadid() qdatetime::currentdatetimeutc().tostring("yyyy-mm-ddthh:mm:ss.zzz") clock() <<"\"\n"; instance()->m_mutex.unlock();
148         return true;
149     }
150 private:
151     QIODevice * m_pDev = nullptr;
152     QString m_url;
153     bool    m_bOwnDev = false;
154     bool    m_bLogOn = true;
155     QMutex  m_mutex;
156 };
157 #endif // PROFILE_LOG_H</"\"";></"subject,detailed,filename,linenum,functionname,thread,utc,clock\n";></qfiledevice></profile_log></profile_log></profile_log></profile_log></profile_log></memory></qcoreapplication></quuid></qmutex></qdatetime></qstring></qthread></qtextstream></qiodevice></qfile></qdir>

3.2 设计要点

上述代码有几个设计要点:

1.使用全局唯一实例。构造函数为保护,不允许直接创建实例。 只能靠静态函数创建唯一实例。

2.用宏简化操作。设计一个宏,以便用最短的代码进行日志标记。

3.支持创建临时文件、从已经打开的QIODevice创建,以及给定文件名创建。特别是QIODevice创建,将可以把内容直接输出到网络等部位,而非落盘。

4.局限:可以去除 QDateTime,以便减少时间消耗。

本文福利, 免费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

Original: https://blog.csdn.net/m0_73443478/article/details/127807917
Author: 十年编程老舅
Title: Qt/C++ 加入轻便性能收集器

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/657638/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球