O System.nanoTime () é completamente inútil?

Conforme documentado no post do blog Beware de System.nanoTime () em Java , em sistemas x86, o System.nanoTime () do Java retorna o valor de tempo usando um contador específico da CPU . Agora considere o seguinte caso que eu uso para medir o tempo de uma chamada:

long time1= System.nanoTime(); foo(); long time2 = System.nanoTime(); long timeSpent = time2-time1; 

Agora, em um sistema multi-core, pode ser que, após a medição do tempo1, o encadeamento esteja programado para um processador diferente, cujo contador seja menor que o da CPU anterior. Assim, poderíamos obter um valor no tempo2 que é menor que o tempo1. Assim, obteríamos um valor negativo em timeSpent.

Considerando este caso, não é que System.nanotime é praticamente inútil por enquanto?

Eu sei que mudar o horário do sistema não afeta o nanotempo. Esse não é o problema que descrevo acima. O problema é que cada CPU manterá um contador diferente desde que foi ligado. Este contador pode ser menor na segunda CPU em comparação com a primeira CPU. Como o encadeamento pode ser programado pelo sistema operacional para a segunda CPU depois de receber o tempo1, o valor de timeSpent pode estar incorreto e até negativo.

Esse post está errado e o nanoTime está seguro. Há um comentário no post com links para uma postagem no blog de David Holmes , um cara em tempo real e concorrente da Sun. Diz:

System.nanoTime () é implementado usando a API QueryPerformanceCounter / QueryPerformanceFrequency […] O mecanismo padrão usado pelo QPC é determinado pela Camada de Abstração de Hardware (HAL) […] Esse padrão não muda apenas em hardware, mas também em SO versões. Por exemplo, o Windows XP Service Pack 2 mudou as coisas para usar o timer de gerenciamento de energia (PMTimer) em vez do contador de timestamp do processador (TSC) devido a problemas com o TSC não estar sincronizado em processadores diferentes em sistemas SMP e devido a sua freqüência pode variar (e, portanto, sua relação com o tempo decorrido) com base nas configurações de gerenciamento de energia.

Então, no Windows, isso foi um problema até o WinXP SP2, mas não é agora.

Não consigo encontrar uma parte II (ou mais) que fale sobre outras plataformas, mas esse artigo inclui uma observação que o Linux encontrou e resolveu o mesmo problema da mesma forma, com um link para o FAQ para clock_gettime (CLOCK_REALTIME) , que diz:

  1. Clock_gettime (CLOCK_REALTIME) é consistente em todos os processadores / núcleos? (Arco importa? Por exemplo, ppc, arm, x86, amd64, sparc).

Deve ou é considerado um buggy.

No entanto, em x86 / x86_64, é possível ver que os TSCs não sincronizados ou com frequência variável causam inconsistências de tempo. Os kernels 2.4 realmente não tinham proteção contra isso, e os primeiros kernels 2.6 também não funcionaram muito bem aqui. A partir do ponto 2.6.18, a lógica para detectar isso é melhor e, normalmente, recorreremos a uma fonte de clocks segura.

O ppc sempre tem uma base de tempo sincronizada, então isso não deve ser um problema.

Então, se o link de Holmes puder ser lido como implicando que nanoTime chama clock_gettime(CLOCK_REALTIME) , então é seguro desde o kernel 2.6.18 no x86, e sempre no PowerPC (porque a IBM e a Motorola, ao contrário da Intel, sabem projetar microprocessadores).

Não há menção de SPARC ou Solaris, infelizmente. E, claro, não temos ideia do que as IBM JVMs fazem. Mas as Sun JVMs no Windows moderno e no Linux acertam isso.

EDIT: Esta resposta é baseada nas fonts citadas. Mas eu ainda me preocupo que isso possa estar completamente errado. Algumas informações mais atualizadas seriam realmente valiosas. Acabei de encontrar um link para um artigo mais recente de quatro anos sobre os relógios do Linux que poderia ser útil.

Fiz um pouco de pesquisa e descobri que se alguém está sendo pedante, então sim, pode ser considerado inútil … em situações particulares … depende de quão sensíveis são as suas necessidades …

Confira esta citação do site Java Sun:

O relógio de tempo real e System.nanoTime () são baseados na mesma chamada de sistema e, portanto, no mesmo relógio.

Com o Java RTS, todas as APIs baseadas no tempo (por exemplo, Timers, Threads Periódicos, Monitoramento de Prazo Final e assim por diante) são baseadas no timer de alta resolução. E, juntamente com prioridades em tempo real, eles podem garantir que o código apropriado seja executado no momento certo para restrições em tempo real. Em contraste, as APIs Java SE comuns oferecem apenas alguns methods capazes de lidar com tempos de alta resolução, sem garantia de execução em um determinado momento. Usar System.nanoTime () entre vários pontos no código para executar medições de tempo decorrido sempre deve ser preciso.

Java também tem uma ressalva para o método nanoTime () :

Este método só pode ser usado para medir o tempo decorrido e não está relacionado a nenhuma outra noção de tempo do sistema ou do relógio de parede. O valor retornado representa nanossegundos desde algum tempo fixo mas arbitrário (talvez no futuro, portanto, os valores podem ser negativos). Esse método fornece precisão de nanossegundos, mas não necessariamente precisão de nanossegundos. Nenhuma garantia é feita sobre a frequência com que os valores mudam. As diferenças nas chamadas sucessivas que abrangem mais de aproximadamente 292,3 anos (2 63 nanossegundos) não calcularão com precisão o tempo decorrido devido ao estouro numérico.

Parece que a única conclusão que pode ser tirada é que nanoTime () não pode ser invocada como um valor preciso. Como tal, se você não precisa medir tempos que são meros nano segundos separados, então este método é bom o suficiente mesmo se o valor retornado resultante for negativo. No entanto, se você precisar de maior precisão, eles parecem recomendar que você use o JAVA RTS.

Então, para responder à sua pergunta … não nanoTime () não é inútil …. apenas não é o método mais prudente para usar em todas as situações.

Não há necessidade de debater, basta usar a fonte. Aqui, SE 6 para Linux, faça suas próprias conclusões:

 jlong os::javaTimeMillis() { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "linux error"); return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); } jlong os::javaTimeNanos() { if (Linux::supports_monotonic_clock()) { struct timespec tp; int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp); assert(status == 0, "gettime error"); jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); return result; } else { timeval time; int status = gettimeofday(&time, NULL); assert(status != -1, "linux error"); jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec); return 1000 * usecs; } } 

Isenção de responsabilidade: Eu sou o desenvolvedor desta biblioteca

Você pode gostar disso melhor:

http://juliusdavies.ca/nanotime/

Mas copia um arquivo DLL ou Unix .so (object compartilhado) no diretório pessoal do usuário atual para que ele possa chamar JNI.

Algumas informações básicas estão no meu site em:

http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html

O Linux corrige as discrepâncias entre as CPUs, mas o Windows não. Eu sugiro que você assuma System.nanoTime () só é preciso para cerca de 1 micro-segundo. Uma maneira simples de obter uma temporização mais longa é chamar foo () 1.000 ou mais vezes e dividir a hora por 1.000.

Absolutamente não é inútil. Os aficionados por tempo apontam corretamente o problema multi-core, mas em aplicações de palavras reais muitas vezes é radicalmente melhor que currentTimeMillis ().

Ao calcular as posições dos charts no quadro atualiza nanoTime () leva a um movimento MUITO mais suave no meu programa.

E eu só testo em máquinas multi-core.

Eu tenho visto um tempo decorrido negativo relatado de usar System.nanoTime (). Para ser claro, o código em questão é:

  long startNanos = System.nanoTime(); Object returnValue = joinPoint.proceed(); long elapsedNanos = System.nanoTime() - startNanos; 

e variável ‘elapsedNanos’ teve um valor negativo. (Eu tenho certeza que a chamada intermediária levou menos de 293 anos também, que é o ponto de estouro para nanos armazenados em longs 🙂

Isso ocorreu usando um IBM v1.5 JRE 64 bits no hardware IBM P690 (multi-core) executando o AIX. Eu só vi esse erro ocorrer uma vez, por isso parece extremamente raro. Eu não sei a causa – é um problema específico de hardware, um defeito da JVM – eu não sei. Eu também não sei as implicações para a precisão do nanoTime () em geral.

Para responder à pergunta original, eu não acho que o nanoTime é inútil – ele fornece um tempo de sub-milissegundo, mas há um risco real (não apenas teórico) de ser impreciso que você precisa levar em conta.

Estou ligando para o que essencialmente é a mesma discussão em que Peter Lawrey está fornecendo uma boa resposta. Por que recebo um tempo decorrido negativo usando System.nanoTime ()?

Muitas pessoas mencionaram que em Java System.nanoTime () poderia retornar tempo negativo. Eu peço desculpas por repetir o que outras pessoas já disseram.

  1. nanoTime () não é um contador de ciclos de clock, mas de CPU.
  2. O valor de retorno é dividido por frequência para parecer com o tempo.
  3. A freqüência da CPU pode flutuar.
  4. Quando o seu segmento está agendado em outra CPU, há uma chance de obter nanoTime () que resulta em uma diferença negativa. Isso é lógico. Contadores em CPUs não são sincronizados.
  5. Em muitos casos, você pode obter resultados bastante enganosos, mas não seria capaz de dizer, porque o delta não é negativo. Pense nisso.
  6. (não confirmado) Eu acho que você pode obter um resultado negativo mesmo na mesma CPU se as instruções forem reordenadas. Para evitar isso, você teria que invocar uma barreira de memory serializando suas instruções.

Seria legal se System.nanoTime () retornasse coreID onde ele foi executado.

Isso não parece ser um problema em um Core 2 Duo que executa o Windows XP e o JRE 1.5.0_06.

Em um teste com três threads não vejo System.nanoTime () indo para trás. Os processadores estão ocupados e os threads vão dormir ocasionalmente para provocar os fios em movimento.

[EDIT] Eu diria que isso só acontece em processadores fisicamente separados, ou seja, que os contadores são sincronizados para múltiplos núcleos no mesmo dado.

O Java é crossplataforma, e o nanoTime é dependente da plataforma. Se você usa Java – quando não usa nanoTime. Eu encontrei bugs reais em diferentes implementações de jvm com essa function.

Não, não é … Apenas depende da sua CPU, verifique o Temporizador de Eventos de Alta Precisão para saber como / por que as coisas são tratadas diferentemente de acordo com a CPU.

Basicamente, leia a fonte do seu Java e verifique o que sua versão faz com a function, e se ela funcionar contra a CPU, você a executará.

A IBM até sugere que você a use para benchmarking de desempenho (um post de 2008, mas atualizado).

A documentação do Java 5 também recomenda o uso desse método para o mesmo propósito.

Este método só pode ser usado para medir o tempo decorrido e não está relacionado a nenhuma outra noção de tempo do sistema ou do relógio de parede.

Documento da API do Java 5

Além disso, System.currentTimeMillies() muda quando você altera o relógio do sistema, enquanto System.nanoTime() não, portanto, o último é mais seguro para medir durações.

nanoTime é extremamente inseguro para o timing. Eu tentei em meus algoritmos básicos de teste de primalidade e deu respostas que foram literalmente separadas por um segundo para a mesma input. Não use esse método ridículo. Eu preciso de algo que seja mais preciso e preciso do que obter o tempo millis, mas não tão ruim quanto o nanoTime .