Java vs. C. Performance-Vergleich
-
Der Punkt ist aber, dass du Objekte (von int, float etc. mal abgesehen) in Java ausschließlich nur über Referenzen ansprechen kannst. Es gibt keinen Weg Objekte direkt anzufassen. Darum kannst du auch nur Referenzen auf Objekte in Container packen und nicht die Objekte selbst, was die Implementierung von "Generics" in Java natürlich trivial macht. Referenzzuweisungen sind vielleicht billig. Aber welcher Code besteht schon einzig und allein aus Zuweisungen und der Zugriff auf ein Objekt über eine Referenz ist dagegen nicht gratis. Abgesehen davon sind Referenzen und Werte einfach rein semantisch völlig was anderes. Das eine ist kein Ersatz für das andere...
-
Ich hab mich mal mit dem Thema neu beschäftigt und war ziemlich erstaunt:
File Copy einer Datei mit einer Größe von 733.6 MB:
Java:
real 0m1.415s
user 0m0.124s
sys 0m1.278sC:
real 0m1.707s
user 0m0.000s
sys 0m1.685sOffenbar ist Java hier in zwei von drei Bereichen schneller.
Hier der Code (der nicht auf Sicherheit getrimmt ist):
import java.io.*; import java.nio.channels.FileChannel; public class FileChannelCopy { public static void main(String[] args) throws IOException { // Pfade entsprechend anpassen File inF = new File(args[0]); File outF = new File(args[1]); copyFile(inF, outF); } public static void copyFile(File in, File out) throws IOException { FileChannel inChannel = new FileInputStream(in).getChannel(); FileChannel outChannel = new FileOutputStream(out).getChannel(); try { inChannel.transferTo(0, inChannel.size(), outChannel); } catch (IOException e) { throw e; } finally { if (inChannel != null) inChannel.close(); if (outChannel != null) outChannel.close(); } } }
#include <stdio.h> /* fprintf */ #include <string.h> /* strerror */ #include <stdlib.h> #include <fcntl.h> /* open, O_RDONLY, O_WRONLY, O_CREAT, O_EXCL */ #include <sys/stat.h> /* mode_t, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH */ #include <unistd.h> /* read, write */ #include <errno.h> /* errno */ int main(int argc, char *argv[]) { const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; /* rw-r--r-- */ int in, out; /* Dateideskriptoren */ unsigned char *b; int size; struct stat buf; if (argc != 3) { fprintf(stderr, "Aufruf: %s Quelle Ziel\n", argv[0]); return 1; } in = open(argv[1], O_RDONLY); if (in == -1) { fprintf(stderr, "Quelle %s kann nicht geoeffnet werden (errno %d: %s)\n", argv[1], errno, strerror(errno)); return 1; } if (fstat(in, &buf) == -1) { fprintf(stderr, "Es konnten keine Informationen aus Quelle '%s' ausgelesen werden (errno %d: %s)\n", argv[1], errno, strerror(errno)); return 1; } size = (int) buf.st_size; printf("Größe: %d\n", size); b = (unsigned char*) malloc(sizeof(unsigned char) * size); out = open(argv[2], O_WRONLY | O_CREAT | O_EXCL, mode); if (out == -1) { fprintf(stderr, "Ziel %s kann nicht erzeugt werden (errno %d: %s)\n", argv[2], errno, strerror(errno)); return 1; } if (read(in, b, size) == -1) { fprintf(stderr, "Quelle %s kann nicht gelesen werden (errno %d: %s)\n", argv[1], errno, strerror(errno)); return 1; } if (write(out, b, size) == -1) { fprintf(stderr, "Schreibfehler (errno %d: %s)\n", errno, strerror(errno)); return 1; } close(out); close(in); free(b); return 0; }
Die Datei soll in einem Stück ausgelesen und geschrieben werden.
Verwendet wurden OpenJDK 6 und GCC 4.6.2 mit den Optionen -W -Wall -pedantic -O3 -march=core2.
Vielleicht kennt jemand eine perfomantere C-Implementierung?
Ich habe hier mal POSIX-Aufrufe genommen, weil ich dachte, dass es nichts schnelleres als Systemaufrufe gibt.
L. G.
Steffo
-
Warum der Code sowieso suboptimal ist wurde dir ja schon im java forum gesagt, ansonsten ist ein Test, der nur eine große Datei kopiert höchst ungeeignet um 2 Sprachen miteinander zu vergleichen. Das einzige was du da misst ist die Geschwindigkeit deiner Festplatte/des Treibers.
-
Steffo schrieb:
Vielleicht kennt jemand eine perfomantere C-Implementierung?
http://linux.die.net/man/2/sendfile
nur so nebenbei, ich habe noch keine app gesehen und ich rede hier nicht von ein paar zeilen code, wo java c outperformed hätte
-
gassssssst schrieb:
Warum der Code sowieso suboptimal ist wurde dir ja schon im java forum gesagt, ansonsten ist ein Test, der nur eine große Datei kopiert höchst ungeeignet um 2 Sprachen miteinander zu vergleichen. Das einzige was du da misst ist die Geschwindigkeit deiner Festplatte/des Treibers.
Die Vorgehensweise ist doch bei beiden gleich: Erst die Datei komplett in einen Buffer lesen und dann auf einen Schlag schreiben. Das Festplattenargument versteh ich nicht ganz, schließlich laufen beide Programme auf demselben System und es gibt einen Geschwindigkeitsunterschied.
Mit der gleichen Logik kann man sagen, dass Benchmarks immer nur die Prozessorgeschwindigkeit messen...
-
das os cached von der platte gelesene daten in ram.
-
KingKarl schrieb:
Steffo schrieb:
Vielleicht kennt jemand eine perfomantere C-Implementierung?
Werde ich später mal ausprobieren, danke.
-
Steffo schrieb:
Die Vorgehensweise ist doch bei beiden gleich: Erst die Datei komplett in einen Buffer lesen und dann auf einen Schlag schreiben.
wo hast das gelesen? kennst du die implementation von obj.transferTo?
-
Probier mal http://msdn.microsoft.com/en-us/library/windows/desktop/aa363851(v=vs.85).aspx
Und wenn du die Geschwinigkeiten irgendwie ernsthaft vergleichen willst, dann nimm nur Lese oder Schreibgeschwindigkeit. Aber auch das dürfte nicht sonderlich spannend sein. Das ist einfach nur ein Aufruf, und der limitierende Faktor ganz klar die Festplatte*. Streng genommen kann man schon sehen, dass irgendwo ein Fehler sein muss, sobald man stark abweichende Ergebnisse bekommt.
Wenn du die Geschwindigkeiten vergleichen willst, dann nimm irgendetwas mit vielen Rechenoperationen und Speicherzugriffen. Parsing, Lineare Algebra, ...
Edit:
* Und nein, das ist nicht das gleiche Argument wie "sonst misst man ja nur die CPU". Es geht ja eben darum zu schauen, mit welcher Sprache man die CPU optimaler ansprechen kann. Bei dem Festplattenzeugs hier wird aber ziemlich simpel ein Befehl ans System gesendet und das wars dann. Da haben die Sprachen nicht viel mit zu tun.
-
KingKarl schrieb:
Steffo schrieb:
Die Vorgehensweise ist doch bei beiden gleich: Erst die Datei komplett in einen Buffer lesen und dann auf einen Schlag schreiben.
wo hast das gelesen? kennst du die implementation von obj.transferTo?
schau mal was in der doku steht...
FileChannel.transferTo...
This method is potentially much more efficient than a simple loop that reads from this channel and writes to the target channel. Many operating systems can transfer bytes directly from the filesystem cache to the target channel without actually copying them.
-
Damit wurde bewiesen, dass mit java sehr wohl I/O-Benchmarks geschrieben werden können, wenn die Dateien gross genug sind. Benchmarking ist nicht so einfach. Schau mal hier:
$ time java Copy copy_c foo
real 0m0.061s
user 0m0.045s
sys 0m0.015s$ time ./copy_c copy_c foo
Größe: 8695real 0m0.002s
user 0m0.002s
sys 0m0.000sDamit kann ich beweisen, dass das C-Programm 2 Millisekunden braucht, das Java-Programm aber 61. Also ist C um den Faktor 30,5 schneller - oder? Oder beweist das, dass ein Java Programm einfach ca. 30 mal so lange braucht, bis es los läuft? Oder beweist das eher, dass der Vergleich Java vs. C mit einem I/O-Benchmark nicht wirklich entschieden werden kann?
-
tntnet schrieb:
...dass der Vergleich Java vs. C mit einem I/O-Benchmark nicht wirklich entschieden werden kann?
da gibts nichts zu entscheiden! es gibt nichts schnelleres als der cpu zu geben was sie braucht und das ist kein java.
-
OK, ich kann nun wieder Entwarnung geben.
Wenn ich das OS-spezifische(!) sendfile aufrufe, braucht esreal 0m1.294s
user 0m0.000s
sys 0m1.279sNochmals zur Erinnerung:
Java braucht:
real 0m1.415s
user 0m0.124s
sys 0m1.278sAber gut: Wie einige schon angemerkt haben, lässt sich mit Betriebssystemaufrufen nicht wirklich gute Benchmarks messen.
L. G.
SteffoPS:
Hier der Code:
#include <stdio.h> /* fprintf */ #include <string.h> /* strerror */ #include <stdlib.h> #include <sys/sendfile.h> #include <fcntl.h> /* open, O_RDONLY, O_WRONLY, O_CREAT, O_EXCL */ #include <sys/stat.h> /* mode_t, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH */ #include <unistd.h> /* read, write */ #include <errno.h> /* errno */ int main(int argc, char *argv[]) { const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; /* rw-r--r-- */ int in, out; /* Dateideskriptoren */ off_t *offset = NULL; unsigned int size; struct stat buf; if (argc != 3) { fprintf(stderr, "Aufruf: %s Quelle Ziel\n", argv[0]); return 1; } in = open(argv[1], O_RDONLY); if (in == -1) { fprintf(stderr, "Quelle %s kann nicht geoeffnet werden (errno %d: %s)\n", argv[1], errno, strerror(errno)); return 1; } if (fstat(in, &buf) == -1) { fprintf(stderr, "Es konnten keine Informationen aus Quelle '%s' ausgelesen werden (errno %d: %s)\n", argv[1], errno, strerror(errno)); return 1; } size = buf.st_size; out = open(argv[2], O_WRONLY | O_CREAT | O_EXCL, mode); if (out == -1) { fprintf(stderr, "Ziel %s kann nicht erzeugt werden (errno %d: %s)\n", argv[2], errno, strerror(errno)); return 1; } if ( sendfile(out, in, offset, size) == -1) { fprintf(stderr, "Ziel %s kann nicht geschrieben werden (errno %d: %s)\n", argv[1], errno, strerror(errno)); return 1; } close(out); close(in); return 0; }
-
danke für die mühe
-
Steffo schrieb:
OK, ich kann nun wieder Entwarnung geben.
Wenn ich das OS-spezifische(!) sendfile aufrufe, braucht esreal 0m1.294s
user 0m0.000s
sys 0m1.279sNochmals zur Erinnerung:
Java braucht:
real 0m1.415s
user 0m0.124s
sys 0m1.278sIch frag mich gerade, ob du die Zeiten überhaupt interpretieren kannst? Dass beide Programme für die Aufgabe die gleiche Zeit brauchen 0m1.279s == 0m1.278s
und der Startup der JVM 0m0.124s einfach ein strich in deine Rechnung setzt.
-
Ist sys nicht einfach nur der Systemaufruf und real die Summe von sys und user?
-
Steffo schrieb:
Ist sys nicht einfach nur der Systemaufruf und real die Summe von sys und user?
Eben. Und der sys-Aufruf ist bei dir bei beiden gleich lang, ist ja auch der gleiche Aufruf. Das C Programm braucht aber unmessbar kurz, um den Aufruf an sich durchzuführen, während die Java-VM 0.124s dazu braucht. Da Javasystemaufrufe ebenfalls unmessbar schnell gehen sollten, wird das wohl die Zeit sein, die die VM an sich zum Starten braucht. Das ist also die Zeit, die selbst bei Benchmarks von C-Befürwortern normalerweise rausgerechnet wird, da das doch ein bisschen unfair wäre (und sie es auch gar nicht nötig haben).
Du hast somit gezeigt:
1. Java und C Systemaufrufe sind die gleichen Systemaufrufe. Überraschung!
2. Java läuft in einer VM die eine gewisse Startzeit hat. Überraschung!
-
vllt. halten wir noch fest, dass die webseite der dab bank in java programmiert ist
-
Ich habe übrigens mal mit einem Shell-skript eine Datei kopiert. Damit ging es in etwa genauso schnell, wie mit Java. Also sind shell-skripte genauso schnell wie Java. Dabei dachte ich, so interpretiertes Zeugs, wie shell-skripte sollten doch langsamer sein.
-
ich bins schrieb:
Ich habe übrigens mal mit einem Shell-skript eine Datei kopiert. Damit ging es in etwa genauso schnell, wie mit Java. Also sind shell-skripte genauso schnell wie Java. Dabei dachte ich, so interpretiertes Zeugs, wie shell-skripte sollten doch langsamer sein.
Da ist ja auch die Festplatte und das Dateisystem das Bottleneck, nicht der Code