Diferença entre std :: system_clock e std :: stable_clock?

97

Qual é a diferença entre std::system_clocke std::steady_clock? (Um exemplo de caso que ilustre resultados / comportamentos diferentes seria ótimo).

Se meu objetivo é medir com precisão o tempo de execução de funções (como um benchmark), qual seria a melhor escolha entre std::system_clock, std::steady_clocke std::high_resolution_clock?

Vincent
fonte
9
Para começar, o system_clock pode não estar estável.
James McNellis
12
@CharlesSalvia Não posso falar por outras plataformas, mas o system_clockWindows não está estável. No Windows, a hora do sistema pode ser alterada para qualquer valor arbitrário por qualquer usuário com privilégios suficientes. Além disso, o serviço de sincronização de tempo pode ajustar a hora do sistema para trás, se necessário. Espero que a maioria das outras plataformas tenha recursos semelhantes que permitem o ajuste da hora do sistema.
James McNellis,
3
@Charles: A maioria das caixas POSIX que eu conheço são afetadas de forma semelhante e terão sua hora alterada se o usuário alterar a hora.
Billy ONeal,
5
Resposta em vídeo a esta pergunta: youtube.com/watch?v=P32hvk8b13M&t=48m44s
Howard Hinnant
1
@CharlesSalvia. De acordo com minha própria experiência, analisando a saída de tempo de dezenas de sistemas de aquisição de dados de PC, o tempo de um computador não é estável. Linux, Windows e as chamadas de sistema específicas usadas são desconhecidas, mas a semelhança são as diferenças de tempo negativas frequentes entre os valores de tempo subsequentes. O tempo em linha reta não é a norma.
Tyson Hilmer

Respostas:

71

De N3376:

20.11.7.1 [time.clock.system] / 1:

Os objetos de classe system_clockrepresentam o tempo do relógio de parede a partir do relógio de tempo real de todo o sistema.

20.11.7.2 [time.clock.steady] / 1:

Objetos de classe steady_clockrepresentam relógios para os quais os valores de time_pointnunca diminuem à medida que o tempo físico avança e para os quais os valores time_pointavançam a uma taxa constante em relação ao tempo real. Ou seja, o relógio pode não ser ajustado.

20.11.7.3 [time.clock.hires] / 1:

Objetos de classe high_resolution_clockrepresentam relógios com o período de tick mais curto. high_resolution_clockpode ser sinônimo de system_clockou steady_clock.

Por exemplo, o relógio amplo do sistema pode ser afetado por algo como o horário de verão, ponto no qual a hora real listada em algum momento no futuro pode realmente ser uma hora no passado. (Por exemplo, nos EUA, no outono o tempo retrocede uma hora, então a mesma hora é experimentada "duas vezes") No entanto, steady_clocknão é permitido ser afetado por tais coisas.

Outra maneira de pensar sobre "estável" neste caso está nos requisitos definidos na tabela de 20.11.3 [time.clock.req] / 2:

Na Tabela 59 C1e C2denotam os tipos de relógio. t1e t2são valores retornados por C1::now()onde o retorno da chamada t1ocorre antes do retorno da chamada t2e ambas as chamadas ocorrem antes C1::time_point::max(). [Nota: isso significa C1que não ocorreu entre t1e t2. —Enviar nota]

Expressão: C1::is_steady
Retorna: const bool
Semântica Operacional: truese t1 <= t2é sempre verdadeiro e o tempo entre os tiques do relógio é constante, caso contrário false.

Isso é tudo que o padrão tem sobre suas diferenças.

Se você quiser fazer benchmarking, sua melhor aposta provavelmente será std::high_resolution_clock, porque é provável que sua plataforma use um cronômetro de alta resolução (por exemplo, QueryPerformanceCounterno Windows) para este relógio. No entanto, se estiver fazendo um benchmarking, você realmente deve considerar o uso de timers específicos da plataforma para seu benchmark, porque plataformas diferentes lidam com isso de maneira diferente. Por exemplo, algumas plataformas podem fornecer alguns meios de determinar o número real de tiques do relógio que o programa requer (independente de outros processos em execução na mesma CPU). Melhor ainda, coloque as mãos em um criador de perfis de verdade e use-o.

Billy ONeal
fonte
1
@Charles: Importa-se de apontar na norma onde é esse o caso? Parece indicar claramente o oposto.
Billy ONeal,
9
@Charles: Além disso, o tempo POSIX não é "estável" - se o usuário alterar a configuração de tempo em seu computador, o tempo POSIX mudará. Se você estiver cozinhando um ovo e precisar de um cronômetro que dure 4 minutos, precisará que dure 4 minutos, mesmo que a hora atual seja alterada. Se você tem um cronômetro definido para uma reunião no dia 5 às 3, então você absolutamente precisa que esse cronômetro mude se o horário local mudar. Daí a diferença entre steady_clocke system_clockaqui.
Billy ONeal,
1
@ 5gon: Nada exige que system_clockseja UTC.
Billy ONeal
1
@CharlesSalvia Observe também que, como o tempo POSIX está vinculado ao UTC, o UTC tem segundos bissextos (cf. en.wikipedia.org/wiki/Unix_time#Leap_seconds ). Isso significa que mesmo se a hora em uma máquina nunca for ajustada, a hora C / POSIX pode ser não monotônica.
Michael Schlottke-Lakemper
3
UPDATE (Visual Studio 2015) A implementação de stable_clock mudou [.....] stable_clock agora é baseado em QueryPerformanceCounter () e high_resolution_clock agora é um typedef para stable_clock. Citado em msdn.microsoft.com/en-us/library/hh874757.aspx
felix-b
47

Billy deu uma ótima resposta com base no padrão ISO C ++ com o qual concordo totalmente. No entanto, há outro lado da história - a vida real. Parece que agora não há realmente nenhuma diferença entre esses relógios na implementação de compiladores populares:

gcc 4.8:

#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
   ...
#else
  typedef system_clock steady_clock;
#endif
  typedef system_clock high_resolution_clock;

Visual Studio 2012:

class steady_clock : public system_clock
{   // wraps monotonic clock
public:
  static const bool is_monotonic = true;    // retained
  static const bool is_steady = true;
};

typedef system_clock high_resolution_clock;

No caso do gcc você pode verificar se você lida com um relógio estável simplesmente verificando is_steady e se comportando de acordo. No entanto, o VS2012 parece trapacear um pouco aqui :-)

Se você precisa de um relógio de alta precisão, eu recomendo agora escrever seu próprio relógio em conformidade com a interface de relógio oficial do C ++ 11 e esperar que as implementações se atualizem. Será uma abordagem muito melhor do que usar a API específica do sistema operacional diretamente em seu código. Para Windows, você pode fazer assim:

// Self-made Windows QueryPerformanceCounter based C++11 API compatible clock
struct qpc_clock {
  typedef std::chrono::nanoseconds                       duration;      // nanoseconds resolution
  typedef duration::rep                                  rep;
  typedef duration::period                               period;
  typedef std::chrono::time_point<qpc_clock, duration>   time_point;
  static bool is_steady;                                                // = true
  static time_point now()
  {
    if(!is_inited) {
      init();
      is_inited = true;
    }
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    return time_point(duration(static_cast<rep>((double)counter.QuadPart / frequency.QuadPart *
                                                period::den / period::num)));
  }

private:
  static bool is_inited;                                                // = false
  static LARGE_INTEGER frequency;
  static void init()
  {
    if(QueryPerformanceFrequency(&frequency) == 0)
      throw std::logic_error("QueryPerformanceCounter not supported: " + std::to_string(GetLastError()));
  }
};

Para o Linux é ainda mais fácil. Basta ler a página do manual clock_gettimee modificar o código acima.

Mateusz Pusz
fonte
19
A implementação do VC ++ 2012 foi reconhecida como um bug pelo mantenedor da biblioteca padrão da MS.
ildjarn
5
Para os interessados, este é um link para esse bug
Ben Voigt
1
O Boost usa QueryPerformanceCounter, portanto, usar boost :: chrono é uma boa solução para esse bug até o lançamento do Visual Studio 14
Mohamed El-Nakib
E aqui estão as chamadas POSIX para as quais encaminhadas no GCC 5.3.0: stackoverflow.com/a/36700301/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
19

Implementação do GCC 5.3.0

C ++ stdlib está dentro da fonte GCC:

  • high_resolution_clock é um apelido para system_clock
  • system_clock encaminha para o primeiro dos seguintes que está disponível:
    • clock_gettime(CLOCK_REALTIME, ...)
    • gettimeofday
    • time
  • steady_clock encaminha para o primeiro dos seguintes que está disponível:
    • clock_gettime(CLOCK_MONOTONIC, ...)
    • system_clock

Então CLOCK_REALTIMEvs CLOCK_MONOTONICé explicado em: Diferença entre CLOCK_REALTIME e CLOCK_MONOTONIC?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fonte
2

Talvez a diferença mais significativa seja o fato de que o ponto de partida std::chrono:system_clocké 1.1.1970, a chamada época do UNIX. Por outro lado, std::chrono::steady_clocknormalmente para o tempo de inicialização do seu PC e é mais adequado para medir intervalos.

Silviu
fonte