Java Performance einer Methode

limoneneis

Cadet 4th Year
Registriert
Okt. 2009
Beiträge
72
Hallo ich habe eine Methode geschrieben, die Daten aus einer Datenbank in Arrays im Speicher vom Java-Programm schreibt. Wenn ich diese Methode nicht ausführe braucht mein Programm nur 4 Sekunden. Mit der Methode 8 Sekunden. Ich wollte mal fragen, ob man erkennen kann woher die Performanceeinbuße kommt?

Habt ihr Tipps um das Programm zu beschleunigen?

Code:
   final public void initNachfrageAnStationen() {
        try {
            ResultSet r;
            for (int i = 0; i <= stationen.size() - 1; i++) { //ArrayListe: Größe=60
                int z = 0;
                String s = stunde < 10 ? "0" + stunde : "" + stunde;
                String t = stationen.get(i).getId() + "";

                prepStmtNachfrage.setString(1, s); //Dahinter steckt eine sehr
                prepStmtNachfrage.setString(2, t); //einfache Abfrage
                prepStmtNachfrage.setString(3, t);

                ResultSet rsN = prepStmtNachfrage.executeQuery(); //ausführen

                double[] entl = new double[stationen.size()]; //Größe des Arrays = 60
                double[] rueckg = new double[stationen.size()]; // size = 60

                

                while (rsN.next()) {
                    
                    String ent = rsN.getString("entleihstation");
                    String rue = rsN.getString("rueckgabestation");
                    double fahrt = rsN.getDouble("fahrten");
                    
                    if (t.equals(ent)) {
                        r = v.output("SELECT index FROM stationen" //Methode output liefert 
                                + " WHERE id = '"                                   //Resultset der Query
                                + rue + "'");

                        while (r.next()) {
                            entl[r.getInt("index")] = fahrt; //Fahrt gespeichert
                                                                            //an der richtigen Stelle im Array
                            
                            if(ent.equals(rue)){
                                rueckg[r.getInt("index")] = fahrt;
                            }
                        }
                    } else if (t.equals(rue)) {
                        r = v.output("SELECT index FROM stationen"
                                + " WHERE id = '"
                                + ent + "'");

                        while (r.next()) {
                            rueckg[r.getInt("index")] = fahrt;
                        }
                    }

                    

                }

                stationen.get(i).setEntleihungen(entl);
                stationen.get(i).setRueckgaben(rueckg);

            }
        } catch (SQLException ex) {
            System.err.println(ex.getMessage());
        }
    }
 
Wie viele Daten sind denn in der DB ? Wenn du da 10000 Datensätze holst, kann das halt schon mal kurz dauern


EDIT:
wenn ich das richtig verstanden hab, verschweigt dein Code eine DB-Anfrage richtig? Und für jedes Ergebnis in dieser Anfrage machst du wieder eine Anfrage. Kein wunder, dass das langsam ist.

Du solltest das Ganze zusammen in einer Anfrage lösen!
(d.h. mit einem oder mehreren JOINs)


EDIT2:
Das was du machst:
1. Einkaufsliste schreiben
2. Für jeden Eintrag in der Liste zum Laden laufen, bezahlen und wieder nach Hause

Wie es einfacher geht:
1. Alles auf einmal kaufen und bezahlen
 
Zuletzt bearbeitet:
Wie benneque bereits anmerkte, du führst das Query in der for Iteration aus, das kostet jedes Mal I/O, und die sind sehr teuer.

Grüße
 
Zuletzt bearbeitet:
Seh ich das richtig, dass du deine Datenbank wie ne Excel-Tabelle bearbeitest, also:
Feld Nr. 1 bitte, Feld Nr. 2 bitte...

Datnebanken wollen clever bedient werden, eine Abfrage, 1 Mio. Änderungen, das ist gut, eine Abfrage, 100 Zeilen Antwort (die du alle brauchst), das ist gut.

Eine Abfrage, eine Änderung, aber 1 Mio mal, das ist schlecht (Synchronisation und Geschwindigkeit ist voll im Eimer), wenn du's unbedingt machen musst, setAutoCommit(false); do... setAotuCommit(true);
 
Danke für die Tipps. Ich werde das mal mit einer schlaueren Query versuchen. Das war bisher einfach nur mein erster Ansatz.
Ergänzung ()

:)

nun braucht mein Prog statt 1:17 Minuten nur noch 2 Sekunden!
 
Hi,


Die DB hat 50.000 Zeilen. Das sind Daten aus dem Öffentlichen Verkehr. Die Zeit habe ich mit Netbeans gestoppt, also soetwas:

BUILD SUCCESSFUL (total time: 2 seconds)

Bin eigentlich zufrieden mit der Dauer. Ich instanziiere diese Klasse jedoch 24 mal (für jede Stunde), wodurch diese Methode 24 mal ausgeführt wird. Aber komplexer wird mein Prog nicht.

Code:
    final public void initNachfrageAnStationen() {
        try {

            ResultSet rsN = prepStmtNachfrage.executeQuery(); //ausführen

            for (int i = 0; i <= stationen.size() - 1; i++) {
                double[] entl = new double[stationen.size()];
                double[] rueckg = new double[stationen.size()];
                stationen.get(i).setEntleihungen(entl);
                stationen.get(i).setRueckgaben(rueckg);
            }

            while (rsN.next()) {

                int ent = rsN.getInt("entleihstation");
                int rue = rsN.getInt("rueckgabestation");

                int eI = rsN.getInt("entlIndex");
                int rI = rsN.getInt("rueckIndex");

                double fahrt = rsN.getDouble("fahrten");


                for (int i = 0; i <= stationen.size() - 1; i++) {

                    Station s = stationen.get(i);

                    if (ent == s.getId()) {
                        s.getEntleihungen()[rI] = fahrt;
                        if (rue == ent) {
                            s.getRueckgaben()[rI] = fahrt;
                        }

                    } else if (rue == s.getId()) {
                        s.getRueckgaben()[eI] = fahrt;

                    }
                }

            }

        } catch (SQLException ex) {
            System.err.println(ex.getMessage());
        }
    }
 
Wofür ist denn die Build-Dauer relevant bzw. warum buildest du das Programm überhaupt jedes Mal neu? Wenn es so funktioniert, pack es doch in ein JAR und führe das stündlich aus.
 
Mit schnell drüber schauen kann ich dir noch eine Performanceoptimierung sagen. (Wenn ich annehme das du viele Stationen hast)
Erstelle am Beginn der Methode z.B.: eine HashMap und verwende die id und das Object selbst.
Dann kannst du mit ich der get Methode nach dem jeweiligen Object suchen.
Der Unterschied ist das du durch die Änderung die Stationen Schleife von worsed Case laufzeit n auf worsed case Laufzeit log(n) bekommst.

(zum Query kann ich nix sagen aber da ist vielleicht auch noch was drin)
 
Zuletzt bearbeitet:
Hi,

ich habe die DB indexiert (B-Baum). Der Zugriff erfolgt bereits in O(log n)

Danke nochmal für die Hinweise
 
Du hast mich nicht verstanden ich hab den Java code gemeint.
Und ein schlechtes query kostet Performance da kannst indexieren was du willst. (Indexieren kostet mit unter auch performance)


Mir gehts um folgenden Code. Es so zu machen kostet Performance und sollte so nicht gemacht werden wenn es viele Stationen gibt.
Davon abgesehen verwend das nächste mal for(Station s : stationen)
Code:
for (int i = 0; i <= stationen.size() - 1; i++) {
 
Station s = stationen.get(i);
 
if (ent == s.getId()) {
s.getEntleihungen()[rI] = fahrt;
if (rue == ent) {
s.getRueckgaben()[rI] = fahrt;
}
 
} else if (rue == s.getId()) {
s.getRueckgaben()[eI] = fahrt;
 
}
}
 
Wie schon richtigerweise erwähnt wurde liegt das Performance-Problem in der Verschachtelung der Abfragen. Versuche alle Daten die du benötigst mit einem Statment rauszukriegen, dafür am besten ein Tool verwenden um die Abfrage direkt auf der DB zu testen. Du musst über die gemeinsamen Attribute (z. B. rueckgabestation und index).
 
Wenn der Code zu langsam ist, sollte man messen, welche Teile dafür hauptsächlich verantwortlich sind. Ansonsten optimiert man u.U. nur unnötig rum - gerade wenn man wenig Erfahrung hat. Profiler gibt es für Java sehr viele und sehr gute.


Die for-each-Schleife ist übrigens mitunter langsamer, als die herkömmliche for-Schleife. Für Random-Access-Listen (wie ArrayList) ist dies das schnellste Idiom:

Code:
for (int i=0, size=list.size(); i < size; i++)
    Value value = list.get(i);

Aber das spielt in 99% aller Fälle keine Rolle und for-each ist leserlicher.
 
soares schrieb:
Aber das spielt in 99% aller Fälle keine Rolle und for-each ist leserlicher.
Gut, dass du das noch angehangen hast. Ich möchte das nochmal bekräftigen, denn das ist (vor allem angesichts des hier stattfindenden, vergleichsweise lahmen IO) zu vernachlässigen. Dazu ist die for-each fail-fast und man merkt sofort, wenn concurrent modification stattfindet. Mit der normalen for kann sich hier die size der Liste während der Schleife ändern und dann gibt es schwer auffindbare Fehler durch nicht vollständig durchiterirte collections/arrays oder out of bounds exceptions.

P.S.: Eclipse bietet in den save actions sogar eine automatische Umwandlung an. Würde ich durchaus aktivieren.

Edit: habe gerade übersehen, dass du die size vorher rausziehst. Das ist natürlich besser. Ich bevorzuge trotzdem den Iterator. ;)
 
Irgendwie geht hier der Hauptgrund meines Posts unter, dass mit der for each war eigentlich nur ein neben hint.

Die for schleifen gehört gar nicht in die while(rs.next()) loop.
Hier sollte der Zugriff auf deine Hashmap laufen, welche intern mit log(n) beim Key arbeitet.
 
Zurück
Oben