Por que o tempo relatado por time () às vezes é 1 segundo atrás do componente segundos de timespec_get () no código C?

12

O seguinte trecho de código:

struct timespec ts;
for (int x = 0; x < 100000000; x++) {
    timespec_get(&ts, TIME_UTC);
    long cTime = (long) time(NULL);
    if (cTime != ts.tv_sec && ts.tv_nsec < 3000000) {
        printf("cTime: %ld\n", cTime);
        printf("ts.tv_sec: %ld\n", ts.tv_sec);
        printf("ts.tv_nsec: %ld\n", ts.tv_nsec);
    }
}

produz esta saída:

...
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2527419
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2534036
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2540359
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2547039
...

Por que a discrepância entre cTimee ts.tv_sec? Observe que o problema não ocorre se a condicional for alterada para ts.tv_nsec >= 3000000. O problema depende dos nanossegundos serem menores que 3000000.

Theo d'Or
fonte
Você precisa ser mais específico sobre o sistema operacional usado, versão dele, versão da biblioteca C usada.
Algum programador
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or
O que é timespec_get()? Isso é C ou C ++? Parece que sim std::timespec_get. Por favor, use a etiqueta apropriada.
Marco Bonelli
@MarcoBonelli: Foi adicionado ao C no C11. Pode se reproduzir online .
ShadowRanger 17/01
@ ShadowRanger obrigado pela referência, não consegui ver uma manentrada timespec_getno meu sistema, então tirei conclusões precipitadas. Faz sentido.
Marco Bonelli

Respostas:

11

O motivo é que você (implicitamente) usa diferentes relógios do sistema. timespec_get()usa o relógio em tempo real de alta resolução em todo o sistema, enquanto time()usa o relógio em tempo real aproximado .

Tente usar

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

em vez de você timespec_get(), a diferença deve desaparecer.

Editar:

Isso pode ser visto na fonte do kernel do Linux, vclock_gettime.c

De fato, a questão é um pouco sutil de se ver aqui. A parte dos segundos dos membros da estrutura usada por CLOCK_REALTIME_COARSEe CLOCK_REALTIMEcontém valores idênticos, mas a parte dos nanossegundos é diferente; com CLOCK_REALTIMEele pode ser maior que 1000000000(que é um segundo). Nesse caso, é corrigido na chamada:

ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;

Esta correção não é realizada com CLOCK_REALTIME_COARSE, nem com time(). Isso explica a diferença entre CLOCK_REALTIMEe time().

Ctx
fonte
Isso está documentado em algum lugar, ou apenas é um artefato de timeser implementado com o (presumivelmente) relógio com melhor desempenho, mas menos preciso (na teoria de que ele só tem segunda granularidade de qualquer maneira, então quem precisa de precisão)? Atrasar o tempo real em um milissegundo mais ou menos (testes on-line mostraram um atraso ocasional de mais de um ms, mas não muito mais) quando você está apenas pedindo uma segunda granularidade não é tão importante, eu acho.
ShadowRanger 17/01
@ShadowRanger eu adicionei mais alguns detalhes
Ctx
Documentação não explícita da intenção, mas isso é detalhe suficiente para uma votação antecipada. :-) Engraçado que o relógio possa realmente relatar mais de um segundo de nanossegundos adicionais.
ShadowRanger 17/01
@ShadowRanger Não consegui encontrar uma documentação real além da fonte, o que também significa que o comportamento também pode ser alterado em detalhes sem aviso prévio
Ctx 17/01
@ CTX Obrigado pela resposta detalhada! Usarei timespec_get () em vez de clock_gettime () que você aconselha, pois timespec_get () é C11 em vez de POSIX e não requer a configuração de qual relógio usar. Eu não tinha idéia de que relógios diferentes fossem usados, mas, dada a escolha, não vejo muito sentido em usar o relógio grosso.
Theo d'Or