gettimeofday()微秒不限于秒以下

时间:2018-05-26 22:59:49

标签: c time gettimeofday

当我输出gettimeofday()的微秒字段时,我注意到微秒字段大于1,000,000。有人知道为什么吗?这是否意味着我一直在解释gettimeofday()错误?

对于记录,我的假设是根据gettimeofday()的当前时间(以微秒为单位)如下:

struct timeval ts;
gettimeofday(&ts, NULL); 
printf("%zu", ts.tv_sec * 1000000 + ts.tv_usec);

编辑:这是导致问题的代码。在下面的注释之后,printf()可能出错了。

struct timeval curr_time;
gettimeofday(&curr_time, NULL);
printf("Done-arino! Onto the matrix multiplication (at %zu s, %03zu ms)\n", curr_time.tv_sec, curr_time.tv_usec);

// Matrix Multiplication
struct timeval start_tv, end_tv, elapsed_tv;
gettimeofday(&start_tv, NULL);
for (i = 0; i < N; i++)
    for (j = 0; j < N; j++)
        for (k = 0; k < N; k++)
            C[i][j] += A[i][k] * B[k][j];
gettimeofday(&end_tv, NULL);
timersub(&end_tv, &start_tv, &elapsed_tv);

// Print results
printf("Elapsed time: %zu s, %03zu ms\n", elapsed_tv.tv_sec, elapsed_tv.tv_usec / 1000);

3 个答案:

答案 0 :(得分:0)

成功到gettimeofday后,是的,tv_usec保证严格小于1000000。

如果你(认为你)看到1000000或更高的值,那么是的,你可能做错了。

一个常见的错误是天真地添加或减去两个struct timeval值,而没有在tv_sectv_usec字段之间实现正确的携带或借用,这很容易导致(错误和tv_usec中的值大于1000000。(在您编辑的帖子中,您提到减去时间规格,但您正在使用系统提供的timersub函数,该函数应该获得正确的借用。)

如果你使用的是struct timespec而不是struct timeval,而如果正在进行闰秒,而如果则是(奇迹般的) )使用一个OS内核实现了Markus Kuhn在https://www.cl.cam.ac.uk/~mgk25/posix-clocks.html提出的CLOCK_UTC时钟类型,你会看到tv_nsec值大于1000000000,但那是很多“if”s。 (据我所知,没有广泛使用的内核曾经实现过CLOCK_UTC。)

答案 1 :(得分:0)

您需要展示一些更有说服力的代码,并找出遇到此问题的平台。

例如:

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

int main(void)
{
    while (1)
    {
        struct timeval ts;
        if (gettimeofday(&ts, 0) == 0 && ts.tv_usec >= 1000000)
            printf("%lu s; %lu µs\n", (long)ts.tv_sec, (long)ts.tv_usec); 
    }
    return 0;
}

非常繁忙的循环有点令人讨厌;也许你应该在每次迭代时使用nanosleep()睡一微秒或两秒:

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

int main(void)
{
    while (1)
    {
        struct timeval tv;
        if (gettimeofday(&tv, 0) == 0 && tv.tv_usec >= 1000000)
            printf("%lu s; %lu µs\n", (long)tv.tv_sec, (long)tv.tv_usec); 
        struct timespec ts = { .tv_sec = 0, .tv_nsec = 2000 };
        nanosleep(&ts, 0);
    }
    return 0;
}

或者,包括一个进度表来证明代码正在运行:

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

    int main(void)
    {
        size_t loop_count = 0;
        size_t line_count = 0;

        while (1)
        {
            struct timeval tv;
            if (gettimeofday(&tv, 0) == 0 && tv.tv_usec >= 1000000)
                printf("%lu s; %lu µs\n", (long)tv.tv_sec, (long)tv.tv_usec); 
            struct timespec ts = { .tv_sec = 0, .tv_nsec = 2000 };
            nanosleep(&ts, 0);
            if (++loop_count > 100000)
            {
                loop_count = 0;
                putchar('.');
                line_count++;
                if (line_count >= 50)
                {
                    putchar('\n');
                    line_count = 0;
                }
                fflush(stdout);
            }
        }
        return 0;
    }

timersub()

在Ubuntu 16.04 LTS VM上,我可以找到包含宏的文件/usr/include/x86_64-linux-gnu/sys/time.h

# define timersub(a, b, result)                                               \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if ((result)->tv_usec < 0) {                                              \
      --(result)->tv_sec;                                                     \
      (result)->tv_usec += 1000000;                                           \
    }                                                                         \
  } while (0)

我可以看到的所有迹象都是tv_usec__u32,是无符号数量。如果是这种情况,则< 0条件永远不会是真的,你有时可能会看到奇怪的大正值。当然是YMMV。

即使我的里程也不尽相同

进一步审查表明,虽然__u32似乎使用tv_usec标题,但这些标题不是主系统标题。

/usr/include/linux/time.h:  __kernel_suseconds_t     tv_usec;   /* microseconds */
/usr/include/linux/can/bcm.h:   long tv_usec;
/usr/include/drm/exynos_drm.h:  __u32           tv_usec;
/usr/include/drm/exynos_drm.h:  __u32           tv_usec;
/usr/include/drm/vmwgfx_drm.h:  uint32_t tv_usec;
/usr/include/drm/drm.h: __u32 tv_usec;
/usr/include/rpc/auth_des.h:    uint32_t tv_usec;           /* Microseconds.  */
/usr/include/valgrind/vki/vki-darwin.h:#define vki_tv_usec tv_usec
/usr/include/valgrind/vki/vki-linux.h:  vki_suseconds_t tv_usec;    /* microseconds */
/usr/include/rpcsvc/rstat.x:    unsigned int tv_usec;   /* and microseconds */
/usr/include/rpcsvc/rstat.h:    u_int tv_usec;
/usr/include/x86_64-linux-gnu/bits/utmpx.h:    __int32_t tv_usec;       /* Microseconds.  */
/usr/include/x86_64-linux-gnu/bits/time.h:    __suseconds_t tv_usec;    /* Microseconds.  */
/usr/include/x86_64-linux-gnu/bits/utmp.h:    int32_t tv_usec;      /* Microseconds.  */
/usr/include/x86_64-linux-gnu/sys/time.h:   (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
/usr/include/x86_64-linux-gnu/sys/time.h:   (tv)->tv_usec = (ts)->tv_nsec / 1000;                           \
/usr/include/x86_64-linux-gnu/sys/time.h:# define timerisset(tvp)   ((tvp)->tv_sec || (tvp)->tv_usec)
/usr/include/x86_64-linux-gnu/sys/time.h:# define timerclear(tvp)   ((tvp)->tv_sec = (tvp)->tv_usec = 0)
/usr/include/x86_64-linux-gnu/sys/time.h:   ((a)->tv_usec CMP (b)->tv_usec) :                         \
/usr/include/x86_64-linux-gnu/sys/time.h:    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec;                 \
/usr/include/x86_64-linux-gnu/sys/time.h:    if ((result)->tv_usec >= 1000000)                        \
/usr/include/x86_64-linux-gnu/sys/time.h:   (result)->tv_usec -= 1000000;                         \
/usr/include/x86_64-linux-gnu/sys/time.h:    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                 \
/usr/include/x86_64-linux-gnu/sys/time.h:    if ((result)->tv_usec < 0) {                         \
/usr/include/x86_64-linux-gnu/sys/time.h:      (result)->tv_usec += 1000000;                          \

令人担心的是,对于具有该名称的成员,任何代码都使用无符号类型,但这并不意味着代码使用struct timevaltimersub()

此代码:

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

int main(void)
{
    struct timeval t = { .tv_sec = 0, .tv_usec = -1 };
    printf("%ld %ld\n", (long)t.tv_sec, (long)t.tv_usec);
    return 0;
}

编译为64位(因此long足够大,可以保存tv_usec可以定义的任何内容)打印0 -1。可以将tv_usec成员初始化为0,递减它,并验证它是否定的,以及各种其他相关测试。

所以,问题并不像“timersub()错误”那么简单 - 这是一种巨大的解脱。

答案 2 :(得分:0)

您的printf格式是可疑的,可能会导致此问题。

%zu格式用于打印size_t值。但tv_sectv_usec都没有size_t类型。

在现代系统中,size_t可能是64位。但如果tv_sectv_usec不是printfprintf将错误地打印这些值。

我将您的printf("Done-arino! Onto the matrix multiplication (at %ld s, %03u ms)\n", curr_time.tv_sec, curr_time.tv_usec); 更改为

printf("Elapsed time: %ld s, %03u ms\n",
        elapsed_tv.tv_sec, elapsed_tv.tv_usec / 1000);

tv_sec

但这些对您来说不一定是正确的 - 这取决于您的系统对tv_usecvar app = angular.module('app', []); app.component('foo', { template: '<input ng-model="$ctrl.fooValue" enter-key="$ctrl.onEnter()" />', controller: function() { this.onEnter = function() { // this.fooValue is not updated with input value }; }, bindings: { fooValue: '@' } }); app.directive('enterKey', function() { return { restrict: 'A', link: function(scope, element, attrs) { element.bind("keydown keypress", function(event) { var keyCode = event.which || event.keyCode; // If enter key is pressed if (keyCode === 13) { scope.$apply(function() { // Evaluate the expression scope.$eval(attrs.enterKey); }); } }); } }; }); describe('test', function() { describe('foo', function() { beforeEach(module('app')); var element, inputElem; beforeEach(inject(function($compile, $rootScope) { var fooScope = $rootScope.$new(); element = angular.element('<foo foo-value="foo1"></foo>'); element = $compile(element)(fooScope); fooScope.$digest(); })); it('should set fooValue with foo3', function() { var controller = element.controller('foo'); inputElem = element.find('input'); inputElem.val('foo3'); inputElem.triggerHandler({ type: 'keydown', which: 13 }); expect(controller.fooValue).toBe('foo3'); }); }); });的具体选择。