百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

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

bigegpt 2024-09-10 11:19 6 浏览

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

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

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

1. 预期需求

1.1 调用方法

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

包含至多一个头文件。

只需要简单的初始化。

在1行内完成记录。

线程安全。

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

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

【领更多QT学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

点击→Qt开发必备技术栈学习路线和资料

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      * \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,以便减少时间消耗。

相关推荐

Go语言泛型-泛型约束与实践(go1.7泛型)

来源:械说在Go语言中,Go泛型-泛型约束与实践部分主要探讨如何定义和使用泛型约束(Constraints),以及如何在实际开发中利用泛型进行更灵活的编程。以下是详细内容:一、什么是泛型约束?**泛型...

golang总结(golang实战教程)

基础部分Go语言有哪些优势?1简单易学:语法简洁,减少了代码的冗余。高效并发:内置强大的goroutine和channel,使并发编程更加高效且易于管理。内存管理:拥有自动垃圾回收机制,减少内...

Go 官宣:新版 Protobuf API(go pro版本)

原文作者:JoeTsai,DamienNeil和HerbieOng原文链接:https://blog.golang.org/a-new-go-api-for-protocol-buffer...

Golang开发的一些注意事项(一)(golang入门项目)

1.channel关闭后读的问题当channel关闭之后再去读取它,虽然不会引发panic,但会直接得到零值,而且ok的值为false。packagemainimport"...

golang 托盘菜单应用及打开系统默认浏览器

之前看到一个应用,用go语言编写,说是某某程序的windows图形化客户端,体验一下发现只是一个托盘,然后托盘菜单的控制面板功能直接打开本地浏览器访问程序启动的webserver网页完成gui相关功...

golang标准库每日一库之 io/ioutil

一、核心函数概览函数作用描述替代方案(Go1.16+)ioutil.ReadFile(filename)一次性读取整个文件内容(返回[]byte)os.ReadFileioutil.WriteFi...

文件类型更改器——GoLang 中的 CLI 工具

我是如何为一项琐碎的工作任务创建一个简单的工具的,你也可以上周我开始玩GoLang,它是一种由Google制作的类C编译语言,非常轻量和快速,事实上它经常在Techempower的基准测...

Go (Golang) 中的 Channels 简介(golang channel长度和容量)

这篇文章重点介绍Channels(通道)在Go中的工作方式,以及如何在代码中使用它们。在Go中,Channels是一种编程结构,它允许我们在代码的不同部分之间移动数据,通常来自不同的goro...

Golang引入泛型:Go将Interface「」替换为“Any”

现在Go将拥有泛型:Go将Interface{}替换为“Any”,这是一个类型别名:typeany=interface{}这会引入了泛型作好准备,实际上,带有泛型的Go1.18Beta...

一文带你看懂Golang最新特性(golang2.0特性)

作者:腾讯PCG代码委员会经过十余年的迭代,Go语言逐渐成为云计算时代主流的编程语言。下到云计算基础设施,上到微服务,越来越多的流行产品使用Go语言编写。可见其影响力已经非常强大。一、Go语言发展历史...

Go 每日一库之 java 转 go 遇到 Apollo?让 agollo 来平滑迁移

以下文章来源于GoOfficialBlog,作者GoOfficialBlogIntroductionagollo是Apollo的Golang客户端Apollo(阿波罗)是携程框架部门研...

Golang使用grpc详解(golang gcc)

gRPC是Google开源的一种高性能、跨语言的远程过程调用(RPC)框架,它使用ProtocolBuffers作为序列化工具,支持多种编程语言,如C++,Java,Python,Go等。gR...

Etcd服务注册与发现封装实现--golang

服务注册register.gopackageregisterimport("fmt""time"etcd3"github.com/cor...

Golang:将日志以Json格式输出到Kafka

在上一篇文章中我实现了一个支持Debug、Info、Error等多个级别的日志库,并将日志写到了磁盘文件中,代码比较简单,适合练手。有兴趣的可以通过这个链接前往:https://github.com/...

如何从 PHP 过渡到 Golang?(php转golang)

我是PHP开发者,转Go两个月了吧,记录一下使用Golang怎么一步步开发新项目。本着有坑填坑,有错改错的宗旨,从零开始,开始学习。因为我司没有专门的Golang大牛,所以我也只能一步步自己去...