Entwickler-Ecke

Sonstiges (Delphi) - Wieso wird das immer langsamer?


Gausi - Di 08.02.05 11:39
Titel: Wieso wird das immer langsamer?
Ich schreibe grad ein kleines Spielchen, bei dem von oben Sachen runterfallen, die man im Fallen anklicken muss.

Die "Sachen" sind im Wesentlichen TImages, die aber weitere Eigenschaften wie z.B. Geschwindigkeit haben. Der Konstruktor sieht so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
constructor TBierflasche.create;
var  resStream: Tresourcestream;
begin
    inherited;
    Speed:= 3;  // Geschwindigkeit, mit der die Items fallen
    Bild:=TImage.Create(Form1);
    Bild.Parent:=Form1.MainPanel;
    case Random(100of
        // verschiedene Fälle, die alle prinzipiell gelich aussehen
        [...]
        else begin
            // Grafiken sind in Ressourcen eingebunden
            resStream:= TResourceStream.CreateFromID(HInstance, 200, RT_RCDATA);
            PWert:=100;  // Punkte fürs anklicken
            LWert:=0// "Leben"
        end;
    end;
    Bild.Picture.Bitmap.LoadFromStream(ResStream);
    resStream.Free;
    Bild.Left:=random(Form1.MainPanel.Width-120); // horizontale Position zufällig
    Bild.OnMouseDown:=Form1.IMAGEMouseDown;
    //[...] ein paar weitere Eigenschaften werden noch gesetzt
end;


Das Erzeugen hab ich zu Testzwecken ins OnIdle-Event gepackt

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
    if (random(100) < 5then
    begin
        //Item erzeugen und in eine Liste packen
        Bierflasche:=TBierflasche.create;
        FlaschenListe.Add(Bierflasche);
    end;
    // Alle Items überprüfen
    for i:=FlaschenListe.Count-1 downto 0 do
    begin
        Bierflasche:=FlaschenListe[i] as TBierflasche;
        if Bierflasche.Bild.Top<200 then
        // weiter runterfallen lassen
            Bierflasche.Bild.Top:=Bierflasche.Bild.Top + Bierflasche.Speed
        else
        begin
            if Bierflasche.PWert>=0 then
            begin
                dec(Leben);
                [...]
            end;
            // Item entfernen
            Bierflasche.Bild.Picture.Assign(Nil);
            FlaschenListe.Delete(i);
        end;
    end;

Dieses Stück Code wird also ständig wiederholt. In 5% (random(100)<5) der Fälle wird ein neues Item erzeugt, und wenn es unten angekommen ist, wird es wieder entfernt.

Problem ist: Das wird immer langsamer. Am Anfang flutschen die Bilder richtig flott von oben nach unten, aber nach einiger Zeit....ne.
Die Anzahl der Items bleibt konstant ungefähr bei 10, die Speicherauslastung bleibt laut Taskmanager bei etwa 4.5MB.

Jemand ne Idee, warum die Geschwindigkeit im während der Programmlaufzeit so extrem abnimmt?


AXMD - Di 08.02.05 11:44

Sieht stark danach aus, als würdest du irgendetwas nicht freigeben... tippe stark auf


Delphi-Quelltext
1:
Bierflasche:=TBierflasche.create;                    
(zweites Codeschnipsel)

AXMD


Gausi - Di 08.02.05 12:06

Aargghhhh... Hatte zwar den Destructor geschrieben, aber der wurde nicht richtig aufgerufen, weil ich override vergessen hatte.. :autsch:
Jetzt scheint alles in Ordnung zu sein, danke!


Tino - Di 08.02.05 12:10

...und im Destruktor von TBierflasche nicht vergessen die Variable Bild freizugeben.


Gausi - Di 08.02.05 12:13

Das hatte ich schon:

Delphi-Quelltext
1:
2:
3:
4:
5:
destructor TBierflasche.destroy;
begin
    Bild.Free;
    inherited destroy;
end;
Trotzdem danke!


MitschL - Di 08.02.05 13:18

Ähm,

irgendwie bleibt mir bei den Auswirkungen der Sinn verborgen. Wenn ich etwas nicht freigebe, dann steigt mein Speicherbedarf im Laufe der Programmarbeit (so eigene Erfahrungen), was dazu führen kann, daß das Programm u.U. langsamer läuft.
Nun bleibt aber das Programm hier wohl gleich groß und trotzdem wird es langsamer, weil wegen nicht destruierten Bierflaschen? :gruebel:

Kann mir das mal einer näher bringen?

gegrüßt!


Udontknow - Di 08.02.05 13:43

Poste doch bitte mal die Klassendeklaration.

Cu,
Udontknow


Gausi - Di 08.02.05 14:18

Da ist eigentlich nichts besonderes bei:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
 TBierFlasche = class(TObject)
      Bild:Timage;
      Speed:integer;
      PWert:integer;
      LWert:integer;
      constructor create;
      destructor destroy; override;
  end;

Warum das jetzt besser klappt, weiss ich auch nicht genau. Fakt ist, dass ohne das 'override' der Destructor bei FlaschenListe.delete(i) nicht aufgerufen wird. Außerdem reagieren dann einige 'Flaschen' nicht auf OnMouseDown, oder erst dann, wenn man eine andere vorher wegklickt.


Udontknow - Di 08.02.05 14:47

Es hätte ja sein können, das "Bild" eine globale Variable wäre... :wink:

So sehe ich erst mal auch nichts. Aber was Optimierung angeht: Wieso ziehst du immer wieder für jede Instanz das Bitmap als Resource? Wäre es nicht effektiver, einmal bei Start eine Bitmapliste zu erstellen und einfach dann das Bitmap über einen Index zu referenzieren und auf die Komponente zu zeichnen, anstelle für jede TBierflasche-Instanz ein Bitmap im Speicher zu halten?

Cu,
Udontknow


Udontknow - Mi 09.02.05 16:54

Woran hat´s denn nun gelegen?

Cu,
Udontknow


Gausi - Mi 09.02.05 17:10

Keine Ahnung. Behoben wurde das Problem auf jeden Fall durch 'Override' des Destructors. Zu deinem Vorschlag: Das tut sich nicht viel. Die Bitmaps sind ziemlich klein (90x90), ich habe ca. 7 verschiedene Grafiken, und so gut wie nie mehr als 15 Flaschen gleichzeitig erzeugt. Da ist die Speicherersparnis nicht so riesig, als dass sich der Aufwand des Umcodens für dieses kleine Spielchen lohnen würde.