关于BenchMark/c++11计时器/Chrome:tracing 问题
Posted Mr song song
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于BenchMark/c++11计时器/Chrome:tracing 问题相关的知识,希望对你有一定的参考价值。
计时器
性能的指标就是时间,在c++11后计时十分方便,因为有<chrono>
神器
在性能测试中,一般依赖堆栈上的生命周期来进行计时
计时器的实现全貌
class InstrumentationTimer
private:
chrono::time_point<chrono::steady_clock> start;
const char *m_hint;
public:
explicit InstrumentationTimer(const char *hint) : m_hint(hint)
start = chrono::steady_clock::now();
~InstrumentationTimer()
auto end = chrono::steady_clock::now();
cout << m_hint << ':' << static_cast<double>((end - start).count()) / 1e6 << "ms\\n";
long long llst = chrono::time_point_cast<chrono::microseconds>(start).time_since_epoch().count();
long long lled = chrono::time_point_cast<chrono::microseconds>(end).time_since_epoch().count();
//Instrumentor::Get().WriteProfile(m_hint, llst, lled);
;
非常简单的原理 就是应用作用域自动调用析构函数来停止计时
唯一难搞的就是chrono的层层包装
本文非常功利 不深究底层 ~
time_pointer
chrono::time_point<chrono::steady_clock> start;
在chrono命名空间下(std下层) 有个神奇的类型 叫时间点time_point
在不同的操作环境下 有不同的实现 所以这是一个模板
模板类型可以有
chrono::high_resolution_clock
高解析度类型 不建议使用 因为这个可能有移植的问题 但好像进度最高?chrono::steady_clock
稳得一批的钟 我超爱这个 因为这个不仅进度不低 而且调用的时间短,影响极小 (300nschrono::system_clock
系统带的钟 不大行 精度因系统而定? windows是100ns
所以 你懂的 用steady就好了(也不用太纠结几纳秒
给时间点一个当前时间 注意类型一致
start = chrono::steady_clock::now();
duration
auto dur = end - start;
为啥用auto 因为方便昂(duration 模板具体化写到头皮发麻
时间点运算得到的是时间段 因为默认的时间点单位时间是纳秒(steady_clock),所以得到的时间是内部以longlong存储的多少纳秒
如何调出时间?
(end - start).count()
得到的是longlong ns
如何更改单位时间?
一个是转换时间段的格式
chrono::duration_cast<chrono::microseconds>(end - start).count())
一个是转换时间点的格式
chrono::time_point_cast<chrono::microseconds>(start)
如何调出一个时间戳?(系统从我也不知道哪开始算起的时间段 1970.1.1大概? 相当于帮你减了一下
start.time_since_epoch().count()
可选格式:
-
chrono::nanoseconds
-
chrono::microseconds
-
chrono::milliseconds
-
chrono::seconds
-
chrono::minutes
-
chrono::hours
回到实现
构造函数没啥好讲的 就是开始计时
重点是析构函数
~InstrumentationTimer()
auto end = chrono::steady_clock::now();
cout << m_hint << ':' << static_cast<double>((end - start).count()) / 1e6 << "ms\\n";
long long llst = chrono::time_point_cast<chrono::microseconds>(start).time_since_epoch().count();
long long lled = chrono::time_point_cast<chrono::microseconds>(end).time_since_epoch().count();
Instrumentor::Get().WriteProfile(m_hint, llst, lled);
思路:
-
首先!!!一定先停止计时 (你不会还想增大误差吧) 用auto接住 省一个成员
-
然后 输出的是你要计时的位置的注释(hint) 接一个时间段
因为时间段输出的是longlong 我看多了几点几ms觉得非常亲切 所以用纳秒算时间段(默认)后再除1e6得到毫秒
-
留两个时间戳后面有用
-
然后是后面的调用记录某一段程序运行时间的函数啦 这里传进去的有hint 开始和结束的时间戳 有了这些 你就能算出经过的时间
整理输出部分
Chrome大法好
chromo 自带了个可视化分析软件 在地址栏上输入chrome://tracing/
就可以看到
它接受的是json文件 所以我们要把我们记录下来的东西打包成json拖到界面上 就可以看到精美(并不) 的可视化界面
这是打包器+记录器的全貌
class Instrumentor
private:
ofstream m_OutputStream;
bool m_Fir;
public:
Instrumentor() : m_Fir(true)
void BeginSession(const string &filepath = "results.json")
m_OutputStream.open(filepath);
WriteHeader();
void EndSession()
WriteFooter();
m_OutputStream.close();
m_Fir = true;
void WriteProfile(const ProfileResult &result)
if (!m_Fir) //not add ',' when first time
m_OutputStream << ',';
else m_Fir = false;
string name(result.Name);
replace(name.begin(), name.end(), '"', '\\'');
m_OutputStream << R"()";
m_OutputStream << R"("cat":"function",)";
m_OutputStream << R"("dur":)" << result.end - result.start << ",";
m_OutputStream << R"("name":")" << name << "\\",";
m_OutputStream << R"("ph":"X",)";
m_OutputStream << R"("pid":0,)";
m_OutputStream << R"("tid":0,)";
m_OutputStream << R"("ts":)" << result.start;
m_OutputStream << R"()";
m_OutputStream.flush();
void WriteHeader()
m_OutputStream << R"("otherData":,"traceEvents":[)";
m_OutputStream.flush();
void WriteFooter()
m_OutputStream << "]";
m_OutputStream.flush();
static Instrumentor &Get()
static auto instance = new Instrumentor();
return *instance;
;
以及我们的目标 Chrome能识别的json文件
"otherData": ,
"traceEvents": [
"cat": "function",
"dur": 2166411,
"name": "void core1(int)",
"ph": "X",
"pid": 0,
"tid": 0,
"ts": 19699253339
,
"cat": "function",
"dur": 1649285,
"name": "void core2()",
"ph": "X",
"pid": 0,
"tid": 0,
"ts": 19701420118
,
"cat": "function",
"dur": 3816266,
"name": "void benchMark()",
"ph": "X",
"pid": 0,
"tid": 0,
"ts": 19699253338
]
Get( )
首先看到最后的Get( )
static Instrumentor &Get()
static auto instance = new Instrumentor();
return *instance;
这个能提供给我们一个单例,就是仅存在一个与我们运行时的对象
static 显式的指出Get得到的东西是和我们exe文件存在时间一样长的 而且这个定义只执行一次
如果你没有听懂 就只要记住它返回的永远是同一个对象 要用这个对象的时候就用Get
该这么用:
Instrumentor::Get().balabala();
初始化
private:
ofstream m_OutputStream;
bool m_Fir;
public:
Instrumentor() : m_Fir(true)
void BeginSession(const string &filepath = "results.json")
m_OutputStream.open(filepath);
WriteHeader();
void EndSession()
WriteFooter();
m_OutputStream.close();
m_Fir = true;
ofsteam文件输出流用于输出到文件默认是results.json
不要忘记列表中的逗号的处理 我们用m_Fir检测是不是第一个
然后是注意到json开头和结尾是固定的
void WriteHeader()
m_OutputStream << R"("otherData":,"traceEvents":[)";
m_OutputStream.flush();
void WriteFooter()
m_OutputStream << "]";
m_OutputStream.flush();
R"( string )"即原始字符串 可以输出字符串里面的原本的字符 感兴趣的可以自行拓展更多有关知识 这里用了之后就不用打转义的双引号了
每次输出到文件时记得及时刷新 m_OutputStream.flush();
防止之后的线程出现毛病
ok 现在我们可以这么用了
int main()
Instrumentor::Get().BeginSession();
benchMark(); //测试的函数放这里
Instrumentor::Get().EndSession();
中间列表的填写
但是?最最最重要的中间列表的填写呢?
在这里
void WriteProfile(const ProfileResult &result)
if (!m_Fir) //not add ',' when first time
m_OutputStream << ',';
else m_Fir = false;
string name(result.Name);
replace(name.begin(), name.end(), '"', '\\'');
m_OutputStream << R"()";
m_OutputStream << R"("cat":"function",)";
m_OutputStream << R"("dur":)" << result.end - result.start << ",";
m_OutputStream << R"("name":")" << name << "\\",";
m_OutputStream << R"("ph":"X",)";
m_OutputStream << R"("pid":0,)";
m_OutputStream << R"("tid":0,)";
m_OutputStream << R"("ts":)" << result.start;
m_OutputStream << R"()";
m_OutputStream.flush();
在InstrumentationTimer中的调用:
//m_hint 是计时器注释 llst 开始时间戳 lled 结束时间戳
Instrumentor::Get().WriteProfile(m_hint, llst, lled);
定义传进来的参数 可以扩展
struct ProfileResult
string Name;
long long start, end;
;
就是简单的往里面塞东西啦
值得注意的是 chrome 的tracing 默认时间戳的单位时间是microseconds 即毫秒 所以要记得转换格式哦
long long llst = chrono::time_point_cast<chrono::microseconds>(start).time_since_epoch().count();
long long lled = chrono::time_point_cast<chrono::microseconds>(end).time_since_epoch().count();
考虑到传进来的函数名字可能会带有" "
让json出错 所以退而求其次 把它转成 ' '
(其实在前面加一个转义字符更好 但是实现起来太麻烦了)
string name(result.Name);
replace(name.begin(), name.end(), '"', '\\'');
以上是关于关于BenchMark/c++11计时器/Chrome:tracing 问题的主要内容,如果未能解决你的问题,请参考以下文章