160 votes

System.nanoTime() est-il complètement inutile ?

Comme indiqué dans l'article de blog Attention à System.nanoTime() en Java Sur les systèmes x86, la fonction System.nanoTime() de Java renvoie la valeur temporelle à l'aide d'une variable de type CPU compteur spécifique. 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;

Or, dans un système multicœur, il se peut qu'après avoir mesuré le temps1, le thread soit programmé sur un autre processeur dont le compteur est inférieur à celui de l'unité centrale précédente. Ainsi nous pourrions obtenir une valeur dans le temps2 qui est moins que le temps1. Ainsi, nous obtiendrions une valeur négative dans timeSpent.

Dans ce cas, n'est-ce pas que System.nanotime est plutôt inutile pour le moment ?

Je sais que la modification de 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 unité centrale conserve un compteur différent depuis sa mise sous tension. Ce compteur peut être plus bas sur la deuxième CPU que sur la première. Puisque le thread peut être programmé par le système d'exploitation sur le second CPU après avoir obtenu time1, la valeur de timeSpent peut être incorrecte et même négative.

2 votes

Je n'ai pas de réponse mais je suis d'accord avec vous. Peut-être que cela devrait être considéré comme un bug dans la JVM.

2 votes

Ce post est incorrect et ne pas utiliser TSC est lent mais vous devez vivre avec : bugs.sun.com/bugdatabase/view_bug.do?bug_id=6440250 TSC peut également être rendu utile par le biais d'un hyperviseur, mais il est alors à nouveau lent.

1 votes

Et bien sûr, vous pouvez fonctionner dans une machine virtuelle où un processeur peut apparaître au milieu d'une session :D

212voto

Tom Anderson Points 22456

Cette réponse a été écrite en 2011 du point de vue de ce que faisait réellement le JDK de Sun de l'époque fonctionnant sur les systèmes d'exploitation de l'époque. C'était il y a bien longtemps ! La réponse de Leventov offre une perspective plus actuelle.

Ce post est faux, et nanoTime est sûr. Il y a un commentaire sur le post qui renvoie à un billet de blog par David Holmes un spécialiste du temps réel et de la concurrence chez Sun. C'est écrit :

System.nanoTime() est mis en œuvre à 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 valeur par défaut change non seulement en fonction du matériel mais aussi des versions du système d'exploitation. Par exemple, le Service Pack 2 de Windows XP a changé les choses pour utiliser la minuterie de gestion de l'énergie (PMTimer) plutôt que le compteur d'horodatage du processeur (TSC) en raison de problèmes de non synchronisation du TSC sur différents processeurs dans les 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'énergie.

Donc, sous Windows, cette était un problème jusqu'à WinXP SP2, mais ce n'est plus le cas maintenant.

Je ne trouve pas de partie II (ou plus) qui parle d'autres plates-formes, mais cet article inclut une remarque selon laquelle Linux a rencontré et résolu le même problème de la même manière, avec un lien vers la page FAQ pour clock_gettime(CLOCK_REALTIME) qui dit :

  1. Est-ce que clock_gettime(CLOCK_REALTIME) est cohérent sur tous les processeurs/cores ? (Est-ce que l'architecture a de l'importance ? par exemple ppc, arm, x86, amd64, sparc).

Es debe ou il est considéré comme bogué.

Cependant, sur x86/x86_64, il est possible de voir des TSC non synchronisés ou à fréquence variable provoquer des incohérences temporelles. Les noyaux 2.4 n'avaient pas vraiment de protection contre cela, et les premiers noyaux 2.6 n'étaient pas non plus très performants dans ce domaine. À partir de la version 2.6.18, la logique de détection de ce problème est meilleure et nous nous rabattons 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 (parce qu'IBM et Motorola, contrairement à Intel, savent réellement comment concevoir des microprocesseurs).

Il n'y a aucune mention de SPARC ou de Solaris, malheureusement. Et bien sûr, nous n'avons aucune idée de ce que font les JVM d'IBM. Mais les JVM de Sun sur les Windows et Linux modernes font ça bien.

EDIT : Cette réponse est basée sur les sources qu'elle cite. Mais je crains toujours qu'elle ne soit complètement fausse. Des informations plus récentes seraient très utiles. Je viens de tomber sur un lien vers un un article plus récent de quatre ans sur les horloges de Linux ce qui pourrait être utile.

0 votes

Je suppose qu'il n'y a pas de mise à jour à ce sujet, concernant le comportement sur Linux, BSD ou d'autres plateformes ?

6 votes

Bonne réponse, il faudrait ajouter un lien vers l'exploration plus récente de ce sujet : shipilev.net/blog/2014/nanotrusting-nanotime

0 votes

Note : le nouveau lien est cassé. Quelqu'un peut-il poster un résumé ici ?

35voto

mezoid Points 10293

J'ai fait quelques recherches et j'ai trouvé que si l'on est pédant, alors oui, cela peut être considéré comme inutile... dans des situations particulières... cela dépend de la rapidité de vos besoins...

Vérifiez cette citation sur le site de Java Sun :

L'horloge en temps réel et System.nanoTime() sont toutes les deux basées sur le même appel système et donc la même horloge.

Avec Java RTS, toutes les API basées sur le temps (par exemple, Timers, Periodic threads périodiques, le suivi des délais, etc. ) sont basées sur le minuterie haute résolution. Et, avec avec les priorités en temps réel, ils peuvent garantir que le code approprié sera sera exécuté au bon moment pour contraintes de temps réel. En revanche, les API Java SE ordinaires n'offrent que quelques méthodes capables de gérer temps à haute résolution, sans aucune garantie d'exécution à un moment donné. L'utilisation de System.nanoTime() entre différents points du code pour effectuer des mesures de temps écoulé devrait toujours être précise.

Java dispose également d'un [mise en garde concernant la fonction nanoTime()](http://java.sun.com/j2se/1.5.0/docs/api/java/lang/System.html#nanoTime()) méthode :

Cette méthode ne peut être utilisée que pour mesurer le temps écoulé et n'est pas n'est pas liée à une autre notion de temps système ou d'horloge murale. La valeur renvoyée représente les nanosecondes écoulées depuis un fixe mais arbitraire (peut-être dans le futur, les valeurs peuvent donc être négatives). Cette méthode fournit précision de la nanoseconde, mais pas mais pas nécessairement une précision de l'ordre de la nanoseconde. Non garantie n'est faite sur la fréquence fréquence de changement des valeurs. Les différences dans des appels successifs qui s'étendent sur plus d'environ 292,3 ans (2 63 nanosecondes) ne permettront pas de calculera pas précisément le temps écoulé à cause du numérique.

Il semblerait que la seule conclusion que l'on puisse tirer soit que l'on ne peut pas se fier à nanoTime() comme valeur précise. Ainsi, si vous n'avez pas besoin de mesurer des temps séparés de quelques nanosecondes, cette méthode est suffisante, même si la valeur retourné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... non, nanoTime() n'est pas inutile...., mais ce n'est pas la méthode la plus prudente à utiliser dans toutes les situations.

3 votes

> cette méthode est suffisamment bonne même si la valeur retournée est négative. Je ne comprends pas, si la valeur de timespent est négative, alors quelle est son utilité pour mesurer le temps pris dans foo() ?

3 votes

C'est ok parce que tout ce qui vous préoccupe est la valeur absolue de la différence. c'est à dire si votre mesure est le temps t où t = t2 - t1 alors vous voulez savoir |t|.... alors que faire si la valeur est négative... même avec le problème du multi-cœur l'impact sera rarement de quelques nanosecondes de toute façon.

0 votes

De plus, selon la documentation Java, vous pouvez toujours obtenir une valeur négative même sur une machine à un seul cœur puisque vous n'avez aucune garantie quant au moment où votre code sera exécuté... "la valeur représente les nanosecondes depuis un moment fixe mais arbitraire (peut-être dans le futur, donc les valeurs peuvent être négatives)"...

18voto

blais Points 169

Pas besoin de débattre, utilisez simplement la source. Ici, SE 6 pour Linux, faites 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;
  }
}

7 votes

Cela n'est utile que si vous savez ce que fait l'API utilisée. L'API utilisée est implémentée par le système d'exploitation ; ce code est correct par rapport à la spécification de l'API utilisée (clock_gettime/gettimeofday), mais comme d'autres l'ont souligné, certains systèmes d'exploitation non récents ont des implémentations boguées.

10voto

Julius Davies Points 1878

Vous aimerez peut-être mieux ça :

http://juliusdavies.ca/nanotime/

Mais il copie une DLL ou un fichier Unix .so (objet partagé) dans le répertoire personnel de l'utilisateur actuel afin de pouvoir appeler JNI.

Vous trouverez des informations générales sur mon site à l'adresse suivante

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

7voto

Peter Lawrey Points 229686

Linux corrige les différences entre les processeurs, mais pas Windows. Je vous suggère de supposer que System.nanoTime() n'est précis qu'à environ 1 microseconde. Une façon simple d'obtenir un chronométrage plus long est d'appeler foo() 1000 fois ou plus et de diviser le temps par 1000.

2 votes

Pourriez-vous fournir une référence (comportement sous Linux et Windows) ?

1 votes

Malheureusement, la méthode proposée sera généralement très imprécise car chaque événement tombant dans le créneau de mise à jour de l'horloge murale de +/- 100 ms renverra souvent zéro pour les opérations inférieures à une seconde. La somme de 9 opérations ayant chacune une durée de zéro est, eh bien, zéro, divisée par neuf est ... zéro. À l'inverse, l'utilisation de System.nanoTime() fournira des durées d'événements relativement précises (non nulles), qui, additionnées et divisées par le nombre d'événements, donneront une moyenne très précise.

0 votes

@DarrellTeague résumer 1000 événements et les additionner est la même chose que le temps de bout en bout.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X