前言,我在使用gcc的Unix(linux)系统上。
我所坚持的是如何准确地实现在一段时间内运行代码段的方法。
以下是我一直在使用的一个例子:
struct timeb start, check;
int64_t duration = 10000;
int64_t elapsed = 0;
ftime(&start);
while ( elapsed < duration ) {
// do a set of tasks
ftime(&check);
elapsed += ((check.time - start.time) * 1000) + (check.millitm - start.millitm);
}
我原本以为这会持续10000毫秒或10秒,但它几乎不会立即发生。我基于How to get the time elapsed in C in milliseconds? (Windows)之类的其他问题。但后来我认为,如果第一次调用ftime,结构为time = 1, millitm = 999
,而在第二次调用time = 2, millitm = 01
时,它将计算经过的时间为1002毫秒。有什么我想念的吗?
各种stackoverflow问题中的建议ftime()和gettimeofday()也被列为已弃用或遗留。
我相信我可以将开始时间转换为毫秒,将检查时间转换为毫秒,然后从检查中减去开始。但是,自纪元以来毫秒需要42位,我试图尽可能高效地保持循环中的所有内容。
我可以采取什么方法来解决这个问题?
答案 0 :(得分:3)
计算经过时间的代码不正确。
// elapsed += ((check.time - start.time) * 1000) + (check.millitm - start.millitm);
elapsed = ((check.time - start.time) * (int64_t)1000) + (check.millitm - start.millitm);
check.millitm - start.millitm
存在一些问题。在struct timeb *tp
的系统上,可以预期在减法发生之前millitm
将被提升为int
。因此差异将在[-1000 ... 1000]
范围内。
struct timeb {
time_t time;
unsigned short millitm;
short timezone;
short dstflag;
};
IMO,更强大的代码将在单独的辅助函数中处理ms转换。这符合OP的“我相信我可以将开始时间转换为毫秒,将检查时间转换为毫秒,然后从检查中减去开始。”
int64_t timeb_to_ms(struct timeb *t) {
return (int64_t)t->time * 1000 + t->millitm;
}
struct timeb start, check;
ftime(&start);
int64_t start_ms = timeb_to_ms(&start);
int64_t duration = 10000 /* ms */;
int64_t elapsed = 0;
while (elapsed < duration) {
// do a set of tasks
struct timeb check;
ftime(&check);
elapsed = timeb_to_ms(&check) - start_ms;
}
答案 1 :(得分:0)
如果您想要效率,请在计时器到期时让系统向您发送信号。
传统上,您可以使用alarm(2)系统调用设置一个分辨率为秒的计时器。
系统然后在计时器到期时向您发送SIGALRM。该信号的默认处置是终止。
如果你处理信号,你可以longjmp(2)从处理程序到另一个地方。
我不认为它比SIGALRM + longjmp更有效率(使用异步计时器,您的代码基本上不受干扰地运行而无需进行任何额外的检查或调用)。
以下是您的示例:
#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
static jmp_buf jmpbuf;
void hndlr();
void loop();
int main(){
/*sisv_signal handlers get reset after a signal is caught and handled*/
if(SIG_ERR==sysv_signal(SIGALRM,hndlr)){
perror("couldn't set SIGALRM handler");
return 1;
}
/*the handler will jump you back here*/
setjmp(jmpbuf);
if(0>alarm(3/*seconds*/)){
perror("couldn't set alarm");
return 1;
}
loop();
return 0;
}
void hndlr(){
puts("Caught SIGALRM");
puts("RESET");
longjmp(jmpbuf,1);
}
void loop(){
int i;
for(i=0; ; i++){
//print each 100-milionth iteration
if(0==i%100000000){
printf("%d\n", i);
}
}
}
如果alarm(2)不够,您可以使用timer_create(2)作为EOF建议。