2010-04-09 6 views
8

Ich habe die folgenden zwei Programme:Iteration Geschwindigkeit von int vs lange

long startTime = System.currentTimeMillis(); 
for (int i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

und

long startTime = System.currentTimeMillis(); 
for (long i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

Hinweis: Der einzige Unterschied ist die Art der Schleifenvariable ist (int und long).

Wenn ich dies ausführe, druckt das erste Programm konsistent zwischen 0 und 16 ms, unabhängig von dem Wert N. Der zweite dauert viel länger. Für N == Integer.MAX_VALUE läuft es in ungefähr 1800 msecs auf meiner Maschine. Die Laufzeit scheint in N mehr oder weniger linear zu sein.

Also warum ist das?

Ich nehme an, der JIT-Compiler optimiert die int Schleife zu Tode. Und das aus gutem Grund, denn offensichtlich tut es nichts. Aber warum tut es das auch nicht für die long Schleife?

Ein Kollege dachte, wir könnten den JIT-Compiler messen, der seine Arbeit in der long-Schleife ausführt, aber da die Laufzeit in N linear zu sein scheint, ist dies wahrscheinlich nicht der Fall.

Ich bin mit JDK 1.6.0 Update 17:

C:\>java -version 
java version "1.6.0_17" 
Java(TM) SE Runtime Environment (build 1.6.0_17-b04) 
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode) 

Ich bin auf Windows XP Professional x64 Edition, Service Pack 2, mit einem Intel Core 2 Quad CPU bei 2.40GHz.


HAFTUNGSAUSSCHLUSS

Ich weiß, dass in der Produktion nicht Microbenchmarks nützlich sind. Ich weiß auch, dass System.currentTimeMillis() nicht so genau ist, wie sein Name vermuten lässt. Dies ist nur etwas, was mir beim Herumalbern aufgefallen ist, und ich war einfach neugierig, warum das passiert; nichts mehr.

+0

ich absolut mit @Andrzej zustimmen hier, aber wenn es wirklich nur um ihrer selbst willen Neugier, Sie könnten das PrintAssembly-Plug-in verwenden, um sich den generierten Code anzusehen: http://wikis.sun.com/display/HotSpotInternals/PrintAssembly –

+0

Was passiert, wenn Sie mit dem Flag -Xint arbeiten? Dies verhindert das Kompilieren von Hotspots, so dass Sie einen besseren Vergleich erhalten. –

+0

@Steven: aber mit '-Xint' ist das Ergebnis * noch weniger * aussagekräftig für alles, was echt aussieht. –

Antwort

5

Es ist eine interessante Frage, aber um ehrlich zu sein, bin ich nicht davon überzeugt, dass die Berücksichtigung von Hotspots Verhalten hier nützliche Informationen liefern wird. Alle Antworten, die Sie erhalten, sind in einem allgemeinen Fall nicht übertragbar (weil wir die Optimierungen betrachten, die Hotspot in einer bestimmten Situation durchführt), so dass sie Ihnen helfen zu verstehen, warum ein No-Op schneller ist als ein anderer. aber sie werden dir nicht helfen, schneller "echte" Programme zu schreiben.

Es ist auch unglaublich einfach, sehr irreführende Mikro-Benchmarks zu schreiben - siehe this IBM DW article für einige der häufigsten Fallstricke, wie man sie vermeidet und einige allgemeine Kommentare zu dem, was du tust.

Also wirklich das ist eine "no comment" Antwort, aber ich denke, das ist die einzige gültige Antwort.Eine Kompilierungszeit-Trivial-No-Op-Schleife braucht nicht , um schnell zu sein, so dass der Compiler in einigen dieser Bedingungen nicht optimiert ist, um schnell zu sein. Meine Vermutung

+0

Sie haben natürlich Recht. Der Grund, warum ich frage, ist pure Neugier auf etwas, das mir beim Herumalbern aufgefallen ist; Ich habe nicht die Absicht, dies in einem echten Programm zu tun. :) – jqno

4

Sie verwenden wahrscheinlich eine 32-Bit-JVM. Die Ergebnisse werden wahrscheinlich bei einer 64-Bit-JVM anders sein. In einer 32-Bit-JVM kann ein Int einer nativen 32-Bit-Ganzzahl zugeordnet und mit einer einzigen Operation inkrementiert werden. Dasselbe hält nicht lange an, was mehr Operationen zum Erhöhen erfordert.

Siehe diese question für eine Diskussion über int und lange Größen.

+0

Guter Punkt. Ich verwende jedoch eine 64-Bit-JVM. Ich werde die Frage aktualisieren. – jqno

+0

Es wird auch nützlich sein, das Betriebssystem und einige Details zu Ihrer Hardwarekonfiguration (hauptsächlich die CPU) zu veröffentlichen. – kgiannakakis

3

- und es ist nur eine Vermutung - ist dies:

Die JVM kommt zu dem Schluss, dass die erste Schleife effektiv nichts tut, so ist es ihm gänzlich entfernt. Keine Variable "entweicht" aus der for-Schleife.

Im zweiten Fall macht die Schleife auch nichts. Aber es könnte sein, dass der JVM-Code, der bestimmt, dass die Schleife nichts tut, eine "if (type of i) == int" -Klausel hat. In diesem Fall funktioniert die Optimierung zum Entfernen der do-nothing for-Schleife nur mit int.

Eine Optimierung, die Code entfernt, muss sicher sein, dass es keine Nebenwirkungen gibt. Die JVM-Programmierer scheinen sich auf der Seite der Vorsicht geirrt zu haben.

1

Ein solches Micro-Benchmarking macht wenig Sinn, da die Ergebnisse stark von der internen Funktionsweise des Hotspot JIT abhängen.

Beachten Sie außerdem, dass die Systemtaktwerte, die Sie mit System.currentTimeMillis() erhalten, keine Auflösung von 1 ms für alle Betriebssysteme haben. Sie können dies nicht verwenden, um sehr kurz dauernde Ereignisse sehr genau zu messen.

Werfen Sie einen Blick auf diese Artikel, die warum tun Mikro-Benchmarks in Java erklärt nicht so einfach ist, wie die meisten Leute denken: Anatomy of a flawed microbenchmark