Geschwindigkeitstest: Java, dann VB und dann C++
-
kannst du mal "deine" C++, Java Quellen hier einstellen?
-
Hier wird also die Sortiergeschwindigkeit auf einem sortierten Array gemessen. Wen interessiert die? (Mal abgesehen davon, dass damit nat. wesentlich Teile des Algorithmus nie ausgeführt und also auch nicht gemessen werden)
-
camper schrieb:
Hier wird also die Sortiergeschwindigkeit auf einem sortierten Array gemessen. Wen interessiert die? (Mal abgesehen davon, dass damit nat. wesentlich Teile des Algorithmus nie ausgeführt und also auch nicht gemessen werden)
Das Array ist zwar Anfangs sortiert, allerdings genau falsch herum. Der Algorithmus wird also tatsächlich ausgeführt.
-
camper schrieb:
Hier wird also die Sortiergeschwindigkeit auf einem sortierten Array gemessen. Wen interessiert die?
Uns interessiert die weil sie auf Java kleiner ist!! Das allein macht sie schonmal interessant.
@tntnet: +1 für ersten konstruktiven Beitrag.
Mein Gott Leute, der Threadersteller hat sich an die Regeln gehalten, hat sich neutral verhalten, hat sich sehr kooperativ gezeigt.
Warum muss er seit über 4 Seiten gegen Fanboys kämpfen?Selbst wenn Java *nur* bei der Sortiergeschwindigkeit auf einem (invers) sortierten Array schneller als C++ ist wär das für mich schonmal interessant und erstaunlich. Also warum gehen wir der Sache nicht auf den Grund?
Oder glaubt ihr da waren VW-Techniker am Werk und javac erkennt Bubble-Sort als Testfall?
-
Atlan schrieb:
@Nathan
Ich wollte ja gerade keine Wrapper oder API benutzen, weil ich die Sprache und nicht die API oder die Programmierer der API testen wollte.Hast du den Test etwa in Java mit händischen Mitteln gemacht? Vielleicht solltest du mal für alle 3 Fälle den Code liefern. Davon abgesehen das die Bibliotheken häufig weit besser optimiert sind als ein Standardbenutzer es tut.
-
Ich arbeite seit 15 Jahre mit Java, und wir haben auch rechtgroße Desktop-Anwendungen.
Die gesamte Java-Welt hat sich geändert, ja weiterentwickelt. In manchen Dingen ist java richtig flott geworden, in machen immer noch elend langsam. Das ist die Realität. Wenn dieser eine Test C++ schlägt, kann man damit leben. Aber es gibt noch so viele andere Java-Szenarien, die ich hier habe, wo ich denke "Mein Gott, wird das heute noch was?".Der JVM-Hotspot hat zur Laufzeit Informationen, die er on-the-fly für sich nutzen kann um noch was zu optimieren. Das hat man mit statischen C++ Code nicht.
Aber die C++-Welt hat natürlich auch nicht geschlafen. Es gibt von heutigen C++ Compilern die Profilgesteuerte Optimierungen (PGO). Ich selber habe damit noch keine praktische Erfahrung. Aber im Prinzip ist es das, was der JVM-Hotsport macht: Lautzeitinformationen gewinnen um den Code zu optimieren.
-
@dot
Mittlerweile entspricht mein Code mehr C++ Code, als portiertem Java Code.
Gebenchmarkt habe ich im Release Build als Standalone.
Warum summiere ich kleine Deltas auf: Ich wollte einzig und allein den Algorithmus benchmarken und keine Methodenaufrufe oder for-Schleifen, die nicht zum BubbleSort gehören.
Warum die ganze Initialisierung betreiben: Würde ich das nicht tun, hätte ich beim zweiten Mal ein fertig sortiertes Array und der Test wäre Schrott.
I/O betreibe ich nur außerhalb des Sortierens und das war auch mehr für Debuggingzwecke, damit ich testen kann, dass wirklich etwas getan wird. Da I/O nur außerhalb des Durchlaufs ist, habe ich es drin gelassen.
Input wird auch nur ein mal betrieben.
Wie kompiliere ich: Visual Studio 2015
General: x64, Windows 10 als Zielplattform, Version der Zielplattform: 10.0.10240.0, Plattform Toolset ist v140, es wird in eine Applikation (.exe) kompiliert, Multi-Byte Character Set, No Common Language Runtime SetupC/C++: Maximize Speed (/O2), Inline Function Expansion steht auf "Any Suitable (/Ob2), Enable Instrinct Functions: Yes (/Oi), Favor fast Code (/Ot), Omit Frame Pointers: Yes(/Oy), Enable Fiber-Safe Optimizations: Yes(/GT), Whole Program Optimization: Yes (/GL).
@tntnet
Danke, für diesen konstruktiven Beitrag.:)
Also meinst du, dass die Java Optimizer tatsächlich für ungefähr die doppelte Geschwindigkeit reichen?@Gast3
Ich habe C++ mit dem Buch "C++ lernen und professionell anwenden" gelernt, komme aber von Java, weshalb viele Algorithmen nicht neu sind (nur die Syntax und die Mittel).
Java habe ich aus der Schule und dem Internet.@sebi707
Danke.@scrontch
Vielen Dank.@asc
Ja. Der Code in Java ist mit händischen Mitteln ohne API, auch wenn man in Java an der API nicht so ganz vorbei kommt, da sogar Arrays von Object erben.
Der Code steht auf Seite 1 und 2. Der Javacode wurde bisher nicht verändert.
Das mit der Optimierung kann ich mir sehr gut vorstellen.
-
scrontch schrieb:
Oder glaubt ihr da waren VW-Techniker am Werk und javac erkennt Bubble-Sort als Testfall?
So aehnlich. Dieser und viele Benchmarks verwenden integer oder character zum testen und die sind bei java nunmal primitives und damit mehr C als idiomatisches Java. Von manchen fans wird dann gerne abgeleitet, dass der garbage collector geschwindigkeitsmaessig ja ueberhaupt nichts ausmache, aber wenn man dann richtige Objekte einsetzt, sieht das dann ganz anders aus.
-
Ich hatte langeweile und in der Uni das ganze mal mit verschiedenen Compilern auf verschiedenen CPUs ausgeführt (auf beiden Systemen läuft RHEL 6):
Intel i7-3770: Java: 2,8s GCC 4.9.2: 6,4s Clang 3.6.2: 3,7s ICC 15.0.0: 3,2s Intel Xeon E5-2620 v2: Java: 10,2s GCC 4.9.2: 9,6s Clang 3.6.2: 5,6s ICC 15.0.0: 7,1s
Irgendwie scheint Java auf dem i7 ein glückliches Händchen zu haben was Optimierungen angeht. Auf dem Xeon sieht es allerdings sehr schlecht aus. Ich habe mal kurz ins Disassembly geschaut und es scheint als ob Clang und ICC loop unrolling für die do-while-Schleife gemacht haben. GCC konnte ich auf die schnelle nicht dazu überreden.
-
Atlan: Mit welchen Arraygrößen testest du überhaupt? Bedenke außerdem, dass du mit einem invers sortierten Array einen extremen Sonderfall testest, weil der Test, ob zwei Elemente getauscht werden sollen, immer anschlägt. Sowas kann der Java-Optimierer (in meiner Vorstellung) zur Laufzeit erkennen und den Code entsprechend umsortieren, sodass der tatsächlich ausgeführte Codepfad linear im Speicher liegt.
-
Atlan schrieb:
Wie kompiliere ich: Visual Studio 2015
General: x64, Windows 10 als Zielplattform, Version der Zielplattform: 10.0.10240.0, Plattform Toolset ist v140, es wird in eine Applikation (.exe) kompiliert, Multi-Byte Character Set, No Common Language Runtime SetupC/C++: Maximize Speed (/O2), Inline Function Expansion steht auf "Any Suitable (/Ob2), Enable Instrinct Functions: Yes (/Oi), Favor fast Code (/Ot), Omit Frame Pointers: Yes(/Oy), Enable Fiber-Safe Optimizations: Yes(/GT), Whole Program Optimization: Yes (/GL).
Das sieht für mich prächtig aus.
Unter Linux verwende ich immer -march=native
Kannste mal schauen, ob
/favor:INTEL64
noch was bringt?Entsprechend @sebi707, haste -march=native an?
-
volkard schrieb:
Entsprechend @sebi707, haste -march=native an?
Jop.
-
sebi707 schrieb:
volkard schrieb:
Entsprechend @sebi707, haste -march=native an?
Jop.
Hab's befürchtet. Meiner Erfahrung nach tut sich kaum da kaum was: Sobald man x64 hat, bringt's sauwenig, noch auf den eigenen Prozessor zu optimieren. Also liegts nicht an dieser Erlaubnis des Jitters.
-
@sebi707
Mein Wissen über Assembler ist leider zu beschränkt, als das ich da reingucken könnte und etwas verstehen würde.@m.e.
Anscheinend kann er es tatsächlich. Ich habe das Array mit Zufallszahlen zwischen -1 und 100.000 befüllt und Java hat 15 Sekunden gebraucht. Allerdings hat C++ mit derselben Modifikation knapp unter 18 Sekunden gebraucht.@volkard
Ich habe /favour:INTEL64 hinzugefügt, jedoch gab es keinen messbaren Unterschied vorher und nachher.
-
Gast3 schrieb:
kannst du mal "deine" C++, Java Quellen hier einstellen?
Das sind fast genau die, die hier schon standen. Java habe ich gar nicht angefasst und hier mein C++-code:
#include <iostream> #include <ctime> #include <vector> using namespace std; class Main { public: Main(int, int); private: void initArray(int elems); long sortList(int); std::vector<int> array; }; Main::Main(int times, int elems) { std::vector<long> timesEach(times); long calcAid = 0; cout << "Total times speedtest will run: " << times << endl; for (int i = 0; i < times; i++) { cout << "Time " << i << " out of " << times << endl; cout << "Initializing List" << endl; initArray(elems); cout << "Finished initializing. List has " << elems << " elements" << endl; cout << "Starting sorting" << endl; timesEach[i] = sortList(elems); cout << "Time: " << timesEach[i] << endl; } for (int i = 0; i < times; i++) { long ms = timesEach[i]; calcAid += ms; } //double result = (double)((double)calcAid / (double)times) / CLOCKS_PER_SEC; double result = (double)((double)calcAid / (double)times); cout << "Average time taken for sorting process in Microseconds: " << result << endl; } long Main::sortList(int elems) { int observedElems = elems; //noch zu behandelnde Objekte bool swapped; //wurde schon gewechselt? Muss nicht initialisiert werden, da in do-while Schleife erledigt int aid; //Hilfsvariable zum Tauschen der Werte time_t StartingTime, EndingTime, ElapsedMicroseconds; //Zeit StartingTime=clock(); // starten do {//do-while-Schleife: Wird mindestens ein mal ausgeführt, aber nur so oft wiederholt, wie die Bedingung stimmt swapped = false; //wahrheitsgemäß. Es wurde noch nicht getauscht for (int i = 1; i < observedElems; i++) { //for-Schleife durchäuft Array von 1 bis observedElems - 1 if (array[i - 1] < array[i]) { //wenn der Wert an der Stelle i - 1 größer ist, als an Stelle i aid = array[i - 1]; //der Wert von array an Ort i - 1 wird in aid geschrieben array[i - 1] = array[i]; //der Wert an Stelle i wird an Stelle i - 1 geschrieben array[i] = aid; //der Wert von aid wird an Stelle i geschrieben swapped = true; //wahrheitsgemäß. Es wurde getauscht } } observedElems--; //das gerade eingefügte Element muss nicht mehr überprüft werden; observedElems um einen decrementieren } while (swapped);//Bediungung der do-while-Schleife EndingTime=clock(); //Zeitmessung ElapsedMicroseconds = (EndingTime-StartingTime)/(double(CLOCKS_PER_SEC)/1000); return (long) ElapsedMicroseconds; //verarbeiten } void Main::initArray(int elems) { array.resize(elems); for (int i = elems - 1; i >= 0; i--) { this->array[i] = i; } } int main(int count, char** args) { Main main(5,100000); }
Also ich konnte die Finger nicht von den Pointern lassen
. Ansonsten ist es wirklich nicht mein Stil. Wenn man den Code mit dem Java-Code vergleicht, kann man erkennen, dass die gemessene Schleifen exakt gleich sind. Was drum herum ist, interessiert eigentlich nicht. Ich habe noch ein wenig weiter geforscht. Ich habe jetzt noch den clang hinzugefügt. Das Ergebnis ist:
gcc: 8,1s java: 3,6s clang: 4.0s!!!
In diesem Fall scheint also der gcc speziell weniger gut zu optimieren. Der erwähte switch -march=native bewirkt gar nichts. Gcc ist übrigens Version 5.1.1, clang Version 3.7.0 und das Java meldet sich als openjdk version 1.8.0_65. Alles auf Fedora 23 auf einem inzwischen recht alten Intel i5 mit 2,67 GHz.
-
sebi707 schrieb:
Irgendwie scheint Java auf dem i7 ein glückliches Händchen zu haben was Optimierungen angeht. Auf dem Xeon sieht es allerdings sehr schlecht aus. Ich habe mal kurz ins Disassembly geschaut und es scheint als ob Clang und ICC loop unrolling für die do-while-Schleife gemacht haben. GCC konnte ich auf die schnelle nicht dazu überreden.
Das ist schon interessant. Mich würd echt noch interessieren, welchen Code Hotspot hier erzeugen kann. Selbst wenn man weiß, dass man auf einem i7 läuft, wie würde man das implementieren? Kann man sich den von Hotspot erzeugten Code ausgeben lassen?
-
Jetzt lohnt es sich vlt noch das mit der standardfunktion zu vergleichen.
-
Mechanics schrieb:
Kann man sich den von Hotspot erzeugten Code ausgeben lassen?
Hab ich mich auch schon gefragt. Soll wohl mit der Option "
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
" gehen. Allerdings fehlt mir dafür die hsdis-amd64.so Library. Wenn ich zuviel Langeweile habe versuche ich die mal zu besorgen oder selbst zu compilieren.roflo schrieb:
Jetzt lohnt es sich vlt noch das mit der standardfunktion zu vergleichen.
Welche Standardfunktion? Das
std::sort
schneller ist als Bullesort sollte klar sein. Oder meinst du Java Standardlibrary gegen C++ Standardlibrary?
-
Atlan schrieb:
@m.e.
Anscheinend kann er es tatsächlich. Ich habe das Array mit Zufallszahlen zwischen -1 und 100.000 befüllt und Java hat 15 Sekunden gebraucht. Allerdings hat C++ mit derselben Modifikation knapp unter 18 Sekunden gebraucht.Das kannst du daraus nicht schliessen.
Die Ausführung wird schonmal deswegen langsamer weil die Branch-Prediction der CPU nicht mehr helfen kann. Was auch der Grund ist warum der C++ Code langsamer wird.Was du probieren müsstest:
Erst ein paar Durchläufe mit zufälligen Zahlen machen und danach nochmal ein paar Durchläufe mit vorsortierten Zahlen.Also z.B. vergleichen:
10 Durchläufe random
t=schellste von(10 Durchläufe sortiert)vs.
10 Durchläufe sortiert
t=schellste von(10 Durchläufe sortiert)
-
Hat mal jemand profile guided optimization beim GCC und/oder ICC ausprobiert? Das ist meiner Erfahrung nach sowieso eine der stärksten Compileroptimierungen und diese Form der Optimierung ist vermutlich auch das, was Java hier den kleinen Vorteil gegenüber dem ICC und Clang gibt.