我写了一些东西来测量我的代码运行多长时间并打印出来。我现在拥有的方式支持这些度量的嵌套。
问题是,在获取时间间隔,将其转换为数字,获取时间格式,然后将所有内容转换为字符串,然后将其打印出来需要一段时间(2-3毫秒)的过程中,我/ O特别昂贵。我希望时钟可以在某种意义上“跳过”此过程,因为我正在测量的内容将以微秒为单位。 (而且,无论是否有其他要跳过的内容,我都认为这将是一个很好的功能)
std::chrono::high_resolution_clock clock;
std::chrono::time_point<std::chrono::steady_clock> first, second;
first = clock.now();
std::chrono::time_point<std::chrono::high_resolution_clock> paused_tp = clock.now();
std::cout << "Printing things \n";
clock.setTime(paused_tp); // Something like this is what I want
second = clock.now();
该想法是确保first
和second
具有最小的差异,理想情况下是相同的。
根据我的见解,high_resolution_clock
类(以及所有其他chrono
个时钟)将它们的time_point
设为私有,您只能从clock.now()
访问它< / p>
我知道那里可能有基准库可以做到这一点,但是我想自己弄清楚如何做到这一点(如果只是为了知道如何去做)。任何其他图书馆如何做到这一点的信息,或关于计时如何工作的见解也将不胜感激。我可能会误解chrono
在内部如何保持跟踪。
(std::chrono::high_resolution_clock
足够准确吗?
(尽管我在这里可以使C ++程序更高效的任何资源都很棒)
编辑:实际上,我确实在尝试计时的那一部分代码之后进行打印,问题只出现在,例如,当我想计时整个程序以及单个功能时。然后,打印功能时间将导致整个程序时间的延迟。
编辑2:我认为我应该更多地说明自己在做什么。
我有一个可以处理所有事情的类,比方说它叫做tracker
,它可以处理所有的废话。
tracker loop = TRACK(
for(int i = 0; i != 100; ++i){
tracker b = TRACK(function_call());
b.display();
}
)
loop.display();
该宏是可选的,它是一项快速的事情,它使混乱程度降低了,并允许我显示被调用函数的名称。
明确地将宏扩展到
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()
最终会干扰{{1} }。
我的一个目标是使跟踪器尽可能不打扰,所以我希望大多数/全部在跟踪器类中进行处理。但是后来我遇到了一个问题:tracker loop
与b.display()
是不同实例的方法。我已经使用tracker loop
关键字尝试了一些操作,但是遇到了一些问题(仍然尝试了一些)。我可能在这里已经陷入困境,但是仍然有很多事情可以尝试。
答案 0 :(得分:1)
只需将两个时间间隔分别计时并添加,即节省4个总时间戳。对于嵌套间隔,您可能只是将时间戳保存到数组中,然后将所有内容整理出来。 (或者在覆盖时间戳之前在外部循环中)。存储到阵列非常便宜。
或更佳:将打印推迟到以后。
如果定时间隔仅为毫秒,则只需保存要打印的内容,然后在定时间隔之外进行打印即可。
如果您有嵌套的定时间隔,请至少将打印从最里面的间隔中抽出,以最大程度地减少停止/重新启动的次数。
如果您要在各处手动检测代码,则可以看一下性能分析工具,例如Flamegraph,尤其是在定时间隔超出函数边界的情况下。 linux perf: how to interpret and find hotspots。
I / O本身不仅花费时间,还会使以后的代码运行几百或数千个周期变慢。进行系统调用会涉及很多代码,因此当您返回时到用户空间,很可能会收到指令缓存和数据缓存未命中的情况。修改页表的系统调用也会导致TLB未命中。
(请参阅“系统调用的(实际)成本”一节in the FlexSC paper(Soares,Stumm),该时钟在运行Linux的i7 Nehalem上计时(〜2008 / 9的第一代i7)。高吞吐量Web服务器和类似服务器的批处理系统调用机制,但它们在普通Linux上的基准结果很有趣,并且与此相关。)
在启用了Meltdown缓解功能的现代Intel CPU上,通常会遇到TLB未命中的情况。在最近的x86上启用Spectre缓解功能后,取决于缓解策略,可能会清除分支预测历史记录。 (英特尔为此增加了一种内核请求高权限分支的方式,此点将不受低权限分支的预测历史记录的影响。在当前CPU上,我认为这只是刷新了分支预测缓存。)>
通过让iostream
为您缓冲,可以避免系统调用的开销。格式化和复制数据仍然是一项重要工作,但是比写入终端要便宜得多。 将程序的stdout
重定向到文件将默认使cout
处于全缓冲状态,而不是行缓冲状态。即按以下方式运行:
./my_program > time_log.txt
最终输出将与您在终端上获得的输出匹配,但是(只要您不像使用std::endl
强制刷新那样愚蠢地进行输出),它就会被缓冲。默认缓冲区大小可能类似于4kiB。使用strace ./my_program
或类似工具跟踪系统调用,并确保最后得到一个大write()
,而不是很多小的write()
。
最好避免在(外部)定时区域内缓冲I / O,但是避免在您的“真实”(非仪表)代码没有它们的地方进行系统调用非常重要,计时到纳秒。即使在 时间间隔之前,也是如此,不仅仅是内部。
cout << foo
如果不进行系统调用,就降低以后的代码速度而言,并不是“特别的”。
答案 1 :(得分:0)
为克服开销,可以通过另一个线程来完成延时打印。主线程将开始时间和结束时间保存到共享的全局变量中,并通知condition variable打印线程正在等待。
#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();
}