C++ 设置/暂停C+中std::时钟的时钟时间+;
我写了一些东西来测量代码运行的时间,并将其打印出来。我现在使用的方法支持这些度量的嵌套 问题是,在获取时间间隔、将其转换为数字、获取时间格式,然后将所有内容转换为字符串,然后将其打印出来的过程中需要一段时间(2-3毫秒),I/O似乎特别昂贵。我想让时钟在某种意义上“跳过”这个过程,因为我测量的是微秒。(我认为这是一个很好的特性,如果我还有其他东西想跳过的话) 宏是可选的,它只是一个快速的东西,使它不那么混乱,并允许我显示被调用函数的名称 宏显式扩展为C++ 设置/暂停C+中std::时钟的时钟时间+;,c++,clock,chrono,benchmarking,C++,Clock,Chrono,Benchmarking,我写了一些东西来测量代码运行的时间,并将其打印出来。我现在使用的方法支持这些度量的嵌套 问题是,在获取时间间隔、将其转换为数字、获取时间格式,然后将所有内容转换为字符串,然后将其打印出来的过程中需要一段时间(2-3毫秒),I/O似乎特别昂贵。我想让时钟在某种意义上“跳过”这个过程,因为我测量的是微秒。(我认为这是一个很好的特性,如果我还有其他东西想跳过的话) 宏是可选的,它只是一个快速的东西,使它不那么混乱,并允许我显示被调用函数的名称 宏显式扩展为 tracker loop = "for(in
tracker loop = "for(int i = 0; i != 100; ++i){ tracker b = TRACK(function_call()); b.display(); }"
loop.start()
for(int i = 0; i != 100; ++i){
tracker b = "function_call()"
b.start();
function_call();
b.end();
b.display();
}
loop.end();
loop.display();
在大多数情况下,打印不是问题,它只跟踪start()
和end()
之间的内容,但这里的b.display()
会干扰跟踪器循环
我的目标是让跟踪器尽可能的非侵入性,所以我希望它的大部分/全部都在跟踪器类中处理。但是随后我遇到了b.display()
是一个不同于跟踪器循环的实例方法的问题。我尝试了一些使用static
关键字的方法,但是遇到了一些问题(仍然尝试了一些)。我可能在这里把自己编码成了死胡同,但还有很多事情要做。只需分别计算两个时间间隔的时间,并将它们相加即可,即总共节省4个时间戳。对于嵌套的时间间隔,您可以将时间戳保存到数组中,并在最后对所有内容进行排序。(或者在时间戳被覆盖之前在外部循环内)。存储到一个数组是相当便宜的
或更好:推迟打印到以后。
如果时间间隔仅为毫秒,只需保存要打印的内容并在时间间隔之外执行即可
如果您有嵌套的定时间隔,则至少将打印从最内部的间隔中移出,以将必须执行的停止/重新启动量降至最低
如果您在各地手动检测代码,可以查看flamegraph之类的评测工具,尤其是当您的时间间隔在函数边界上出现故障时
不仅I/O本身需要时间,而且会使以后的代码在几百或几千个周期内运行得更慢。进行系统调用会涉及大量代码,因此当您返回到用户空间时,很可能会出现指令缓存和数据缓存未命中。修改页面表的系统调用也会导致TLB未命中
(参见“系统调用的(实际)成本”一节(Soares,Stumm),在运行Linux的i7 Nehalem上计时。(2008/9年的第一代i7)。本文提出了一种用于高吞吐量web服务器和类似服务器的批处理系统调用机制,但它们在普通Linux上的基线结果很有趣,并且与此无关。)
在启用了熔毁缓解功能的现代Intel CPU上,通常会出现TLB未命中。在最近的x86上启用Spectre缓解后,分支预测历史记录可能会被删除,具体取决于缓解策略。(英特尔为内核添加了一种方法,要求在此点之后较高权限的分支不会受到较低权限分支的预测历史记录的影响。在当前CPU上,我认为这只是刷新分支预测缓存。)
您可以通过让iostream
为您提供缓冲区来避免系统调用开销。格式化和复制数据仍然是一项重要的工作,但比向终端写入数据要便宜得多将程序的stdout
重定向到文件时,默认情况下会使cout
完全缓冲,而不是行缓冲。即,按如下方式运行:
./my_program > time_log.txt
最终的输出将与您在终端上获得的结果相匹配,但是(只要您不做任何傻事,比如使用std::endl
强制刷新),它将被缓冲。默认缓冲区大小可能类似于4KB。使用strace./my_program
或类似的工具跟踪系统调用,并确保在最后得到一个大的write()
,而不是很多小的write()
s
避免在(外部)计时区域内进行缓冲I/O是很好的,但如果您将计时降至纳秒,则避免在“真实”(未插入指令的)代码不会进行系统调用的地方进行系统调用是非常重要的。即使在定时间隔之前,这也是事实,而不仅仅是在内部
cout为了克服开销,延时打印可以由另一个线程完成。主线程将开始和结束时间保存到共享的全局变量中,并通知打印线程正在等待
#include<iostream>
#include<thread>
#include<chrono>
#include<mutex>
#include<condition_variable>
#include<atomic>
std::condition_variable cv;
std::mutex mu;
std::atomic<bool> running {true};
std::atomic<bool> printnow {false};
// shared but non-atomic: protected by the mutex and condition variable.
// possible bug: the main thread can write `now` before print_thread wakes up and reads it
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::time_point now;
void print_thread() {
std::thread([]() {
while (running) {
std::unique_lock<std::mutex> lock(mu);
cv.wait(lock, []() { return !running || printnow; });
if (!running) return;
std::chrono::milliseconds lapse_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
printnow = false;
std::cout << " lapse time " << lapse_ms.count() << " ms\n";
}
}).detach();
}
void print_lapse(std::chrono::high_resolution_clock::time_point start1, std::chrono::high_resolution_clock::time_point now1) {
start = start1;
now = now1;
printnow = true;
cv.notify_one();
}
int main()
{
//launch thread
print_thread();
// laspe1
std::chrono::high_resolution_clock::time_point start1 = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::chrono::high_resolution_clock::time_point now1 = std::chrono::high_resolution_clock::now();
print_lapse(start1,now1);
// laspe2
start1 = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
now1 = std::chrono::high_resolution_clock::now();
print_lapse(start1, now1);
//winding up
std::this_thread::sleep_for(std::chrono::milliseconds(300));
running = false;
cv.notify_one();
}
#包括
#包括
#包括
#包括
#包括
#包括
std::条件变量cv;
std::互斥mu;
std::原子运行{true};
std::原子printnow{false};
//共享但非原子的:受互斥和条件变量保护。
//可能的错误:主线程可以在print_线程唤醒并读取它之前写入'now'
标准:时钟:高分辨率时钟:时间点开始;
标准::时钟::高分辨率时钟::时间点现在;
无效打印线程(){
标准::线程([])(){
(跑步时){
std::唯一锁(mu);
cv.wait(lock,[](){return!running | | printnow;});
如果(!运行)返回;
std::chrono::毫秒间隔\u ms=std::chrono::持续时间\u转换(现在-开始);
printnow=false;
std::cout smth like:`std::chrono::time_point first2=clock.now();std::cout延迟打印会很麻烦,因为我有多个这样的实例,所以打印可能发生在另一个间隔内,这实际上是唯一的延迟
./my_program > time_log.txt
#include<iostream>
#include<thread>
#include<chrono>
#include<mutex>
#include<condition_variable>
#include<atomic>
std::condition_variable cv;
std::mutex mu;
std::atomic<bool> running {true};
std::atomic<bool> printnow {false};
// shared but non-atomic: protected by the mutex and condition variable.
// possible bug: the main thread can write `now` before print_thread wakes up and reads it
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::time_point now;
void print_thread() {
std::thread([]() {
while (running) {
std::unique_lock<std::mutex> lock(mu);
cv.wait(lock, []() { return !running || printnow; });
if (!running) return;
std::chrono::milliseconds lapse_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
printnow = false;
std::cout << " lapse time " << lapse_ms.count() << " ms\n";
}
}).detach();
}
void print_lapse(std::chrono::high_resolution_clock::time_point start1, std::chrono::high_resolution_clock::time_point now1) {
start = start1;
now = now1;
printnow = true;
cv.notify_one();
}
int main()
{
//launch thread
print_thread();
// laspe1
std::chrono::high_resolution_clock::time_point start1 = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::chrono::high_resolution_clock::time_point now1 = std::chrono::high_resolution_clock::now();
print_lapse(start1,now1);
// laspe2
start1 = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
now1 = std::chrono::high_resolution_clock::now();
print_lapse(start1, now1);
//winding up
std::this_thread::sleep_for(std::chrono::milliseconds(300));
running = false;
cv.notify_one();
}