Comme indiqué dans l'article de blog Attention à System.nanoTime () en Java, sur les systèmes x86, le système Java de System.nanoTime ( ) renvoie la valeur de temps en utilisant un compteur spécifique CPU . Considérons maintenant le cas suivant que j’utilise pour mesurer le temps d’un appel:
long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;
Maintenant, dans un système multicœur, il se peut qu'après la mesure time1, le thread soit planifié vers un autre processeur dont le compteur est inférieur à celui de la CPU précédente. Ainsi, nous pourrions obtenir une valeur dans time2 qui est inférieure à time1. Ainsi, nous aurions une valeur négative en timeSpent.
Compte tenu de ce cas, n’est-ce pas que System.nanotime est pratiquement inutile pour l’instant?
Je sais que changer l'heure du système n'affecte pas le nanotime. Ce n'est pas le problème que je décris ci-dessus. Le problème est que chaque CPU gardera un compteur différent depuis sa mise sous tension. Ce compteur peut être inférieur sur le second processeur par rapport au premier processeur. Comme le thread peut être planifié par le système d'exploitation sur la deuxième CPU après avoir obtenu time1, la valeur de timeSpent peut être incorrecte et même négative.
Cette réponse a été écrite en 2011 du point de vue de ce que le JDK de Sun de l'époque fonctionnait réellement sur les systèmes d'exploitation. C'était il y a très longtemps! La réponse de Leventov offre une perspective plus actualisée.
Ce message est faux et nanoTime
est sans danger. Il y a un commentaire sur le post qui renvoie à n post de blog de David Holmes , un gars de Sun en temps réel et accès simultané. Ça dit:
System.nanoTime () est implémenté à l'aide de l'API QueryPerformanceCounter/QueryPerformanceFrequency [...] Le mécanisme par défaut utilisé par QPC est déterminé par la couche d'abstraction matérielle (HAL) [...]. Cette modification par défaut ne concerne pas uniquement le matériel, mais également le système d'exploitation. versions. Par exemple, Windows XP Service Pack 2 a changé pour utiliser le minuteur de gestion de l'alimentation (PMTimer) plutôt que le compteur d'horodatage du processeur (TSC) en raison de problèmes empêchant le TSC d'être synchronisé sur différents processeurs en Systèmes SMP, et du fait que sa fréquence peut varier (et donc sa relation avec le temps écoulé) en fonction des paramètres de gestion de l’alimentation.
Donc, sous Windows, ceci était un problème jusqu’à WinXP SP2, mais ce n’est pas maintenant.
Je ne trouve pas de partie II (ou plus) qui parle d’autres plates-formes, mais cet article inclut une remarque que Linux a rencontrée et a résolu le même problème de la même manière, avec un lien vers le FAQ for clock_gettime (CLOCK_REALTIME) , qui dit:
- Clock_gettime (CLOCK_REALTIME) est-il cohérent sur tous les processeurs/cœurs? (Est-ce que Arch est important? Par exemple, ppc, arm, x86, AMD64, sparc).
C'est devrait ou c'est considéré comme un buggy.
Cependant, sur x86/x86_64, il est possible de voir que les TSC freq non synchronisés ou variables entraînent des incohérences temporelles. Les noyaux 2.4 n’ont vraiment aucune protection contre cela, et les premiers noyaux 2.6 ne se portent pas très bien ici non plus. À partir de la version 2.6.18, la logique de détection est meilleure et nous retombons généralement sur une source d'horloge sûre.
ppc a toujours une base de temps synchronisée, donc cela ne devrait pas être un problème.
Donc, si le lien de Holmes peut être lu comme impliquant que nanoTime
appelle clock_gettime(CLOCK_REALTIME)
, alors il est sûr à partir du noyau 2.6.18 sur x86, et toujours sur PowerPC (car IBM et Motorola, contrairement à Intel, savent réellement concevoir des microprocesseurs).
Malheureusement, il n’est fait aucune mention de SPARC ou de Solaris. Et bien entendu, nous n’avons aucune idée de ce que font les machines virtuelles JVM d’IBM. Mais les machines virtuelles JVM de Sun sur Windows et Linux modernes obtiennent ce droit.
EDIT: Cette réponse est basée sur les sources citées. Mais je crains toujours que cela puisse être complètement faux. Des informations plus récentes seraient vraiment utiles. Je viens de trouver un lien vers un n article plus récent sur quatre ans sur les horloges de Linux qui pourrait être utile.
J'ai fait un peu de recherche et j'ai trouvé que si l'on est pédant alors oui, cela pourrait être considéré comme inutile ... dans des situations particulières ... cela dépend de la sensibilité du temps à vos exigences ...
Départ cette citation à partir du Java site Sun:
L'horloge en temps réel et System.nanoTime () sont toutes deux basées sur le même appel système et donc sur la même horloge.
Avec Java RTS, toutes les API basées sur le temps (par exemple, les temporisations, les threads périodiques, la surveillance des délais, etc.) sont basées sur le minuteur haute résolution. priorités, ils peuvent s’assurer que le code approprié sera exécuté au bon moment pour les contraintes en temps réel. En revanche, les API ordinaires Java SE ne proposent que quelques méthodes capables de gérer des temps de résolution élevés. Utilisez System.nanoTime () entre différents points du code pour effectuer des mesures de temps écoulé doit toujours être précis.
Java a également une mise en garde pour la méthode nanoTime () :
Cette méthode ne peut être utilisée que pour mesurer le temps écoulé et n'est liée à aucune autre notion de temps système ou d'horloge murale. La valeur renvoyée représente des nanosecondes depuis un certain temps fixe mais arbitraire (peut-être à l'avenir, les valeurs peuvent donc être négatives). Cette méthode fournit une précision nanoseconde, mais pas nécessairement une précision nanoseconde. Aucune garantie n'est donnée sur la fréquence à laquelle les valeurs changent. Différences d’appels successifs supérieurs à environ 292,3 ans (263 nanosecondes) ne calculera pas avec précision le temps écoulé en raison du débordement numérique.
Il semblerait que la seule conclusion que l'on puisse tirer est que nanoTime () ne peut pas être considéré comme une valeur précise. En tant que tel, si vous n'avez pas besoin de mesurer des temps qui ne sont que quelques nanosecondes, cette méthode est suffisante, même si la valeur renvoyée est négative. Cependant, si vous avez besoin d'une plus grande précision, ils semblent vous recommander d'utiliser Java RTS.
Donc, pour répondre à votre question ... nanoTime () n’est pas inutile ... ce n’est tout simplement pas la méthode la plus prudente à utiliser dans toutes les situations.
Pas besoin de débattre, utilisez simplement la source. Ici, SE 6 pour Linux, tirez vos propres conclusions:
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;
}
}
Disclaimer: Je suis le développeur de cette bibliothèque
Vous pourriez aimer ceci mieux:
http://juliusdavies.ca/nanotime/
Mais il copie un fichier DLL ou Unix .so (objet partagé) dans le répertoire de base de l'utilisateur actuel pour qu'il puisse appeler JNI.
Quelques informations de base sont sur mon site à:
http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html
Depuis Java 7, System.nanoTime()
est garanti par la spécification JDK. [Javdoc de System.nanoTime()
indique clairement que tous les invocations observées au sein d'une machine virtuelle (c'est-à-dire, sur tous les threads) sont monotones:
La valeur renvoyée représente des nanosecondes car une heure d'origine fixe mais arbitraire (peut-être plus tard, les valeurs peuvent donc être négatives). La même origine est utilisée par tous les appels de cette méthode dans une instance d'une machine virtuelle Java; d'autres instances de machines virtuelles sont susceptibles d'utiliser une origine différente.
L'implémentation JVM/JDK est chargée de résoudre les incohérences pouvant être observées lors de l'appel des utilitaires de système d'exploitation sous-jacents (par exemple, ceux mentionnés dans de la réponse de Tom Anderson ).
La majorité des anciennes réponses à cette question (écrites en 2009-2012) expriment un FUD qui était probablement pertinent pour Java 5 ou Java 6 mais n'est plus pertinent pour les versions modernes de Java.
Il convient toutefois de mentionner que, malgré la sécurité de la garantie JDK nanoTime()
, plusieurs bogues d'OpenJDK ont empêché l'application de cette garantie sur certaines plateformes ou dans certaines circonstances (par exemple ). JDK-8040140 , JDK-8184271 ). Il n'y a pas de bogues ouverts (connus) dans OpenJDK à propos de nanoTime()
pour le moment, mais la découverte d'un nouveau bogue de ce type ou une régression dans une version plus récente d'OpenJDK ne devrait choquer personne.
Dans cet esprit, le code qui utilise nanoTime()
pour le blocage temporisé, les intervalles en attente, les délais, etc., devrait de préférence traiter les différences de temps négatives (délais) comme des zéros plutôt que comme des exceptions. Cette pratique est également préférable car elle est cohérente avec le comportement de toutes les méthodes d’attente chronométrées dans toutes les classes de Java.util.concurrent.*
, Par exemple Semaphore.tryAcquire()
, Lock.tryLock()
, BlockingQueue.poll()
, etc.
Néanmoins, nanoTime()
devrait tout de même être préféré pour la mise en œuvre du blocage temporisé, de l'intervalle d'attente, des délais, etc. à currentTimeMillis()
car ce dernier est sujet au phénomène de "recul du temps" (par exemple en raison de correction du temps), i. e. currentTimeMillis()
ne convient pas du tout pour mesurer des intervalles de temps. Voir cette réponse pour plus d'informations.
Au lieu d’utiliser directement nanoTime()
pour les mesures du temps d’exécution du code, utilisez plutôt des frameworks de benchmarking et des profileurs spécialisés, par exemple [~ # ~] jmh [~ # ~] et profileur asynchrone en mode profilage horloge murale .
Linux corrige les divergences entre les processeurs, mais pas Windows. Je vous suggère de supposer que System.nanoTime () n’est précis qu’à environ 1 micro-seconde. Un moyen simple d'obtenir un timing plus long consiste à appeler foo () 1000 fois ou plus et à diviser le temps par 1000.
J'ai vu un temps négatif écoulé signalé à l'aide de System.nanoTime (). Pour être clair, le code en question est:
long startNanos = System.nanoTime();
Object returnValue = joinPoint.proceed();
long elapsedNanos = System.nanoTime() - startNanos;
et la variable 'elapsedNanos' avait une valeur négative. (Je suis convaincu que l'appel intermédiaire a également pris moins de 293 ans, ce qui est le point de débordement pour les nanos stockés en mémoire longue :)
Cela s'est produit à l'aide d'un serveur IBM v1.5 JRE 64 bits sur du matériel IBM P690 (multicœur) exécutant AIX. Je n'ai vu cette erreur se produire qu'une seule fois, alors cela semble extrêmement rare. Je ne connais pas la cause - s'agit-il d'un problème spécifique au matériel, d'un défaut de la machine virtuelle Java - je ne le sais pas. Je ne connais pas non plus les implications pour la précision de nanoTime () en général.
Pour répondre à la question initiale, je ne pense pas que nanoTime soit inutile: il fournit une temporisation inférieure à une milliseconde, mais il existe un risque réel (et pas uniquement théorique) d’exactitude qui doit être pris en compte.
Absolument pas inutile. Les amateurs de chronométrage soulignent correctement le problème multicœur, mais dans les applications de type Word, il est souvent radicalement meilleur que currentTimeMillis ().
Lors du calcul des positions graphiques dans les images, nanoTime () génère un mouvement BEAUCOUP plus fluide dans mon programme.
Et je teste uniquement sur des machines multi-core.
Non, ce n'est pas ... Cela dépend de votre processeur, vérifiez minuterie d'événement de haute précision pour savoir comment/pourquoi les choses sont traitées différemment en fonction du processeur.
En gros, lisez le code source de votre Java et vérifiez ce que votre version fait avec la fonction. Si cela fonctionne avec le processeur, vous l’exécuterez.
IBM suggère même vous l'utilisez pour l'analyse comparative des performances (un article de 2008, mais mis à jour).
Cela ne semble pas être un problème sur un Core 2 Duo sous Windows XP et JRE 1.5.0_06.
Dans un test avec trois threads, System.nanoTime () ne semble pas revenir en arrière. Les processeurs sont tous les deux occupés et les threads s'endormissent de temps en temps pour provoquer le déplacement de threads.
[EDIT] Je suppose que cela ne se produit que sur des processeurs physiquement séparés, c’est-à-dire que les compteurs sont synchronisés pour plusieurs cœurs sur le même dé.
Je renvoie à ce qui est essentiellement la même discussion où Peter Lawrey fournit une bonne réponse. Pourquoi le temps écoulé est-il négatif avec System.nanoTime ()?
Beaucoup de gens ont mentionné que dans Java System.nanoTime () pourrait retourner un temps négatif. Je m'excuse d'avoir répété ce que d'autres personnes avaient déjà dit.
Ce serait cool si System.nanoTime () renvoyait le CoreID où il s’exécutait.
Java est multiplate-forme et nanoTime dépend de la plate-forme. Si vous utilisez Java - lorsque vous n'utilisez pas nanoTime. J'ai trouvé de vrais bogues avec différentes implémentations de JVM avec cette fonction.
La documentation Java 5 recommande également d'utiliser cette méthode dans le même but.
Cette méthode ne peut être utilisée que pour mesurer le temps écoulé et n'est liée à aucune autre notion de système ou de temps d'horloge.
De plus, System.currentTimeMillies()
change lorsque vous modifiez l'horloge de votre système, tandis que System.nanoTime()
ne change pas, de sorte que cette dernière est plus sûre pour mesurer les durées.