C ++细粒度时间

时间:2012-11-20 09:04:48

标签: c++ c timer

以下代码将0作为函数的运行时。任何人都可以指出错误吗?

struct timeval start,end;
long seconds,useconds;
gettimeofday(&start, NULL);
int optimalpfs=optimal(n,ref,count);
gettimeofday(&end, NULL);
seconds  = end.tv_sec  - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
long opt_runtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
cout<<"\nOptimal Runtime is "<<opt_runtime<<"\n";

我得到的开始和结束时间都相同。我得到以下输出

Optimal Runtime is 0

请告诉我错误。

4 个答案:

答案 0 :(得分:1)

POSIX 1003.1b-1993指定clock_gettime()(和clock_getres())的接口,并提供MON选项,可以有一种clockid_t值为{CLOCK_MONOTONIC的时钟。 1}}(以便您的计时器不受系统时间调整的影响)。如果您的系统可用,那么这些函数会返回一个结构,其分辨率可能低至1纳秒,但后者的功能会告诉您时钟具有的分辨率。

 struct timespec {
         time_t  tv_sec;         /* seconds */
         long    tv_nsec;        /* and nanoseconds */
 };

您可能仍需要多次循环运行测试功能,以便时钟在超过其分辨率的任何时间进行注册,并且您可能希望运行循环足够的时间以至少持续一个数量级以上时间比时钟的分辨率。

请注意,显然Linux人员误读了POSIX.1b规范和/或不理解单调增加的时钟的定义,并且他们的CLOCK_MONOTONIC时钟受系统时间调整的影响,所以你必须使用他们发明的非标准CLOCK_MONOTONIC_RAW时钟来获得真正的单调时钟。

或者,可以使用相关的POSIX.1 timer_settime()调用来设置计时器运行,信号处理程序捕获计时器传递的信号,并timer_getoverrun()找出之间经过的时间。信号的排队及其最终传送,然后设置循环运行直到定时器关闭,计算设置的时间间隔内的迭代次数加上超限。

当然,对于抢占式多任务系统,这些时钟和计时器即使在您的进程未运行时也会运行,因此它们对于基准测试并不是非常有用。

稍微更为罕见的是clockid_t的可选POSIX.1-1999 CLOCK_PROCESS_CPUTIME_ID值,由来自_POSIX_CPUTIME的{​​{1}}表示,代表CPU-调用进程的时钟,给出表示调用进程的执行时间量的值。 (<time.h> clockid_t的TCT选项更为罕见,由CLOCK_THREAD_CPUTIME_ID宏表示,表示CPU时钟,给出表示调用线程执行时间的值。)

不幸的是,POSIX没有提到这些所谓的CPUTIME时钟是仅计算用户时间,还是由进程或线程累积的用户和系统(和中断)时间,所以如果你的代码分析下进行任何系统调用,那么在内核模式下花费的时间可能会或可能不会被表示。

更糟糕的是,在多处理器系统上,如果您的进程在执行期间从一个CPU迁移到另一个CPU,那么CPUTIME时钟的值可能完全是假的。实现这些CPUTIME时钟的定时器也可能在不同的CPU内核上以不同的速度运行,并且在不同的时间,进一步使它们的含义复杂化。即它们可能并不意味着与实际挂钟时间相关的任何内容,而只是表示CPU周期数(只要总是使用相对时间并且用户意识到执行时间可能会有所不同,这对于基准测试仍然有用取决于外部因素)。更糟糕的是,据报道,在Linux CPU上,基于TimeStampCounter的CPU时钟CPU甚至可以报告进程休眠的时间。

如果您的系统有良好的_POSIX_THREAD_CPUTIME系统调用,则希望能够为每个实际用户和系统时间分别提供getrusage() 在您的进程运行时使用它。然而,由于这会让你回到微秒钟,所以你需要反复运行你的测试代码足够多次以获得更准确的时序,在循环之前调用struct timeval一次,之后再调用,并计算给出的时间之间的差异。对于简单算法,这可能意味着运行数百万次或更多次。还要注意,在许多系统上,用户时间和系统时间之间的划分有些任意,如果在重复循环中单独检查,一个或另一个甚至可能看起来倒退。但是,如果您的算法没有进行系统调用,那么对时间增量求和应该仍然是代码执行的合理时间。

顺便说一句,在比较时间值时要小心,这样你就不会溢出或者在字段中以负值结束,无论是@Nim建议,还是像这样(来自NetBSD的getrusage()):< / p>
<sys/time.h>

(你甚至可能希望更加偏执, #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) 在范围内)

关于基准测试的另一个重要注意事项:确保实际调用函数,理想情况是检查编译器的汇编输出。在驱动程序循环的单独源模块中编译函数通常会说服优化器保持调用。另一个技巧是让它将你在循环中赋值的值返回到定义为tv_usec的变量。

答案 1 :(得分:0)

你在这里有一些奇怪的花车和冲浪组合:

long opt_runtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

尝试使用:

long opt_runtime = (long)(seconds * 1000 + (float)useconds/1000);

这样您就可以在几毫秒内得到结果。

答案 2 :(得分:0)

optimal(...)的执行时间小于gettimeofday(...)的粒度。这可能发生在Windows上。在Windows上,典型的粒度最长为20毫秒。我已经回答了相关的gettimeofday(...)问题here。 对于Linux,我问How is the microsecond time of linux gettimeofday() obtained and what is its accuracy?并得到了一个好结果。

有关如何获得准确时间的更多信息,请参阅this SO回答。

答案 3 :(得分:0)

我通常做这样的计算:

long long ss = start.tv_sec * 1000000LL + start.tv_usec;
long long es = end.tv_sec * 1000000LL + end.tv_usec;

然后做一个差异

long long microsec_diff = es - ss;

现在按要求转换:

double seconds = microsec_diff / 1000000.;

通常情况下,我不打扰最后一步,以微秒为单位做所有时间。