Lors de la mesure du temps écoulé à un faible niveau, j'ai le choix d'utiliser l'un de ces éléments:
System.currentTimeMillis();
System.nanoTime();
Les deux méthodes sont implémentées native
. Avant de creuser dans un code C, quelqu'un sait-il s'il y a des frais généraux importants pour appeler l'un ou l'autre? Je veux dire, si je ne me soucie pas vraiment de la précision supplémentaire, laquelle devrait être moins gourmande en temps CPU?
N.B: J'utilise le standard Java 1.6 JDK, mais la question peut être valable pour n'importe quel JRE ...
La réponse marquée correcte sur cette page n'est en fait pas correcte. Ce n'est pas un moyen valable d'écrire un benchmark en raison de l'élimination du code mort JVM (DCE), du remplacement sur pile (OSR), du déroulement de la boucle, etc. Seul un framework comme le framework de micro-benchmarking JMH d'Oracle peut mesurer correctement quelque chose comme ça. Lisez cet article si vous avez des doutes sur la validité de ces micro-repères.
Voici un benchmark JMH pour System.currentTimeMillis()
vs System.nanoTime()
:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
@Benchmark
public long currentTimeMillis() {
return System.currentTimeMillis();
}
@Benchmark
public long nanoTime() {
return System.nanoTime();
}
}
Et voici les résultats (sur un Intel Core i5):
Benchmark Mode Samples Mean Mean err Units
c.z.h.b.NanoBench.currentTimeMillis avgt 16 122.976 1.748 ns/op
c.z.h.b.NanoBench.nanoTime avgt 16 117.948 3.075 ns/op
Ce qui montre que System.nanoTime()
est légèrement plus rapide à ~ 118ns par invocation par rapport à ~ 123ns. Cependant, il est également clair qu'une fois l'erreur moyenne prise en compte, il y a très peu de différence entre les deux. Les résultats sont également susceptibles de varier selon le système d'exploitation. Mais la conclusion générale devrait être qu'ils sont essentiellement équivalents en termes de frais généraux.
MISE À JOUR 2015/08/25: Bien que cette réponse soit plus proche de la correction que la plupart, en utilisant JMH pour mesurer, elle n'est toujours pas correcte. Mesurer quelque chose comme System.nanoTime()
lui-même est un type particulier de benchmarking tordu. La réponse et l'article définitif est ici .
Je ne pense pas que vous ayez à vous soucier des frais généraux de l'un ou de l'autre. C'est tellement minime qu'il est à peine mesurable. Voici un micro-benchmark rapide des deux:
for (int j = 0; j < 5; j++) {
long time = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
long x = System.currentTimeMillis();
}
System.out.println((System.nanoTime() - time) + "ns per million");
time = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
long x = System.nanoTime();
}
System.out.println((System.nanoTime() - time) + "ns per million");
System.out.println();
}
Et le dernier résultat:
14297079ns per million
29206842ns per million
Il semble que System.currentTimeMillis()
soit deux fois plus rapide que System.nanoTime()
. Cependant, 29ns va être beaucoup plus court que tout ce que vous mesureriez de toute façon. J'irais pour System.nanoTime()
pour la précision et l'exactitude car il n'est pas associé aux horloges.
Vous ne devez utiliser que System.nanoTime()
pour mesurer le temps nécessaire à l'exécution. Ce n'est pas seulement une question de précision en nanosecondes, System.currentTimeMillis()
est "l'heure de l'horloge murale" tandis que System.nanoTime()
est destiné à chronométrer les choses et n'a pas les bizarreries "temps réel" que le d'autres le font. À partir du Javadoc de System.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.
Si vous avez le temps, regardez cette conférence de Cliff Click , il parle du prix de System.currentTimeMillis
ainsi que d'autres choses.
System.currentTimeMillis()
est généralement très rapide (afaik 5-6 cycles de processeur mais je ne sais plus où j'ai lu ceci), mais sa résolution varie sur différentes plates-formes.
Donc, si vous avez besoin d'une grande précision, optez pour nanoTime()
, si vous êtes préoccupé par les frais généraux, optez pour currentTimeMillis()
.
La réponse acceptée à cette question est en effet incorrecte. La réponse alternative fournie par @brettw est bonne mais néanmoins légère sur les détails.
Pour un traitement complet de ce sujet et le coût réel de ces appels, veuillez consulter https://shipilev.net/blog/2014/nanotrusting-nanotime/
Pour répondre à la question posée:
quelqu'un sait-il s'il y a des frais généraux importants pour appeler l'un ou l'autre?
System#nanoTime
est compris entre 15 et 30 nanosecondes par appel.nanoTime
, sa résolution, ne change qu'une fois toutes les 30 nanosecondesCela signifie que si vous essayez de faire des millions de requêtes par seconde, appeler nanoTime
signifie que vous perdez effectivement une énorme partie du deuxième appelant nanoTime
. Pour de tels cas d'utilisation, envisagez de mesurer les demandes du côté client, vous assurant ainsi de ne pas tomber dans omission coordonnée , la mesure de la profondeur de la file d'attente est également un bon indicateur.
Si vous n'essayez pas de charger autant de travail que vous le pouvez en une seule seconde, alors nanoTime
n'aura pas vraiment d'importance mais l'omission coordonnée est toujours un facteur.
Enfin, pour être complet, currentTimeMillis
ne peut pas être utilisé quel que soit son coût. En effet, il n'est pas garanti d'avancer entre deux appels. Surtout sur un serveur avec NTP, currentTimeMillis
se déplace constamment. Sans oublier que la plupart des choses mesurées par un ordinateur ne prennent pas une milliseconde entière.
Au niveau théorique, pour un VM qui utilise des threads natifs et se trouve sur un système d'exploitation préemptif moderne, le currentTimeMillis peut être implémenté pour être lu une seule fois par tranche de temps. Vraisemblablement, les implémentations de nanoTime ne sacrifiez la précision.