2009-10-14 6 views
2

Un de mes amis m'a montré quelque chose qu'il avait fait, et j'étais sérieusement découragé d'expliquer comment cela aurait pu arriver: il utilisait un System.nanotime pour chronométrer quelque chose, et il donnait à l'utilisateur une mise à jour toutes les secondes combien de temps s'est écoulé (Thread.sleep (1000) pour cette partie), et il a fallu apparemment pour toujours (quelque chose qui attendait 10 secondes a pris environ 3 minutes pour finir). Nous avons essayé d'utiliser millitime afin de voir combien de temps s'était écoulé: il imprimait combien de nanotime s'était écoulé chaque seconde, et nous avons vu que chaque seconde, le nanotime se déplaçait de 40 à 50 millisecondes par seconde.System.nanotime est-il lent?

J'ai vérifié les bogues liés à System.nanotime et Java, mais il semblait que les seules choses que je pouvais trouver impliqué le nanotime soudainement greatly increasing and then stopping. J'ai également parcouru this blog entry basé sur quelque chose que j'ai lu dans une question différente, mais cela n'avait rien qui pourrait le causer.

De toute évidence, cela pourrait être travaillé pour cette situation en utilisant simplement le millénaire à la place; Il y a beaucoup de solutions de contournement à cela, mais ce qui m'intéresse, c'est s'il y a autre chose qu'un problème matériel avec l'horloge système ou du moins l'horloge la plus précise du CPU (puisque c'est ce que System.nanotime semble utiliser) qui pourrait le faire fonctionner régulièrement lent comme ça?

long initialNano = System.nanoTime(); 
long initialMili = System.currentTimeMillis(); 
//Obviously the code isn't actually doing a while(true), 
//but it illustrates the point 
while(true) { 
    Thread.sleep(1000); 
    long currentNano = System.nanoTime(); 
    long currentMili = System.currentTimeMillis(); 
    double secondsNano = ((double) (currentNano - initialNano))/1000000000D; 
    double secondsMili = ((double) (currentMili - initialMili))/1000D; 
    System.out.println(secondsNano); 
    System.out.println(secondsMili); 
} 

secondsNano imprime quelque chose le long des lignes de 0,04, alors que secondsMili imprime quelque chose de très proche de 1.

Il ressemble à un bug le long de cette ligne a été rapporté à Sun's bug database, mais ils ont fermé comme un doublon, mais leur lien ne va pas à un bug existant. Il semble être très spécifique au système, donc je suis de plus en plus sûr que c'est un problème matériel.

+1

Pouvez-vous fournir plus d'informations sur votre environnement: système d'exploitation, matériel, version Java? Je viens de lancer votre échantillon sur ma machine et cela produit des résultats attendus (incrément de nano et mili seconde chaque seconde de 1, plus ou moins). Mon environnement: Windows XP, Dell Optiplex, Sun JDK 1.6.0_14 –

+0

Pas ma machine, donc je vais donner ce que je peux: Windows XP SP3, Core Duo, JDK 1.6. Je suppose que c'est probablement un problème matériel avec tout ce que java utilise pour synchroniser le nanoTime, mais j'essaie de voir s'il y a quelque chose d'autre qui pourrait le provoquer. – DivineWolfwood

+0

J'ai essayé votre code (sur Win XP, Core 2 Duo, Sun Java 1.6.0_16) mais je ne peux pas reproduire le problème. Les temps nano/milli se situent à 0,001 s les uns des autres sur mon système. – Jesper

Répondre

4

... il utilisait un System.nanoTime pour que le programme d'attendre avant de faire quelque chose, et ...

Pouvez-vous nous montrer un code qui montre exactement ce qu'il faisait? Était-ce une sorte étrange de boucle occupée, comme ceci:

long t = System.nanoTime() + 1000000000L; 
while (System.nanoTime() < t) { /* do nothing */ } 

Si oui, alors ce n'est pas la bonne façon de faire votre pause du programme pendant un certain temps. Utilisez plutôt Thread.sleep(...) pour que le programme attende un nombre spécifié de millisecondes.

+0

+1. Ne pas occupé-attendez. –

+0

Ce n'est pas occupé-en attente: je vais éditer la question avec un échantillon de code pour être plus explicite sur ce qui se passe exactement. – DivineWolfwood

+0

Et en y pensant plus, même s'il était occupé à attendre, nous verrons toujours le même comportement ici. Je suis conscient que l'attente occupée n'est pas la bonne chose à faire, mais je me demande s'il n'y a rien d'autre qu'une défaillance matérielle qui pourrait causer la divergence constante mentionnée ci-dessus. – DivineWolfwood

3

Vous réalisez que la boucle que vous utilisez ne prend pas exactement 1 seconde pour s'exécuter? Premièrement Thread.sleep() n'est pas garanti d'être précis, et le reste du code dans la boucle prend un certain temps à s'exécuter (nanoTime() et currentTimeMillis() peuvent être assez lent en fonction de l'implémentation sous-jacente). Deuxièmement, System.currentTimeMillis() n'est pas garanti d'être précis non plus (il ne se met à jour que toutes les 50ms sur certaines combinaisons de système d'exploitation et de matériel). Vous mentionnez également qu'il est inexact à 40-50 ms au-dessus, puis continuez en disant 0.004s, ce qui est en fait seulement 4 ms.

Je vous recommande de changer votre System.out.println() être:

System.out.println(secondsNano - secondsMili); 

De cette façon, vous serez en mesure de voir à quel point les deux horloges diffèrent sur une seconde par seconde base. Je l'ai laissé fonctionner pendant environ 12 heures sur mon ordinateur portable et il a été sorti par 1,46 secondes (rapide, pas lent). Cela montre qu'il y a une certaine dérive dans les deux horloges.Je pense que la méthode currentTimeMillis() fournit une durée plus précise sur une longue période, mais nanoTime() a une plus grande résolution et est bonne pour le code temporel ou fournit un timing de moins d'une milliseconde sur de courtes périodes.

+0

Je comprends que cela ne prend pas une minute pour s'exécuter. Cependant, pour ce qui est essentiellement la même période de temps, l'une des deux lectures qui s'est écoulée environ 40-50 ms est passée, alors que l'autre horloge indique qu'environ 1000 ms est passé. C'est cohérent, et je ne peux pas le reproduire sur un autre système. À ce stade, je suis fondamentalement prêt à le classer à un problème d'OS/BIOS/matériel étrange, car je ne pense pas que je puisse trouver une autre cause qui pourrait expliquer cela. – DivineWolfwood

+0

J'ai également corrigé le .004 dans mon message original: je voulais dire qu'il avait enregistré .04 secondes. Merci d'avoir attrapé ça. – DivineWolfwood

1

J'ai rencontré le même problème. Sauf dans mon cas, c'est plus prononcé.

Avec ce programme simple:

public class test { 
    public static void main(String[] args) { 
     while (true) { 
      try { 
       Thread.sleep(1000); 
      } 
      catch (InterruptedException e) { 
      } 

      OStream.out("\t" + System.currentTimeMillis() + "\t" + nanoTimeMillis()); 
     } 
    } 

    static long nanoTimeMillis() { 
     return Math.round(System.nanoTime()/1000000.0); 
    } 
} 

Je reçois les résultats suivants:

13:05:16:380 main: 1288199116375 61530042 
13:05:16:764 main: 1288199117375 61530438 
13:05:17:134 main: 1288199118375 61530808 
13:05:17:510 main: 1288199119375 61531183 
13:05:17:886 main: 1288199120375 61531559 

Le NanoTime montre que ~ 400ms pour chaque seconde étant écoulés.