1

当我输出 的微秒字段时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);
4

3 回答 3

1

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

如果您(认为您)看到 1000000 或更大的值,那么是的,很可能您做错了什么。

一个常见的错误是天真地添加或减去两个值,而没有在and字段struct timeval之间实现适当的进位或借位,这很容易导致(错误和错误的)值大于 1000000。(在您编辑的帖子中,您提到减去时间规格,但是您使用的是系统提供的功能,应该可以正确借用。)tv_sectv_usectv_usectimersub

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

于 2018-05-26T23:04:14.227 回答
0

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

%zu格式用于打印size_t值。但既没有tv_sec也没有tv_usectype size_t

在现代系统上,size_t很可能是 64 位。但是,如果两者tv_sec都不tv_usec是,printf将会非常错误地打印这些值。

我把你printf的 s改成了

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_secand的特定选择tv_usec

于 2018-05-26T23:36:59.247 回答
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 timevaland的代码会发生这种情况timersub()

这段代码:

#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()错了”那么简单——这是一种巨大的解脱。

于 2018-05-26T23:15:41.620 回答