web-dev-qa-db-fra.com

Pourquoi System.nanoTime () est-il plus lent (en termes de performances) que System.currentTimeMillis ()?

Aujourd'hui, j'ai fait un petit test d'évaluation rapide pour tester les performances de vitesse de System.nanoTime() et System.currentTimeMillis():

long startTime = System.nanoTime();

for(int i = 0; i < 1000000; i++) {
  long test = System.nanoTime();
}

long endTime = System.nanoTime();

System.out.println("Total time: "+(endTime-startTime));

Ce sont les résultats:

System.currentTimeMillis(): average of 12.7836022 / function call
System.nanoTime():          average of 34.6395674 / function call

Pourquoi les différences de vitesse de course sont-elles si grandes?

Système de référence:

Java 1.7.0_25
Windows 8 64-bit
CPU: AMD FX-6100
59
Frithjof

De ce blog Oracle :

System.currentTimeMillis() est implémenté à l'aide de La méthode GetSystemTimeAsFileTime, qui lit essentiellement le bas heure de résolution que Windows maintient. Lisant cela la variable globale est naturellement très rapide - environ 6 cycles selon informations rapportées. 

System.nanoTime() est implémenté à l'aide de QueryPerformanceCounter/ QueryPerformanceFrequency API (si disponible, sinon, il retourne currentTimeMillis*10^6). QueryPerformanceCounter(QPC) est implémenté de différentes manières en fonction du matériel sur lequel il est exécuté. Il utilisera généralement soit le programmable-interval-timer-timer (PIT) , ou le temporisateur de gestion (PMT) de l'alimentation ACPI, ou le compteur d'horodatage (TSC) au niveau de la CPU . L'accès à la PIT/PMT nécessite l'exécution d'instructions de port d'E/S lentes. le temps d'exécution pour QPC est de l'ordre de microsecondes. En revanche, la lecture du TSC est de l'ordre de 100 cycles (pour lire le TSC à partir de la puce et le convertir en valeur temporelle sur la fréquence de fonctionnement).

Peut-être que cela répond à la question. Les deux méthodes utilisent un nombre différent de cycles d'horloge, ce qui entraîne une vitesse lente de la dernière.

Plus loin dans ce blog dans la section conclusion:

Si vous souhaitez mesurer/calculer le temps écoulé, utilisez toujours System.nanoTime (). Sur la plupart des systèmes, il donnera une résolution de l'ordre de quelques microsecondes. Sachez cependant que cet appel peut également prendre quelques microsecondes pour s'exécuter sur certaines plates-formes.

64
Rohit Jain

La plupart des systèmes d'exploitation (vous ne précisez pas celui que vous utilisez) ont un compteur/horloge en mémoire qui fournit une précision à la milliseconde (ou proche de celle-ci). Pour une précision à la nanoseconde, la plupart doivent lire un compteur matériel. La communication avec le matériel est plus lente que la lecture d'une valeur déjà en mémoire.

24
Eelke

Ce n'est peut-être le cas que sous Windows. Voir cette réponse à une question similaire.

Fondamentalement, System.currentTimeMillis() lit simplement une variable globale gérée par Windows (ce qui correspond à une granularité faible), alors que System.nanoTime() doit en réalité effectuer des opérations IO.

4
Michael Borgwardt

Vous mesurez cela sous Windows, n'est-ce pas? J'ai effectué cet exercice en 2008. nanoTime IS est plus lent sous Windows que currentTimeMillis. Si je me souviens bien, sous Linux, nanotime est plus rapide que currentTimeMillis et certainement plus rapide que sous Windows.

La chose importante à noter est que si vous essayez de mesurer l’agrégat de plusieurs opérations inférieures à la milliseconde, vous devez utiliser nanotime comme si l’opération se terminait en moins de 1/1000e de seconde de votre code. donc 1000 d'entre eux seront toujours instantanés. Vous voudrez peut-être utiliser nanotime puis arrondir à la milliseconde près. Ainsi, si une opération prend 8 000 nanosecondes, elle sera comptée pour une milliseconde et non pour 0.

1
Walt Corey

Vous voudrez peut-être utiliser nanotime puis arrondir à la milliseconde près. Ainsi, si une opération prend 8 000 nanosecondes, elle sera comptée pour une milliseconde et non pour 0.

Note arithmétique:

8000 nanosecondes est 8 microsecondes soit 0,008 milliseconde. Arrondir cela prendra à 0 millisecondes.

0
dave