如何简单且持续的关注自己的函数性能表现?
这是一个通过chrono库时间函数写的Time类简单检验函数性能例子
#include <iostream>
#include <string>
#include <chrono>
class Timer {
public:
Timer(const char* name)
:name(name),stopped(false)
{
startTime = std::chrono::high_resolution_clock::now();
}
void Stop() {
auto endTime = std::chrono::high_resolution_clock::now();
long long start = std::chrono::time_point_cast
<std::chrono::milliseconds>(startTime).time_since_epoch().count();
long long end = std::chrono::time_point_cast
<std::chrono::milliseconds>(endTime).time_since_epoch().count();
std::cout << name << ":" << (end - start) << "ms\n";
stopped = true;
}
~Timer() {
if (!stopped) {
Stop();
}
}
private:
const char* name;
std::chrono::time_point<std::chrono::steady_clock> startTime;
bool stopped;
};
void Function1() { //示例函数
Timer timer("Function1");
for (int i = 0; i < 1000; i++) {
std::cout <<"Hello World #" << i << std::endl;
}
}
int main() {
Function1();
std::cout << "Hello world!" << std::endl;
std::cin.get();
return 0;
}
在测试函数类构建一个Timer对象让他开始计时,再离开函数作用时会自动调用析构函数停止计时并且输出耗时结果
但显然,他们仍是数字,而且在控制台寻找计时结果非常烦人,所以,有没有更好的方法呢?
我们可以可视化我们的分析和堆栈跟踪视图
使用微软自带的egde浏览器在你的url栏输入edge://tracing
如果你是谷歌浏览器就输入chrome://tracing就好了
别的浏览器我不太确定,你可以试试:对应浏览器英文名://tracing
进入这个页面通过Load加载一个包含所有相关数据的.json文件就可以可视化数据了。
但,我们如果获取c++项目里的计时相关.json数据呢?
首先在C++项目分别创建两个头文件填入如下代码
1.InstrumentorMacro.h
#pragma once
#define PROFILING 1
#if PROFILING
#define PROFILE_SCOPE(name) InstrumentationTimer timer##__LINE__(name)
#ifdef _MSC_VER
#define PROFILE_FUNCTION() PROFILE_SCOPE(__FUNCSIG__)
#else
#define PROFILE_FUNCTION() PROFILE_SCOPE(__PRETTY_FUNCTION__)
#endif
#else
#define PROFILE_SCOPE(name)
#define PROFILE_FUNCTION()
#endif
2.InstrumentorTimer.h
//
// Basic instrumentation profiler by Cherno
// ------------------------
// Modified by Gavin
// 1. Make 'WriteProfile' thread-safe by add 'std::mutex' and 'std::lock_guard'
// 2. Remove the 'InstrumentationSession' which is no significance
// 3. Add destructor for 'Instrumentor'
// 4. Change the Constructor of 'Instrumentor' to private
// ------------------------
// Usage: include this header file somewhere in your code (e.g. precompiled header), and then use like:
//
// Instrumentor::Get().BeginSession("Session Name"); // Begin session
// {
// InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling
// // Code
// }
// Instrumentor::Get().EndSession(); // End Session
//
// You will probably want to macro-fy this, to switch on/off easily and use things like __FUNCSIG__ for the profile name.
//
#pragma once
#include <string>
#include <chrono>
#include <fstream>
#include <thread>
#include <mutex>
#include <algorithm>
struct ProfileResult {
std::string Name;
long long Start, End;
uint64_t ThreadID;
};
class Instrumentor {
private:
std::string m_SessionName;
std::ofstream m_OutputStream;
int m_ProfileCount;
std::mutex m_Lock;
bool m_ActiveSession;
public:
~Instrumentor() {
EndSession();
}
Instrumentor(const Instrumentor&) = delete;
static void BeginSession(const std::string& name, const std::string& filepath = "Results.json") {
GetInstance().BeginSessionImpl(name, filepath);
}
static void EndSession() {
GetInstance().EndSessionImpl();
}
static void WriteProfile(const ProfileResult& result) {
GetInstance().WriteProfileImlp(result);
}
private:
Instrumentor()
: m_SessionName("None"), m_ProfileCount(0), m_ActiveSession(false) {
;
}
static Instrumentor& GetInstance() {
static Instrumentor instance;
return instance;
}
void BeginSessionImpl(const std::string& name, const std::string& filepath = "Results.json") {
if (m_ActiveSession) {
EndSession();
}
m_ActiveSession = true;
m_SessionName = name;
m_OutputStream.open(m_SessionName + std::string("_") + filepath);
WriteHeader();
}
void EndSessionImpl() {
if (!m_ActiveSession) {
return;
}
m_ActiveSession = false;
m_ProfileCount = 0;
WriteFooter();
m_OutputStream.close();
}
void WriteHeader() {
m_OutputStream << "{\"otherData\": {},\"traceEvents\":[";
m_OutputStream.flush();
}
void WriteFooter() {
m_OutputStream << "]}";
m_OutputStream.flush();
}
void WriteProfileImlp(const ProfileResult& result) {
std::lock_guard<std::mutex> lockGuard(m_Lock);
if (m_ProfileCount++ > 0)
m_OutputStream << ",";
std::string name = result.Name;
std::replace(name.begin(), name.end(), '"', '\'');
m_OutputStream << "{";
m_OutputStream << "\"cat\":\"function\",";
m_OutputStream << "\"dur\":" << (result.End - result.Start) << ',';
m_OutputStream << "\"name\":\"" << name << "\",";
m_OutputStream << "\"ph\":\"X\",";
m_OutputStream << "\"pid\":\"" << m_SessionName << "\",";
m_OutputStream << "\"tid\":" << result.ThreadID << ",";
m_OutputStream << "\"ts\":" << result.Start;
m_OutputStream << "}";
m_OutputStream.flush();
}
};
class InstrumentationTimer {
private:
const char* m_Name;
std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint;
bool m_Stopped;
public:
explicit InstrumentationTimer(const char* name)
: m_Name(name), m_Stopped(false) {
m_StartTimepoint = std::chrono::high_resolution_clock::now();
}
~InstrumentationTimer() {
if (!m_Stopped) {
Stop();
}
}
void Stop() {
auto endTimepoint = std::chrono::high_resolution_clock::now();
long long start = std::chrono::time_point_cast<std::chrono::microseconds>(
m_StartTimepoint).time_since_epoch().count();
long long end = std::chrono::time_point_cast<std::chrono::microseconds>(
endTimepoint).time_since_epoch().count();
uint64_t threadID = std::hash<std::thread::id>{}(std::this_thread::get_id());
Instrumentor::WriteProfile({ m_Name, start, end, threadID });
m_Stopped = true;
}
};
在main函数所在的.cpp文件中
将InstrumentorTimer.h和InstrumentorMacro.h(可选的,一些宏定义)正确引入后。
Instrumentor::BeginSession("SessionName"); // Begin session
{
InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling
// Code Blocks
// timer.Stop(); // (Optional) Stop timing manually, timer's destructor will call this function automatically
}
// Instrumentor::EndSession(); // (Optional) End Session manually, Instrumentor's destuctor will call this function automatically
则作用域内的代码会被正确记录到${SessionName}_Results.json
文件中,该文件可在该项目.sln文件所在目录找到
在edge浏览器中进入edge://tracing页面,将该文件拖入即可看到可视化结果。
在将头文件引入后,在main函数中调用BeginSession()
函数,在里面调用测试函数进行计时,不过在这之前需在测试函数域头部创建InstrumentationTimer
的实例开始计时,当希望停止计时时调用该实例的Stop()
函数即可,当作用域完成后该实例销毁,析构函数会检测当前是否已经完成计时,如果没有完成会自动调用Stop()
函数。
代码例
#include<iostream>
#include "InstrumentorTimer.h"
#include "InstrumentorMacro.h"
void Function1() {
std::string name = std::string("Function1");
InstrumentationTimer timer(name.c_str()); //测试函数内必加行
//Timer timer("Function1");
for (int i = 0; i < 1000; i++) {
std::cout <<"Hello World #" << i << std::endl;
}
}
void Function2() {
std::string name = std::string("Function2");
InstrumentationTimer timer(name.c_str());//测试函数内必加行
for (int i = 0; i < 100; i++) {
std::cout << "Hello World2 #" << i << std::endl;
}
}
int main() {
Instrumentor::BeginSession("SessionName"); // Begin session
{
Function1(); //测试函数1
Function2(); //测试函数2
InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling
// Code Blocks
// timer.Stop(); // (Optional) Stop timing manually, timer's destructor will call this function automatically
}
//std::cin.get();
return 0;
}
当然不止是单线程函数检测,还可以检测多线程
笔者线程相关了解肤浅,无妨能用就行。
多线程代码例:
#include <iostream>
#include<thread>
#include "InstrumentorTimer.h"
#include "InstrumentorMacro.h"
void DoWork() {
std::string name = std::string("DoWork");
InstrumentationTimer timer(name.c_str()); //必加行
int x = 2;
using namespace std::literals;
std::cout << "Starting thread id=" << std::this_thread::get_id() << std::endl;
while (x) {
std::cout << x << "Working..." << std::endl;
std::this_thread::sleep_for(1s);
x--;
}
}
void Function1() {
std::string name = std::string("Function1");
InstrumentationTimer timer(name.c_str());
//Timer timer("Function1");
for (int i = 0; i < 1000; i++) {
std::cout <<"Hello World #" << i << std::endl;
}
}
void Function2() {
std::string name = std::string("Function2");
InstrumentationTimer timer(name.c_str());
for (int i = 0; i < 100; i++) {
std::cout << "Hello World2 #" << i << std::endl;
}
}
int main() {
Instrumentor::BeginSession("SessionName"); // Begin session
{
std::thread worked1(Function1); //测试函数1
Function2(); //测试函数2
std::thread worked2(DoWork);
worked2.join();
worked1.join();
InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling
// Code Blocks
// timer.Stop(); // (Optional) Stop timing manually, timer's destructor will call this function automatically
}
//std::cin.get();
return 0;
}
图片例:
参考例:
Github上的:InstrumentorTimer
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。