gettimeofday () микросекунда не ограничена до секунды - PullRequest
0 голосов
/ 27 мая 2018

Когда я вывожу поле микросекунд для 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 голосов
/ 27 мая 2018

Вам нужно будет показать более убедительный код и определить платформу, на которой вы столкнулись с проблемой.

Например:

#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 я могу найти файл /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 timeval и 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() не так "- что является огромным облегчением.

0 голосов
/ 27 мая 2018

Ваши форматы printf являются подозрительными и могут вызывать эту проблему.

Формат %zu предназначен для печати значений size_t.Но ни tv_sec, ни tv_usec не имеют типа 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_sec и tv_usec.

0 голосов
/ 27 мая 2018

После успешного завершения до gettimeofday, да, tv_usec гарантированно будет строго меньше, чем 1000000.

Если вы (кажется, вы) видели значение 1000000 или выше, тогда да, вероятно,вы делали что-то не так.

Частая ошибка - наивно складывать или вычитать два значения struct timeval, не применяя правильный перенос или заимствование между полями tv_sec и tv_usec, и это может легко привести к(ошибочно и неправильно) значения в tv_usec больше, чем 1000000. (В вашем отредактированном посте вы упомянули вычитание временных параметров, но вы используете предоставляемую системой функцию timersub, которая должна обеспечить правильное заимствование.)

Если вы использовали struct timespec вместо struct timeval, и , если происходила дополнительная секунда, и , если , вы (чудесным образом) использовали ядро ​​ОС, которое реализовалоCLOCK_UTC тип часов, предложенный Маркусом Куном на https://www.cl.cam.ac.uk/~mgk25/posix-clocks.html,, вы увидите tv_nsec значения, превышающие 1000000000, но это много "если".(И, насколько мне известно, ни одно ядро, получившее широкое распространение, никогда не реализовывало CLOCK_UTC.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...