定时器在C中查找函数调用中的已用时间
我想计算在C中函数调用期间经过的时间,精度为1纳秒。
C中是否有可用的定时器function?
如果是,请提供示例代码段。
伪代码
Timer.Start() foo(); Timer.Stop() Display time elapsed in execution of foo()
环境细节: – 在RHEL机器上使用gcc 3.4编译器
请问您使用的是哪种处理器? 如果您使用的是x86处理器,则可以查看时间戳计数器( tsc
)。 此代码段:
#define rdtsc(low,high) \ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
将把CPU运行的周期数分别设置为low
和high
(它需要2 long
s;你可以将结果存储在long long int
),如下所示:
inline void getcycles (long long int * cycles) { unsigned long low; long high; rdtsc(low,high); *cycles = high; *cycles <<= 32; *cycles |= low; }
请注意,这将返回CPU执行的周期数。 您需要获得CPU速度,然后计算每ns的循环次数,以获得经过的ns数。
为了做到这一点,我已经解析了/proc/cpuinfo
的“cpu MHz”字符串,并将其转换为小数。 在那之后,它只是一点数学,并记住1MHz =每秒1,000,000个周期,并且有10亿ns /秒。
在Intel和兼容的处理器上,您可以使用rdtsc指令,该指令可以轻松地包装到asm()C代码块中。 它返回内置处理器周期计数器的值,该计数器在每个周期递增。 你获得高分辨率,这样的时间非常快。
要确定您需要校准的增量有多快 – 在固定的时间段(如5秒)内调用此指令两次。 如果在将频率转换为较低功耗的处理器上执行此操作,则可能会出现校准问题。
使用clock_gettime(3)
。 有关更多信息,请键入man 3 clock_gettime
。 话虽这么说,纳秒精度很少是必要的。
任何计时器function都必须是特定于平台的,特别是在具有该精度要求的情况下。
POSIX系统中的标准解决方案是gettimeofday()
,但它只有几微秒的精度。
如果这是用于性能基准测试,则标准方法是使测试中的代码花费足够的时间来降低精度要求。 换句话说,运行测试代码一秒钟(或更长时间)。
c中没有定时器,保证1纳秒的精度。 您可能想要查看clock()
或更好的POSIX gettimeofday()
我不知道你是否会找到任何提供单纳秒分辨率的定时器 – 这取决于系统时钟的分辨率 – 但你可能想看看http://code.google.com/ p /高分辨率计时器/ 。 他们表示,他们可以在大多数Linux系统上提供微秒级别的分辨率,在Sun系统上提供纳秒级的分辨率。
在这种规模上制定基准并不是一个好主意。 你有足够的时间来获得最少的时间,如果你在纳秒上工作,这会导致你的结果不可靠。 您可以使用平台系统调用或更大规模的boost :: Date_Time [首选]。
你可以运行10 ^ 9次并秒表吗?
您可以使用标准系统调用,如gettimeofday,如果您确定您的进程在CPU时间内获得100%。 我可以想到很多情况,当你执行foo()时,其他线程和进程可能会窃取CPU时间。
你要求的是这种方式不可能的东西。 您需要硬件级别支持才能达到该级别的精度,甚至可以非常仔细地控制变量。 如果在运行代码时遇到中断会怎样? 如果操作系统决定运行其他一些代码怎么办?
你的代码做了什么? 它是否使用RAM内存? 如果您的代码和/或数据是否在缓存中,该怎么办?
在某些环境中,只要您控制这些变量,就可以将HW级别计数器用于此作业。 但是如何防止Linux中的上下文切换?
例如,在德州仪器的DSP工具(Code Composer Studio)中,您可以非常准确地分析代码,因为整个调试环境的设置使得仿真器(例如Blackhawk)接收有关每个操作运行的信息。 您还可以在某些处理器中设置直接编码到芯片内部HW块中的观察点。 这是有效的,因为内存通道也被路由到此调试块。
它们确实在CSL(芯片支持库)中提供了function,这些function是您要求的,其时序开销是几个周期。 但这仅适用于其处理器,完全取决于从HW寄存器读取定时器值。
我们都浪费时间重新创建这个测试样本。 为什么不发布编译就绪? 无论如何,这是我的结果。
CLOCK_PROCESS_CPUTIME_ID resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 459.427311 msec 0.110 microsec / call CLOCK_MONOTONIC resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 64.498347 msec 0.015 microsec / call CLOCK_REALTIME resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 65.494828 msec 0.016 microsec / call CLOCK_THREAD_CPUTIME_ID resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 427.133157 msec 0.102 microsec / call rdtsc 4194304 iterations : 115.427895 msec 0.028 microsec / call Dummy 16110479703957395943 rdtsc in milliseconds 4194304 iterations : 197.259866 msec 0.047 microsec / call Dummy 4.84682e+08 UltraHRTimerMs 197 HRTimerMs 197.26 #include #include #include #include #include #include enum { TESTRUNS = 1024*1024*4 }; class HRCounter { private: timespec start, tmp; public: HRCounter(bool init = true) { if(init) SetStart(); } void SetStart() { clock_gettime(CLOCK_MONOTONIC, &start); } double GetElapsedMs() { clock_gettime(CLOCK_MONOTONIC, &tmp); return (double)(tmp.tv_nsec - start.tv_nsec) / 1000000 + (tmp.tv_sec - start.tv_sec) * 1000; } }; __inline__ uint64_t rdtsc(void) { uint32_t lo, hi; __asm__ __volatile__ ( // serialize "xorl %%eax,%%eax \n cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx"); /* We cannot use "=A", since this would use %rax on x86_64 and return only the lower 32bits of the TSC */ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); return (uint64_t)hi << 32 | lo; } inline uint64_t GetCyclesPerMillisecondImpl() { uint64_t start_cyles = rdtsc(); HRCounter counter; std::this_thread::sleep_for (std::chrono::seconds(3)); uint64_t end_cyles = rdtsc(); double elapsed_ms = counter.GetElapsedMs(); return (end_cyles - start_cyles) / elapsed_ms; } inline uint64_t GetCyclesPerMillisecond() { static uint64_t cycles_in_millisecond = GetCyclesPerMillisecondImpl(); return cycles_in_millisecond; } class UltraHRCounter { private: uint64_t start_cyles; public: UltraHRCounter(bool init = true) { GetCyclesPerMillisecond(); if(init) SetStart(); } void SetStart() { start_cyles = rdtsc(); } double GetElapsedMs() { uint64_t end_cyles = rdtsc(); return (end_cyles - start_cyles) / GetCyclesPerMillisecond(); } }; int main() { auto Run = [](std::string const& clock_name, clockid_t clock_id) { HRCounter counter(false); timespec spec; clock_getres( clock_id, &spec ); printf("%s resolution: %ld sec %ld nano\n", clock_name.c_str(), spec.tv_sec, spec.tv_nsec ); counter.SetStart(); for ( int i = 0 ; i < TESTRUNS ; ++ i ) { clock_gettime( clock_id, &spec ); } double fb = counter.GetElapsedMs(); printf( "clock_gettime %d iterations : %.6f msec %.3f microsec / call\n", TESTRUNS, ( fb ), (( fb ) * 1000) / TESTRUNS ); }; Run("CLOCK_PROCESS_CPUTIME_ID",CLOCK_PROCESS_CPUTIME_ID); Run("CLOCK_MONOTONIC",CLOCK_MONOTONIC); Run("CLOCK_REALTIME",CLOCK_REALTIME); Run("CLOCK_THREAD_CPUTIME_ID",CLOCK_THREAD_CPUTIME_ID); { HRCounter counter(false); uint64_t dummy; counter.SetStart(); for ( int i = 0 ; i < TESTRUNS ; ++ i ) { dummy += rdtsc(); } double fb = counter.GetElapsedMs(); printf( "rdtsc %d iterations : %.6f msec %.3f microsec / call\n", TESTRUNS, ( fb ), (( fb ) * 1000) / TESTRUNS ); std::cout << "Dummy " << dummy << std::endl; } { double dummy; UltraHRCounter ultra_hr_counter; HRCounter counter; for ( int i = 0 ; i < TESTRUNS ; ++ i ) { dummy += ultra_hr_counter.GetElapsedMs(); } double fb = counter.GetElapsedMs(); double final = ultra_hr_counter.GetElapsedMs(); printf( "rdtsc in milliseconds %d iterations : %.6f msec %.3f microsec / call\n", TESTRUNS, ( fb ), (( fb ) * 1000) / TESTRUNS ); std::cout << "Dummy " << dummy << " UltraHRTimerMs " << final << " HRTimerMs " << fb << std::endl; } return 0; }