Java vs. C. Performance-Vergleich



  • 314159265358979 schrieb:

    Natürlich stimmt da was nicht. Aber kannst du mir sagen, wie ich da wieder raus kommen soll? Ich sehe eigentlich keinen Weg. So viel Mühe ich mir auch gebe (und das habe ich schon mehrmals versucht), die Leute suchen trotzdem nach dem kleinsten Fehler. Und wenn kein Fehler gefunden wird, wird nach dem am einfachst missinterpretierbaren gesucht. Was soll ich denn da tun? Ich würde gerne meinen Ruf verbessern, aber die Leute müssen das auch zulassen.

    Ich bin hier im Forum absolut neu und ich habe dich heute als Troll kennengelernt. Da gab es nichts zu suchen, um dich in eine Schublade zu stecken. Deine Beiträge lesen sich hinterher so als ob dir gar nicht bewusst sei, was du da eigentlich schreibst. Ich schlage dir vor, du liest den Thread ab Seite 10 nochmals durch...

    L. G.
    Steffo



  • Steffo schrieb:

    Ich bin hier im Forum absolut neu und ich habe dich heute als Troll kennengelernt. Da gab es nichts zu suchen, um dich in eine Schublade zu stecken. Deine Beiträge lesen sich hinterher so als ob dir gar nicht bewusst sei, was du da eigentlich schreibst. Ich schlage dir vor, du liest den Thread ab Seite 10 nochmals durch...

    L. G.
    Steffo

    Was du heute kennengelernt hast, war der besonders schlecht gelaunte, vom RL geplagte PI. Mein Verhalten heute war scheiße, das sehe ich auch ein. Aber das ist nicht immer so.



  • 314159265358979 schrieb:

    Was du heute kennengelernt hast, war der besonders schlecht gelaunte, vom RL geplagte PI. Mein Verhalten heute war scheiße, das sehe ich auch ein. Aber das ist nicht immer so.

    Tja, ich habe kein Problem den Resetknopf zwischen uns beiden zu drücken, ich denke, bei dem Rest hier sieht das nicht anders aus. Wenn du lernst Schwächen anderer zu akzeptieren, wirst du auch deine Schwächen akzeptieren und dann wirst du auch angemessener anderen Leuten gegenüber antworten können.

    L. G.
    Steffo



  • ...PI hats gelesen. Posting gelöscht...



  • Nochmal ein kurzer Einwurf zum Thema:

    Im folgenden sind alle int durch int64_t bzw. long ersetzt, um Überläufe zu vermeiden.
    r=100000 - gcc 4.6, x86-64, march=native -O3:
    `real 0m48.759s

    user 0m48.750s

    sys 0m0.000s`

    r=100000 - openJDK 6b22:
    `real 1m59.765s

    user 1m59.750s

    sys 0m0.050s`

    Und jetzt noch einmal C++ ohne -march=native (amdfam10):
    `real 1m6.583s

    user 1m6.490s

    sys 0m0.010s`

    -march=native bringt bei mir also durchaus eine ganze Menge, fast 30%.
    Dabei ist der einzig relevante Unterschied zwischen den beiden Erzeugnissen, dass mit march=native an zwei Stellen incq statt addq $1 verwendet wird.



  • Der Fairness halber: Könntest du evtl. mal das neueste JDK7 runterladen und dann nochmals testen?
    Fedora bietet bei mir das neueste JDK noch nicht an.



  • Mit dem openJDK 7 komme ich auf folgende Werte:
    `real 1m54.053s

    user 1m54.060s

    sys 0m0.050s`

    Also knapp 5% besser.

    Edit: wo wir gerade dabei sind:
    r=10000(!) gcj-4.6 -O3 march=native
    `real 0m36.228s

    user 0m36.100s

    sys 0m0.090s`

    Das mit r=100000 läuft immer noch und wenn ich mal hochrechnen darf, wird's wahrscheinlich eine Stunde dauern.
    Hätte es gerne auch mit LTO ausprobiert um Inlining zu ermöglichen, aber den Versuch quittiert mir sowohl gcj-4.5 wie auch 4.6 mit unterschiedlichen ICEs.

    Jedenfalls hat da die JRE doch deutlich die Nase vorn.



  • Hätte ehrlich gesagt nicht gedacht, dass C++ Java immernoch so sehr überlegen ist. Oo Bin da ehrlich gesagt etwas enttäuscht!



  • Gregor schrieb:

    Java wird da aus folgendem Grund ein Problem bekommen: Es gibt in Java einen Overhead für jedes Objekt, der 8 Byte groß ist. Zudem steckst Du nicht die Objekte in das Array, sondern nur Zeiger auf diese Objekte. Auch nochmal 8 Byte. Deshalb sind Java-Objekte einfach viel größer als C++ Objekte. Du sprengst den Cache damit früher, brauchst mehr Speicher und so weiter.

    Nochmal zum Verständnis: Willst du damit sagen, dass in Java Objekte in Arrays so etwas wie verkettete Listen sind?!



  • Steffo schrieb:

    Gregor schrieb:

    Java wird da aus folgendem Grund ein Problem bekommen: Es gibt in Java einen Overhead für jedes Objekt, der 8 Byte groß ist. Zudem steckst Du nicht die Objekte in das Array, sondern nur Zeiger auf diese Objekte. Auch nochmal 8 Byte. Deshalb sind Java-Objekte einfach viel größer als C++ Objekte. Du sprengst den Cache damit früher, brauchst mehr Speicher und so weiter.

    Nochmal zum Verständnis: Willst du damit sagen, dass in Java Objekte in Arrays so etwas wie verkettete Listen sind?!

    Nein. ...vielleicht erläutere ich das morgen nochmal genauer.



  • Ändern sich die Java Zeiten wenn die Applikation mehrmals nacheinander gestartet wird?



  • Gregor schrieb:

    Steffo schrieb:

    Gregor schrieb:

    Java wird da aus folgendem Grund ein Problem bekommen: Es gibt in Java einen Overhead für jedes Objekt, der 8 Byte groß ist. Zudem steckst Du nicht die Objekte in das Array, sondern nur Zeiger auf diese Objekte. Auch nochmal 8 Byte. Deshalb sind Java-Objekte einfach viel größer als C++ Objekte. Du sprengst den Cache damit früher, brauchst mehr Speicher und so weiter.

    Nochmal zum Verständnis: Willst du damit sagen, dass in Java Objekte in Arrays so etwas wie verkettete Listen sind?!

    Nein. ...vielleicht erläutere ich das morgen nochmal genauer.

    Ok, wenn Du in Java ein Array von Point Objekten mit jeweils 2 int Variablen (2 * 4 Byte) hast, dann sieht das IMMER so aus:

    ---------   ---------   ---------
      |Point 1|   |Point 2|   |Point 3|
      | 8+4+4 |   | 8+4+4 |   | 8+4+4 |     (3 Point
      | Byte  |   | Byte  |   | Byte  |      Objekte)
      ---------   ---------   ---------
          ^           ^           ^
          |           |           |         (3 Zeiger)
    ------|-----------|-----------|------
    |     |     |     |     |     |     |
    |     |     |     |     |     |     |   (Array mit
    |     X     |     X     |     X     |    3 Elementen)
    |  8 Byte   |  8 Byte   |  8 Byte   |
    |           |           |           |
    -------------------------------------
    

    In jedem Array-Platz ist nur ein Zeiger auf das jeweilige Objekt gespeichert. Ich gehe von einer 64-Bit JVM aus, da sind diese Zeiger dann natürlich 8 Byte groß. Zudem hat jedes Objekt einen Overhead von 8 Byte, in dem praktisch abgespeichert ist, zu welcher Klasse dieses Objekt gehört. Das macht insgesamt 8+8+4+4=24 Byte Speicherbedarf pro gespeichertem Point Objekt.

    In C++ kannst Du hingegen auch so etwas haben:

    -------------------------------------
    | --------- | --------- | --------- |
    | |Point 1| | |Point 2| | |Point 3| |
    | |  4+4  | | |  4+4  | | |  4+4  | |
    | |  Byte | | |  Byte | | |  Byte | |
    | --------- | --------- | --------- |
    -------------------------------------
    

    Hier kannst Du die Objekte also direkt ins Array stecken und hast nicht notwendigerweise eine Indirektion durch irgendwelche Zeiger, die viel Platz benötigen. Zudem hast Du hier keinen Overhead, in dem der Typ des Objekts steht. Zusammengefasst hast Du hier also nur 4+4=8Byte Speicherbedarf pro gespeichertem Point Objekt.

    Natürlich macht sich das vor allem bei Massen an sehr kleinen Objekten bemerkbar. Aber dann kann es eben durchaus sein, dass ein Javaprogramm ein Vielfaches des Speichers benötigt, den ein entsprechendes C++Programm benötigt.

    War das verständlich?



  • Die Erklärung ist verständlich, danke Gregor. Aber weshalb werden in Java auf diese Weise Objekte mit so viel Overhead implementiert? Hat das unter anderem etwas mit Reflections zu tun?

    L. G.
    Steffo



  • Steffo schrieb:

    Aber weshalb werden in Java auf diese Weise Objekte mit so viel Overhead implementiert?

    Weil Java keine benutzerdefinierten Typen mit Wertsemantik kennt. Imo eine der ganz ganz großen Schwächen von Java.



  • dot schrieb:

    Steffo schrieb:

    Aber weshalb werden in Java auf diese Weise Objekte mit so viel Overhead implementiert?

    Weil Java keine benutzerdefinierten Typen mit Wertsemantik kennt. Imo eine der ganz ganz großen Schwächen von Java.

    Wenn man eine Wertzuweisung möchte, dann kann man ja die Clone-Methode benutzen. Referenzzuweisung ist ja eigentlich etwas, was Speicherplatz spart und billig ist.

    L. G.
    Steffo



  • 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.278s

    C:

    real 0m1.707s
    user 0m0.000s
    sys 0m1.685s

    Offenbar 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...


Anmelden zum Antworten