zoukankan      html  css  js  c++  java
  • Qt/C++ 加入轻便性能收集器

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

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

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

    1. 预期需求

    1.1 调用方法

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

    包含至多一个头文件。

    只需要简单的初始化。

    在1行内完成记录。

    线程安全。

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

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

    1.2 理想记录内容

    记录要包括:

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

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

    精确到毫秒的时间。

    我们的报告应该类似:

    2. 设计样例

    我们使用一个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

    就可以了。

    3.具体实现

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

    3.1 实现代码

      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      * rief 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
    ";
    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() <<""
    ";
    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,以便减少时间消耗。

  • 相关阅读:
    Android ImageView设置图片原理(下)
    C++ 虚函数表 多重继承
    C++ 虚函数表 单继承
    私有继承
    内联函数和宏定义的区别
    #pragma pack(x) CPU对齐
    static 变量(静态变量)
    C++ 中const作用
    如何连接宏参数
    几种常见容器比较和分析 hashmap, map, vector, list ...hash table
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/12654467.html
Copyright © 2011-2022 走看看