Java vs. C. Performance-Vergleich



  • Steffo schrieb:

    Der nächst Punkt zum Stack (wie gesagt, bin kein Profi, aber unser Prof hat das so gesagt 😉 ) ist der, dass beim Stack Rücksprungsadressen gespeichert werden, d. h. BufferOverflows bei dem eine eigene Rücksprungsadresse mit eigenem Code untergejubelt wird, problemlose möglich sind. Beim Heap geht das nicht.

    Das Unterjubeln geht immer nur, wenn Programmierfehler ein Loch aufmachen.

    Steffo schrieb:

    Außerdem kann man seine eigenen Programme mit valgrind (was unser Prof. für die C/C++-Programmiung für unverzichtbar hält) nicht vollständig testen, da valgrind nur den Heap beobachtet.

    Keine Ahnung, wie es mit C ist. Aber für C++ ist valgrind völlig unnötig.

    Und der Stack ist SCHNELL! Allokierung aller lokaler Variablen geschieht in einem Takt. Einfach die Größe aller lokaler Variablen vom Stackpointer abziehen. Beim Heap braucht man so um die 100 Takte pro Objekt (Na, sind immernoch 40 Mio pro Sekunde, normalerweise rechnet man dann auf den Objekten hundertmalsolang, also egal.) (geht auch schneller, mit Allokatoren, die auf die Anwendung angepaßt werden).



  • Michael E. schrieb:

    Auch Javaprogramme, die nicht Swing benutzen, werden als langsam empfunden.

    Z. B.?

    Der nächst Punkt zum Stack (wie gesagt, bin kein Profi, aber unser Prof hat das so gesagt 😉 ) ist der, dass beim Stack Rücksprungsadressen gespeichert werden, d. h. BufferOverflows bei dem eine eigene Rücksprungsadresse mit eigenem Code untergejubelt wird, problemlose möglich sind. Beim Heap geht das nicht.

    Was willst du damit sagen?

    Ich habe die Sicherheit angesprochen. Es ist prinzipiell sicherer ein Array auf dem Heap anzulegen als auf dem Stack.

    Außerdem kann man seine eigenen Programme mit valgrind (was unser Prof. für die C/C++-Programmiung für unverzichtbar hält) nicht vollständig testen, da valgrind nur den Heap beobachtet.

    Auf dem Stack können prinzipbedingt keine Speicherlecks auftreten. Also braucht valgrind auch nicht den Stack zu beobachten.

    Man kann aber problemlos den Speicherbereich auf dem Stack überschreiben und das wird valgrind nicht bemerken!

    L. G.
    Steffo



  • Steffo schrieb:

    Ich habe die Sicherheit angesprochen. Es ist prinzipiell sicherer ein Array auf dem Heap anzulegen als auf dem Stack.

    Ja, in C.
    In C++ kann man ein std::array (Arraygrenzencheck im Debug-Code vorausgesetzt) nehmen und ist sicher.



  • Also ich kenne ja nicht viel Java-Software, aber das, was ich kenne, was ne UI hat, ist wegen der Langsamkeit fast nie benutzbar. Jetzt macht man in Java natürlich auch keine Anwendungsprogramme, weil es auch einfach sehr hässlich ist.

    Man müsste Mal komplexe Serverframeworks vergleichen. Nur wird so was wiederum nicht in C++ programmiert. Aber wenn man zwei solche Umgebungen vergleichen würde, wären die Komplexitäten wohl zu hoch, als dass die Vergleiche vernünftig wären. Und da Java ja produktiv auch für Riesenprodukte eingesetzt werden kann, ist die Performance wohl zwangsläufig in Ordnung.

    Und wenn Java nicht für irgendwelche Dinge zu langsam ist, dann ist es eben auch wieder egal, richtig? Mit Java kommt man nur eben nicht an die Systeminterna dran, man kann wahrscheinlich auch nicht die Grafikkarte programmieren (oder muss da was durchreichen, was dann zu viel Zeit kostet), oder? Ohne solche Spielereien verliert man viel Zeit.



  • Steffo schrieb:

    Michael E. schrieb:

    Auch Javaprogramme, die nicht Swing benutzen, werden als langsam empfunden.

    Z. B.?

    Man sollte den Spieß umdrehen und nach schnellen Javaprogrammen fragen 😉 Javaprogrammierer, die ich getroffen habe, tendierten alle dazu, auch schon kleine Probleme totzudesignen. Dann hatte man auf einmal ein Dutzend Klassen, wo es eine Handvoll Funktionen auch getan hätten. Klar, dass das auf die Performance geht.



  • Zu der Sache mit dem stack overflow: Bei mir hat sich eclipse schon einige Male mit einer stack overflow exception verabschiedet, so gut scheint das in java also auch nicht gelöst zu sein 🙂



  • GorbGorb schrieb:

    Zu der Sache mit dem stack overflow: Bei mir hat sich eclipse schon einige Male mit einer stack overflow exception verabschiedet, so gut scheint das in java also auch nicht gelöst zu sein 🙂

    Das hat den Hintergrund, dass der Stack für gewisse Aufgaben tatsächlich unverzichtbar ist. Z. B. gerade im Bereich Compiler bzw. IDE, wo der Code auf Gültigkeit geprüft werden muss, arbeitet man mit Stackalgorithmen und da können sich natürlich Programmierfehler einschleichen. Aber Arrays werden in Java soweit ich weiss nicht im Stack, sondern im Heap angelegt.

    Liebe Grüße
    Steffo



  • Steffo schrieb:

    Das hat den Hintergrund, dass der Stack für gewisse Aufgaben tatsächlich unverzichtbar ist. Z. B. gerade im Bereich Compiler bzw. IDE, wo der Code auf Gültigkeit geprüft werden muss, arbeitet man mit Stackalgorithmen

    Damit ist aber nicht der Stack[TM] gemeint, über den wir hier reden.



  • Michael E. schrieb:

    Damit ist aber nicht der Stack[TM] gemeint, über den wir hier reden.

    OK, das stimmt. Peinlich. 🙂



  • Zum Swing ist mir gerade ein Ideechen gekommen.
    Also erstmal halte ich es für unverzichtbar, sich Klassen wie Point, Size, Rectangle und so zu schreiben und sie auch ganz heftig als Übergabeparameter zu benutzen. Ich habe mal ein Testprogramm geschrieben, das fast nichts anderes macht. Es berechnet PI.

    #include <iostream>
    using namespace std;
    
    class Point{
        public:
        int x;
        int y;
        Point(int xx,int yy){
            x=xx;
            y=yy;
        }
    };
    
    class Pi{
        private:
        int treffer;
        int radius;
        int radiusQuadrat;
        public:
        Pi(int r){
            radius=r;
            radiusQuadrat=radius*radius;
            treffer=0;
        }
        void run(){
            for(int y=-radius;y<=radius;++y)
                for(int x=-radius;x<=radius;++x)
                    shoot(Point(x,y));
        }
        void shoot(Point p){
            if(p.x*p.x+p.y*p.y<=radiusQuadrat)
                ++treffer;
        }
        double value(){
            return double(treffer)/double(radiusQuadrat);
        }
    };
    
    int main() {
        Pi p(10000);
        p.run();
        cout<<p.value()<<'\n';
    }
    

    Könnte das mal einer schnell nach Java übersetzen bitte?



  • Ich habe das mal mit bestem Willen portiert, wobei ich von C++ keine Ahnung habe.
    Allerdings habe ich mir erlaubt einige Optimierungen nach Java-Art vorzunehmen.

    public final class Main {
    
    	public static void main(final String[] args) {
    		final Pi p = new Pi(10000);
    	    p.run();
    
    	    System.out.println(p.value());
    	}
    }
    
    public final class Pi {
    	private int treffer;
    	private final int radius;
    	private final int radiusQuadrat;
    
        public Pi (final int r){
            radius = r;
            radiusQuadrat = radius*radius;
            treffer = 0;
        }
    
        public void run(){
            for (int y = -radius; y <= radius; ++y)
                for (int x = -radius; x <= radius; ++x)
                    shoot(new Point(x,y));
        }
    
        public void shoot (final Point p){
            if (p.x * p.x + p.y * p.y <= radiusQuadrat)
                ++treffer;
        }
    
        public double value(){
            return (double) (treffer) / (double) (radiusQuadrat);
        } 
    }
    
    public final class Point {
    	public final int x;
    	public final int y;
    
        public Point(final int xx, final int yy){
            x = xx;
            y = yy;
        } 
    }
    

    Werde auch gleich Performancetests durchführen...

    EDIT: Habe Point nochmals optimiert.

    Liebe Grüße
    Steffo



  • Steffo schrieb:

    Werde auch gleich Performancetests durchführen...

    Ich vermute, Du wirst über die Java-Performance entsetzt sein. 😃



  • Wird interessant, ob der Java Compiler shoot inlined und merkt, dass er kein Point erstellen muss.



  • HereIGo schrieb:

    Wird interessant, ob der Java Compiler shoot inlined und merkt, dass er kein Point erstellen muss.

    Ich denke nicht, dass er das macht. Ich habe in Java immer die Erfahrung gemacht, dass man kurzlebige Objekte eher vermeiden sollte. Bei C++ ist das hingegen nicht wichtig.



  • Also ich habe das mal mit Java 6 kompiliert (hat jemand Java 7 installiert?!) und mit gcc -03 getestet:

    [steffo@localhost performance]$ time ./pi-cpp
    3.14159
    
    real	0m2.598s
    user	0m2.595s
    sys	0m0.004s
    [steffo@localhost performance]$ time java Main
    3.14159053
    
    real	0m4.468s
    user	0m4.075s
    sys	0m0.474s
    

    Hier kommt die C++ Version ohne gcc-Optimierung (konnte ich mir nicht verkneifen :D):

    [steffo@localhost performance]$ time ./pi-cpp
    3.14159
    
    real	0m32.149s
    user	0m32.122s
    sys	0m0.009s
    

    Liebe Grüße
    Steffo



  • Michael E. schrieb:

    Steffo schrieb:

    Michael E. schrieb:

    Auch Javaprogramme, die nicht Swing benutzen, werden als langsam empfunden.

    Z. B.?

    Man sollte den Spieß umdrehen und nach schnellen Javaprogrammen fragen 😉

    http://www.heli-x.net/ Die Version 0.9 gibts kostenlos. http://www.heli-x.net/download.shtml
    Fühlt sich eigentlich nie wie Java an.



  • Steffo schrieb:

    Also ich habe das mal mit Java 6 kompiliert (hat jemand Java 7 installiert?!) und mit gcc -03 getestet:

    [steffo@localhost performance]$ time ./pi-cpp
    3.14159
    
    real	0m2.598s
    user	0m2.595s
    sys	0m0.004s
    [steffo@localhost performance]$ time java Main
    3.14159053
    
    real	0m4.468s
    user	0m4.075s
    sys	0m0.474s
    

    [/code]

    Oh, interessant. Ich hätte gedacht, dass Java da wesentlich schlechter abschneidet. Kannst Du das auch nochmal mit Werten überpüfen, die eine Größenordnung größer sind. Benchmarks, die 2-3 Sekunden dauern, zeigen in Java zu einem großen Anteil das Laden der JVM.

    Außerdem kommt hier die nächste Herausforderung. Da wird Java ein echtes Problem bekommen:

    Sortiere ein Array mit 300.000 Point-Objekten nach ihrem Abstand vom Ursprung.

    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.



  • HereIGo schrieb:

    Wird interessant, ob der Java Compiler shoot inlined und merkt, dass er kein Point erstellen muss.

    Anscheinend ist das der Fall! 😮 😮 😮
    War es nicht so, daß Java eine Exception schmeißen muß, wenn es integer-Überläufe gibt? Die würden vielleicht das Bißchen mehr an Laufzeit erklären. Kann mir kaum vorstellen, daß new nebst Garbage-Collection so billig ist. Ok, new ist sehr billig, klar. Oder war genug Speicer da, daß kein gc-Lauf überhaupt nötig war?

    Naja, prinzipiell ist es möglich, einen Optimierer zu schreiben, der erkennt, daß gar kein Point erstellt werden muß. Und prinzipiell ist es möglich, einen Optimierer zu schreiben, der erkennt, daß da noch keine Überläufe entstehen können. Wir messen hier nicht Sprachen aus, sondern nur derzeitige Implementierungen.

    Ich hatte vermutet, Jave würde abkacken. Das ist nicht der Fall. 100% mehr ist nichts. Diese langsamen gefühlten Java-Oberflächen haben weit mehr als 1000%. Also daran lag es wohl nicht. Aber am Code liegt es auch nicht, mag ich meinen. Was ich da so gelesen habe, war einfach, zielstrebig, offensichtlich richtig, elegant und bescheiden, offensichtlich schnell. Ich würde mir wünschen, C++-Programmierer würden so gut coden. Warum Java-Anwendungen so unglaublich oft endlos lahm sind, mir ist es ein Rätsel.

    ps: Bitte immer mit -march=native testen. Der Java-Jitter darf ja auch den Prozesssor kennen.



  • Steffo: Kannst du mal die final aus dem Point raus machen und schauen, ob das was ändert?



  • Ich habe mal dem Pi-Konstruktor 100000 als Parameter übergeben, wobei sowohl bei der C++- als auch bei der Java-Version Blödsinn rauskommt (es kommt kein PI, sondern eine Zahl kleiner 0 raus):

    [steffo@localhost performance]$ time ./pi-cpp
    -1.003
    
    real	4m19.205s
    user	4m18.967s
    sys	0m0.114s
    
    [steffo@localhost performance]$ time java Main
    -1.0029964730543903
    
    real	6m40.989s
    user	6m41.627s
    sys	0m1.991s
    

    ps: Bitte immer mit -march=native testen. Der Java-Jitter darf ja auch den Prozesssor kennen.

    Das mache ich extra nicht. Weshalb? Zeig mir mal Software, die auf diese Weise kompiliert wird. Das wird man in der Praxis kaum finden. Die Software, die auf deinem Computer läuft wurde garantiert nicht so kompiliert, es sei denn du benutzt Gentoo. Die Stärke von Java ist ja gerade, dass es zur Laufzeit optimieren kann.

    Steffo: Kannst du mal die final aus dem Point raus machen und schauen, ob das was ändert?

    Kommt gleich. 😉

    L. G.
    Steffo


Anmelden zum Antworten