在做比较耗费计算资源或者存储资源的多线程程序时,往往需要分析每个环节耗费了多少时间。使用valgrind系列工具,在Linux下可以来做类似的工作,但是我们还是希望在所有平台下,以及最终发行
Release版本中(优化开关全开)完成评估。
实际上,只要能够有一个工具方便的记录每个关键位置的时刻,即可使用后期分析来计算每一步的成本。
1.1 调用方法
按照轻量级、简单的需要,我们要求:
包含至多一个头文件。
只需要简单的初始化。
在1行内完成记录。
线程安全。
可以按需关闭或开启记录。
输出为CSV格式,以便直接用WPS打开。
1.2 理想记录内容
记录要包括:
一个用户自定义的专题名,用于后续处理时的排序、分类。
文件名、行号、函数名、线程ID
精确到毫秒的时间。
我们的报告应该类似:
我们使用一个Qt控制台程序,进行理想的日志操作测试。
- 1 //main.cpp
- 2 #include <QCoreApplication>
- 3 #include <QThread>
- 4 #include "profile_log.h"
- 5 void foo()
- 6 {
- 7 //一行完成标记
- 8 LOG_PROFILE("FOO deal","Start (100 times)");
- 9 #pragma omp parallel for
- 10 for (int i=0;i<100;++i)
- 11 {
- 12 //模拟多线程耗时操作
- 13 QThread::msleep(rand()%20+40);
- 14 LOG_PROFILE("FOO deal",QString("T%1").arg(i));
- 15 }
- 16 LOG_PROFILE("FOO deal","Finished ");
- 17 }
- 18
- 19 int main(int argc, char *argv[])
- 20 {
- 21 QCoreApplication a(argc, argv);
- 22 //直接初始化,使用UUID的唯一文件名,在当前路径下的log创建。
- 23 profile_log::init();
- 24 //开启日志。在发布时,可以设为false。
- 25 profile_log::set_log_state(true);
- 26 //测试
- 27 foo();
- 28 return 0;
- 29 }
编译上述程序,需要开启openMP多线程并行,以便更好的测试线程。在VC下要在proj属性的语言特性页面设置,在Linux/GCC下,直接在Qt的工程文件中加入开关:
- 1 QMAKE_CXXFLAGS += -fopenmp
- 2 LIBS += -lgomp
就可以了。
在Qt/C++下,可以仅用一个内联类完成上述功能。
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 st_out.flush();
- 126 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 st_out << ",\"" << detailed <<"\"";
- 141 st_out << ",\"" << filename <<"\"";
- 142 st_out << ",\"" << linenum <<"\"";
- 143 st_out << ",\"" << funcname <<"\"";
- 144 st_out << ",\"" << QThread::currentThreadId() <<"\"";
- 145 st_out << ",\"" << QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH:mm:ss.zzz") <<"\"";
- 146 st_out << ",\"" << clock() <<"\"\n";
- 147 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
3.2 设计要点
上述代码有几个设计要点:
1.使用全局唯一实例。构造函数为保护,不允许直接创建实例。 只能靠静态函数创建唯一实例。
2.用宏简化操作。设计一个宏,以便用最短的代码进行日志标记。
3.支持创建临时文件、从已经打开的QIODevice创建,以及给定文件名创建。特别是QIODevice创建,将可以把内容直接输出到网络等部位,而非落盘。
4.局限:可以去除 QDateTime,以便减少时间消耗。
本文福利, 免费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓