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


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

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

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

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

    1. 预期需求

    1.1 调用方法

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

    包含至多一个头文件。

    只需要简单的初始化。

    在1行内完成记录。

    线程安全。

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

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

    1.2 理想记录内容

    记录要包括:

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

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

    精确到毫秒的时间。

    我们的报告应该类似:

    2. 设计样例

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

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

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

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

    就可以了。

    3.具体实现

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

    3.1 实现代码

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

    1. 1 /**
    2. 2 Class profile_log is a lite tool-kit for millsec multi-thread profile log.
    3. 3 @author goldenhawking@163.com
    4. 4 @date 2019-05-14
    5. 5 */
    6. 6 #ifndef PROFILE_LOG_H
    7. 7 #define PROFILE_LOG_H
    8. 8 #include <QDir>
    9. 9 #include <QFile>
    10. 10 #include <QIODevice>
    11. 11 #include <QTextStream>
    12. 12 #include <QThread>
    13. 13 #include <QString>
    14. 14 #include <QDateTime>
    15. 15 #include <QMutex>
    16. 16 #include <QUuid>
    17. 17 #include <QCoreApplication>
    18. 18 #include <memory>
    19. 19
    20. 20 #define LOG_PROFILE(SUBJECT,DETAILED) profile_log::log(\
    21. 21 SUBJECT,DETAILED,__FILE__,__LINE__,__FUNCTION__)
    22. 22
    23. 23 /*!
    24. 24 * \brief The profile_log class is a tool-class for lite profile log.
    25. 25 * We can use this tool-kit simply by 3 steps:
    26. 26 * 1. include profile_log.h
    27. 27 * 2. Call profile_log::init at the very beginning of your program.
    28. 28 * 3. Call LOG_PROFILE(Subject, Detailed) anywhere you want.
    29. 29 */
    30. 30 class profile_log{
    31. 31 public:
    32. 32 static inline bool init()
    33. 33 {
    34. 34 if (instance().get()!=nullptr)
    35. 35 return false;
    36. 36 instance() = std::shared_ptr<profile_log>(new profile_log());
    37. 37 return instance()->write_title();
    38. 38 }
    39. 39 static inline bool init(const QString & filename)
    40. 40 {
    41. 41 if (instance().get()!=nullptr)
    42. 42 return false;
    43. 43 instance() = std::shared_ptr<profile_log>(new profile_log(filename));
    44. 44 return instance()->write_title();
    45. 45 }
    46. 46 static inline bool init(QIODevice * dev)
    47. 47 {
    48. 48 if (instance().get()!=nullptr)
    49. 49 return false;
    50. 50 instance() = std::shared_ptr<profile_log>(new profile_log(dev));
    51. 51 return instance()->write_title();
    52. 52 }
    53. 53 static inline std::shared_ptr<profile_log> & instance()
    54. 54 {
    55. 55 static std::shared_ptr<profile_log> plog;
    56. 56 return plog;
    57. 57 }
    58. 58 static inline bool log_state()
    59. 59 {
    60. 60 if (!instance().get()) return false;
    61. 61 return instance()->m_bLogOn;
    62. 62 }
    63. 63 static inline bool set_log_state(bool s)
    64. 64 {
    65. 65 if (!instance().get()) return false;
    66. 66 return instance()->m_bLogOn = s;
    67. 67 }
    68. 68 static QString url(){
    69. 69 if (!instance().get()) return "";
    70. 70 return instance()->m_url;
    71. 71 }
    72. 72 protected:
    73. 73 profile_log(){
    74. 74 //m_url = QDir::tempPath()+"/"+QUuid::createUuid().toString()+".csv";
    75. 75 m_url = QCoreApplication::applicationDirPath()+"/log/";
    76. 76 QDir dir;
    77. 77 dir.mkpath(m_url);
    78. 78 m_url += "/" + QUuid::createUuid().toString()+".csv";
    79. 79 QFile * fp = new QFile(m_url);
    80. 80 if(fp->open(QIODevice::WriteOnly))
    81. 81 {
    82. 82 m_pDev = fp;
    83. 83 m_bOwnDev = true;
    84. 84 }
    85. 85 }
    86. 86 profile_log(const QString & filename){
    87. 87 QFile * fp = new QFile(filename);
    88. 88 if(fp->open(QIODevice::WriteOnly))
    89. 89 {
    90. 90 m_pDev = fp;
    91. 91 m_bOwnDev = true;
    92. 92 m_url = filename;
    93. 93 }
    94. 94 }
    95. 95 profile_log(QIODevice * dev){
    96. 96 m_pDev = dev;
    97. 97 QFileDevice * fp = qobject_cast<QFileDevice *>(dev);
    98. 98 if (fp)
    99. 99 m_url = fp->fileName();
    100. 100 m_bOwnDev = false;
    101. 101 }
    102. 102 public:
    103. 103 ~profile_log()
    104. 104 {
    105. 105 if (m_bOwnDev && m_pDev)
    106. 106 {
    107. 107 if (m_pDev->isOpen())
    108. 108 m_pDev->close();
    109. 109 m_pDev->deleteLater();
    110. 110 }
    111. 111 if (!m_bLogOn)
    112. 112 if (m_url.length())
    113. 113 QFile::remove(m_url);
    114. 114
    115. 115
    116. 116 }
    117. 117 static inline bool write_title()
    118. 118 {
    119. 119 if (!instance().get()) return false;
    120. 120 if (instance()->log_state()==false)
    121. 121 return true;
    122. 122 instance()->m_mutex.lock();
    123. 123 QTextStream st_out(instance()->m_pDev);
    124. 124 st_out<<"Subject,Detailed,FileName,LineNum,FunctionName,Thread,UTC,Clock\n";
    125. 125 st_out.flush();
    126. 126 instance()->m_mutex.unlock();
    127. 127 return true;
    128. 128 }
    129. 129 static inline bool log(const QString & subject, const QString & detailed,
    130. 130 const QString & filename,
    131. 131 const int linenum,
    132. 132 const QString & funcname)
    133. 133 {
    134. 134 if (!instance()->m_pDev) return false;
    135. 135 if (instance()->log_state()==false)
    136. 136 return true;
    137. 137 instance()->m_mutex.lock();
    138. 138 QTextStream st_out(instance()->m_pDev);
    139. 139 st_out << "\"" << subject <<"\"";
    140. 140 st_out << ",\"" << detailed <<"\"";
    141. 141 st_out << ",\"" << filename <<"\"";
    142. 142 st_out << ",\"" << linenum <<"\"";
    143. 143 st_out << ",\"" << funcname <<"\"";
    144. 144 st_out << ",\"" << QThread::currentThreadId() <<"\"";
    145. 145 st_out << ",\"" << QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH:mm:ss.zzz") <<"\"";
    146. 146 st_out << ",\"" << clock() <<"\"\n";
    147. 147 instance()->m_mutex.unlock();
    148. 148 return true;
    149. 149 }
    150. 150 private:
    151. 151 QIODevice * m_pDev = nullptr;
    152. 152 QString m_url;
    153. 153 bool m_bOwnDev = false;
    154. 154 bool m_bLogOn = true;
    155. 155 QMutex m_mutex;
    156. 156 };
    157. 157 #endif // PROFILE_LOG_H

    3.2 设计要点

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

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

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

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

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

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

  • 相关阅读:
    哈希(哈希散列数据结构)---底层原理
    java驾校预约
    【无标题】
    Mysql集群及高可用-主从工作原理3
    4.1 探索LyScript漏洞挖掘插件
    LeetCode第100题—相同的树
    ubuntu中用docker部署jenkins,并和码云实现自动化部署
    转换张量形状:`nlc_to_nchw` 函数详解
    2022年6月PMP考试,大家觉得考的怎么样?我3A通过了
    golang 结构体标签 -- struct tag (八)
  • 原文地址:https://blog.csdn.net/m0_73443478/article/details/127807917