测量Linux上的增量时间时出现奇怪的结果

时间:2010-05-21 12:43:29

标签: c linux gettimeofday

更新:代码中的固定增量计算,问题仍然存在

伙计们,请您解释为什么我不时会使用以下代码得到非常奇怪的结果:

#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>

int main()
{
  struct timeval start, end;
  long mtime1, mtime2, diff;

  while(1)
  {
    gettimeofday(&start, NULL);

    usleep(2000);

    gettimeofday(&end, NULL);

    mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0);
    mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0);

    diff = mtime2 - mtime1;
    if(diff > 10) 
        printf("WTF: %ld\n", diff);
  }

  return 0;
}

(你可以编译并运行它: gcc test.c -o out -lrt&amp;&amp; ./out

我所经历的是差异变量的零星大值几乎每秒或更频繁,例如:

$ gcc test.c -o out -lrt && ./out 
WTF: 14
WTF: 11
WTF: 11
WTF: 11
WTF: 14
WTF: 13
WTF: 13
WTF: 11
WTF: 16

这怎么可能?是操作系统的责任吗?是否进行了太多的上下文切换?但我的盒子闲置了( 平均负荷:0.02,0.02,0.3)。

这是我的Linux内核版本:

$ uname -a
Linux kurluka 2.6.31-21-generic #59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux

5 个答案:

答案 0 :(得分:4)

睡眠功能只能确保您至少在一定时间内睡觉。由于Linux不是一个实时操作系统,你不能确定它只会睡觉你想要的时间。这是一个问题,因为你不能指望这个值。正如你所指出的那样,睡眠时间真的很大。

Linux调度程序无法保证。使用实时操作系统,您可以获得该功能。

你的公式在某种程度上是错误的,但我认为这不是你有如此大的睡眠时间的原因。我用这个片段检查了两个公式,结果相同:

#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>

int main()
{
  struct timeval start, end;
  long mtime, mtime2, start_time, end_time, seconds, useconds;

  while(1)
  {
    gettimeofday(&start, NULL);

    usleep(2000);

    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

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

    start_time = ((start.tv_sec) * 1000 + start.tv_usec/1000.0) + 0.5;
    end_time = ((end.tv_sec) * 1000 + end.tv_usec/1000.0) + 0.5;

    mtime2 = end_time - start_time;

    if(mtime > 10 || mtime2 > 10)
    {
      printf("WTF: %ld\n", mtime);
      printf("WTF2: %ld\n", mtime2);
    }
  }

  return 0;
}

结果:

$ gcc test.c -o out -lrt && ./out
WTF: 11
WTF2: 12
WTF: 21
WTF2: 21

我认为这是错误的,因为useconds部分是循环的并且可能导致很大的负面差异。但它不会导致你使用签名长整数这么大的时间......

my2cents

编辑:来自man nanosleep:

  

当前的实施   nanosleep()基于正常   内核计时器机制,它有一个   分辨率为1 / HZ s(见时间(7))。   因此,nanosleep()总是暂停   至少在指定的时间内,   但是它可能需要长达10毫秒   直到过程指定   再次变得可以运行。对于相同的   原因,在以下情况下返回的值   * rem中的传递信号通常是   四舍五入到下一个更大的倍数   1 / HZ s。

答案 1 :(得分:2)

我的猜测是它与操作系统有关。尝试以实时优先级运行该进程(请参阅chrt程序),看看是否有帮助。

另一方面,你正在计算错误的mtime。这是'我使用的例程,虽然它是struct timespec而不是struct timeval(纳秒而不是微秒),原则应该是明确的:

timespec diff(timespec start, timespec end)
{
    timespec temp;
    if ((end.tv_nsec - start.tv_nsec) < 0) {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    } else {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
    }
    return temp;
}

答案 2 :(得分:1)

找到它,参见手册页

http://linux.die.net/man/3/usleep

  

以系统定时器的粒度。

这是10毫秒afaik。因此,usleep可以在重新安排流程之前很久就会过期。

它与你得到的值一致,它们是“正常”时间片的数量级。

答案 3 :(得分:0)

你的公式错了。您必须以相同的比例转换两次。在你的例子中。

double mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0) ;
double mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0) ;

double diff = mtime2 - mtime1;
if(diff > 10) 
  printf("WTF: %ld\n", diff);

您必须减去更正后的值

示例:t1 = 1.999999 t2 = 2.000001所以间隔为2μs

使用您的公式计算:

2 - 1 == 11 - 9999999给出(1 * 1000 - 999998 / 1000) + 0.5 ==的结果 0.502这显然是假的。

我的方法给出:

mtime1 = (1 * 1000 + 999999 / 1000) = 1999.999
mtime2 = (2 * 1000 +      1 / 1000) = 2000.001

2000.001 - 1999.999 = 0.002 ms

答案 4 :(得分:0)

我已经做过这样的措施,我的结论完全相同。在Windows和Linux上同样发生。

在10 ^ -n秒内构建直方图的程序会得到以下结果。

0.1 0
0.01 0
0.001 0
0.0001 2
1e-05 24
1e-06 69726023
Total: 69726049
Duration: 6.47403 seconds.
Average: 0.0928495 microseconds.

但请注意,这是一个全新的系统。我记得一年前在2004系统上使用它,并且在0.01范围内(10毫秒以上)每秒有几次点击。