guojh's Blog.

自定义Profile工具

字数统计: 975阅读时长: 5 min
2020/05/01

自定义Profile工具

许多IDE都有Profile功能,那么如何自定义一个Profile工具,并进行可视化呢?

本文为个人总结,主要参考:

自定义Profile类

Profile主要包含两个功能:计时和格式化输出结果。计时主要用到chrono类,而格式化输出按照Chrome Tracing Format格式输出即可。直接看代码,代码部分不难理解,注意多线程部分即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#ifndef PROFILER_H
#define PROFILER_H

#include <fstream>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>

// Profile类,单例
class Profiler {
public:
static Profiler &getInstance() {
static Profiler profiler;
return profiler;
}

Profiler(const Profiler &profiler) = delete;
Profiler &operator=(const Profiler &profiler) = delete;

void beginSession(const std::string &name, const std::string &filepath = "results.json") {
fout.open(filepath);
fout << "{\"otherData\": {},\"traceEvents\":[";
fout.flush();
}

void endSession() {
fout << "]}";
fout.flush();
fout.close();
}

void writeProfile(std::string name, long long startTime, long long endTime, uint32_t threadId) {
std::lock_guard<std::mutex> mutexLock(lock);

if (profileCount++ > 0) fout << ",";
std::replace(name.begin(), name.end(), '"', '\'');

fout << "{";
fout << "\"cat\":\"function\",";
fout << "\"dur\":" << (endTime - startTime) << ',';
fout << "\"name\":\"" << name << "\",";
fout << "\"ph\":\"X\",";
fout << "\"pid\":0,";
fout << "\"tid\":" << threadId << ",";
fout << "\"ts\":" << startTime;
fout << "}";

fout.flush();
}

private:
Profiler() {};

private:
std::ofstream fout;
int profileCount = 0;
std::mutex lock;
};

// 计时类
class ProfileTimer {
public:
using clock_t = std::chrono::high_resolution_clock;

explicit ProfileTimer(const std::string &name) : name(name), isStopped(false) {
start = clock_t::now();
}

virtual ~ProfileTimer() {
if (!isStopped) stop();
}

void reset() { start = clock_t::now(); }

void stop() {
end = clock_t::now();
long long startTime = std::chrono::time_point_cast<std::chrono::microseconds>(start).time_since_epoch().count();
long long endTime = std::chrono::time_point_cast<std::chrono::microseconds>(end).time_since_epoch().count();
uint32_t threadId = std::hash<std::thread::id>{}(std::this_thread::get_id());
Profiler::getInstance().writeProfile(name, startTime, endTime, threadId);

isStopped = true;
}

private:
std::string name;
std::chrono::time_point<clock_t> start;
std::chrono::time_point<clock_t> end;
bool isStopped;
};

#endif // PROFILER_H

测试Profile功能

下面对两个排序函数进行Profile,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <vector>
#include <ctime>
#include <thread>

#include "profiler.h"

using namespace std;

// use bubble sort
vector<int> sort1(const vector<int> &data) {
vector<int> ret = data;
ProfileTimer timer(__PRETTY_FUNCTION__);
for (int i = 0; i < ret.size(); ++i) {
for (int j = ret.size() - 1; j > i; --j) {
if (ret[j] < ret[j - 1]) swap(ret[j], ret[j - 1]);
}
}

return ret;
}

// use selection sort
vector<int> sort2(const vector<int> &data) {
vector<int> ret = data;
ProfileTimer timer(__PRETTY_FUNCTION__);
for (int i = 0; i < ret.size(); ++i) {
int minIndex = i;
for (int j = i; j < ret.size(); ++j) {
if (ret[j] >= ret[minIndex]) continue;
minIndex = j;
}
swap(ret[i], ret[minIndex]);
}

return ret;
}

int main() {
Profiler::getInstance().beginSession("myprofile");
ProfileTimer timer(__PRETTY_FUNCTION__);

// generate random data
int num = 1000;
vector<int> data(num, 0);
srand(time(0));
for (int i = 0; i < data.size(); ++i) data[i] = rand() % num;

// profile
thread thread1(sort1, data);
thread thread2(sort2, data);
thread1.join();
thread2.join();

timer.stop();
Profiler::getInstance().endSession();

return 0;
}

最终以json格式输出的Profile结果文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"otherData":{

},
"traceEvents":[
{
"cat":"function",
"dur":4199,
"name":"vector<int> sort2(const vector<int> &)",
"ph":"X",
"pid":0,
"tid":1043027567,
"ts":1915227044667
},
{
"cat":"function",
"dur":7265,
"name":"vector<int> sort1(const vector<int> &)",
"ph":"X",
"pid":0,
"tid":1790294731,
"ts":1915227044658
},
{
"cat":"function",
"dur":7563,
"name":"int main()",
"ph":"X",
"pid":0,
"tid":3987176989,
"ts":1915227044469
}
]
}

可视化Profile结果

将json文件放入chrome://tracing/中进行可视化,结果如下:

可以从中看到有3个线程,以及各函数的运行时间线,这对于调试程序性能、理解程序结构等十分有用。

CATALOG
  1. 1. 自定义Profile工具
    1. 1.1. 自定义Profile类
    2. 1.2. 测试Profile功能
    3. 1.3. 可视化Profile结果