【发布时间】:2022-01-11 04:35:45
【问题描述】:
应该使用哪些函数来计算函数的时间 有没有为此目的的内置函数。
【问题讨论】:
应该使用哪些函数来计算函数的时间 有没有为此目的的内置函数。
【问题讨论】:
您可以使用此代码:
auto a = chrono::steady_clock::now();
//write your code here
auto b = chrono::steady_clock::now();
double time = chrono::duration <double, milli> (b - a).count();
cout << endl << "Time ---> " << time;
【讨论】:
没有专门用于计算函数所用时间的内置内容。
有内置函数可以检索当前时间。有关详细信息,请参阅std::chrono::high_resolution_clock::now()。
要对函数计时,您应该(至少通常)在进入函数之后检索当前时间,并在退出之前再次检索。
然后,您可以将两者相减,并使用std::duration_cast 将其转换为方便的持续时间(毫秒、微秒或纳秒,视情况而定)。
把它们放在一起,你可以(例如)得到这样的东西:
template <class T, class U, template<class, class> class F, typename ...Args>
auto timer(F<T,U> f, std::string const &label, Args && ...args) {
using namespace std::chrono;
auto start = high_resolution_clock::now();
auto holder = f(std::forward<Args>(args)...);
auto stop = high_resolution_clock::now();
std::cout << label << " time: " << duration_cast<microseconds>(stop - start).count() << "\n";
return holder;
}
这使您可以传递一些任意函数(以及要传递给该函数的参数)并打印出函数执行所用的时间。目前它正在执行微秒,但将其更改为毫秒应该是微不足道的。
最后,这会返回被调用函数产生的任何值,所以如果你有类似的东西:
x = f(a, b, c);
您可以将其替换为:
x = timer(f, a, b, c);
...打印出f 消耗的时间。
【讨论】:
std::steady_clock 通常不推荐用于计时吗? std::high_resolution_clock 允许与 std::system_clock 相同,这会受到进程外部时间更改的影响。
clock() 可能是最接近的。它应该给 CPU 时间,但一些实现却给了 wall time,而且分辨率通常很粗糙(通常在几十到几百毫秒的数量级)。 time()应该是给wall time的,一般分辨率是1秒。
我通常不以秒为单位计算时间,因为它在具有不同 CPU 频率的机器上会有所不同。我通常使用 CPU 周期,这样如果您在笔记本电脑或超频的高端服务器上运行,它将大致相同。周期还可以更好地反映和关联其他机器特性,如 CPU 缓存延迟。这是Linux内核内部使用的
# cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
每个 CPU 架构都有某种内部时间戳计数器。在 x86_64 上,它是 TSC(时间戳计数器),可以使用 Intel 内在 '__builtin_ia32_rdtsc()' 读取,如
#include <cstdint>
static inline std::uint64_t now() {
return __builtin_ia32_rdtsc();
}
然后在您的代码上,您可以执行以下操作:
std::uint64_t t0 = now();
call_my_algo();
uint64_t t1 = now();
uint64_t ticks = t1>=t0 ? t1-t0 : (std::numeric_limits<uint64_t>::max() - t0)+t1;
最后一行是处理翻转,但这应该在 177 年中只发生一次,所以不是真正的问题。您只需 t1-t0 即可逃脱惩罚。
rdtsc 调用对 std::chrono 的优势在于您没有调用 glibc 和内核,而这正是 chrono 最终所做的。开销很小,因此这种方法非常适合微基准测试。
有几点需要注意。每个核心都包含自己的时间戳计数器,因此如果它们不同步,那么如果进程从一个核心移动到另一个核心,或者新的时间戳可能低于 start 从而差异变为负数,那么您可能会读取错误 - 因此滴答声将下溢.但这些现在很少见,在旧机器中更常见。
您可以检查您机器上的 TSC 是否值得信任
cat /proc/cpuinfo | grep tsc | tr ' ' '\n' | grep tsc | sort | uniq
constant_tsc
nonstop_tsc
rdtscp
tsc
tsc_scale
constant_tsc - 时间戳计数器频率恒定
nonstop_tsc - 计数器不会在 C 状态下停止
rdtscp - cpu 有 rdtscp 指令(返回时间戳和核心)
tsc - cpu 有时间戳计数器
tsc_scale - amd tsc 缩放支持
你应该小心,无论你使用 std::chrono,它最终会在 Linux 上调用 clock_gettime(),或者如果你使用 TSC,编译器可能会颠倒你的语句顺序,这样上面的代码就可以成为
std::uint64_t t0 = now();
uint64_t t1 = now();
uint64_t ticks = t1>=t0?t1-t0 : (std::numeric_limits<uint64_t>::max() - t0)+t1;
call_my_algo();
因此,您将不会测量任何东西。避免这种情况的一种方法是设置优化障碍,例如
#include <cstdint>
static inline std::uint64_t now() {
asm volatile ( "" ::: "memory" );
return __builtin_ia32_rdtsc();
}
还有记忆障碍的问题,可能非常复杂。
【讨论】: