Autor |
Beitrag |
bbfan
Beiträge: 164
|
Verfasst: Sa 09.04.05 11:35
Hallo!
Ich habe eine eigene Class gebaut. Ganz simple...
Nun erzeuge ich über eine Schleife Objects von dieser Class und muss feststellen, dass mein Speicher sehr schnell vollläuft.
Ich habe zum Test dafür gesorgt, dass das erzeugte Object nicht mit Daten gefüllt wird. Der Speicher läuft trotzdem sehr schnell zu. Warum?
Gib es nicht Speicher fressende Objects?
Die erzeugten Objekte werden in einem dynamischen Array of CUFile abgelegt.
So fängt meine Class an:
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: 26: 27: 28: 29: 30: 31: 32:
| unit CUFile;
interface
uses Classes,SysUtils; type TFile = Class private sFilename : String; sSize : Int64; sPath : String; sPathFilename : String; sTime : Integer; sAttr : Integer; Public Constructor create(); Published property Filename : String read sFilename write sFilename; property Size : Int64 read sSize write sSize; property Path : String read sPath write sPath; property Time : Integer read sTime write sTime; property Attr : Integer read sAttr write sAttr; property PathFilename : String read sPathFilename write sPathFilename; procedure importFromSearchRec(src:TSearchRec; FilePath:String); procedure importFromTFile(src:TFile); function formatedTime():String; end;
implementation ..... |
Moderiert von raziel: Code- durch Delphi-Tags ersetzt.
|
|
Leto
Beiträge: 98
XP
D7 Enterprise
|
Verfasst: Sa 09.04.05 11:43
Wenn ich dich Frage nun richtig verstehe würde ich sagen:
Die Felder einer Klasse (ob nun intialisiert oder nicht), bestimmen den Maßanteil für die Größe einer entsprechenden Instanz (Pointer- und virtuelle Pointer-Tabellen und ähnliches mal ausgelassen). Bedeutet also, hast du eine Klasse mit 2 Integer wird (in gängigen Archittekturen) 2*4 = 8 Bytes pro Objekt belegt.
Ich hebe aber das Gefühl, dass ich deine Frage dennoch falsch verstanden habe...
_________________ Carpe Noctem
|
|
bbfan
Beiträge: 164
|
Verfasst: Sa 09.04.05 12:14
An für sich nicht... geht schon in die richtige Richtung.
Mich wundert es nur, dass der Speicher dermassen schnell zu geht, das ich dabei zuschauen kann.
Vielleicht liegt es am dynamischen array.... vielleicht sollte ich alles in ein TList packen.
Vom Prinzip her habe ich eien rekursive schleife, die die Dateien aus Verzeichnissen ausliest. Die Daten je Datei werden (s. Code oben) einem CUFile Object abgelegt.
Und die einzelnen CUFile Objecte gehen dann in ein dym. Array rein.
Frage: Wie bekomme ich das speicherschonender hin? Es kommen schon mehrere 1000 Dateien zusammen...
|
|
bbfan
Beiträge: 164
|
Verfasst: Sa 09.04.05 14:52
ich habe eine Lösung gefunden: Mit TList arbeiten und Records.
Allerdings warne ich davor den TList.sort Befehl zu nutzen. Der sortiert nicht richtig.
Benutzt eurer eigene QuickSort Procedure und alles wird gut!
|
|
Leto
Beiträge: 98
XP
D7 Enterprise
|
Verfasst: Sa 09.04.05 22:20
Also willst du nun statt Objekten (Referenzen) die Zeiger von Record-Variablen dort ablegen? Wenn die Methoden deiner Objekte nicht unbedingt sein müssen, sparst du damit ja etwas (ich galub der eine oder andere wird es als "unsauberer" empfinden, da nicht OOP).
Ich hab noch nie Probleme mit dem Sort einer TList gehabt (ist doch auch ein Quicksort). Du musst nur eine entsprechende Compare-Funktion übergeben, dann sollte es klappen.
_________________ Carpe Noctem
|
|
Keldorn
Beiträge: 2266
Erhaltene Danke: 4
Vista
D6 Prof, D 2005 Pro, D2007 Pro, DelphiXE2 Pro
|
Verfasst: Sa 09.04.05 22:50
bbfan hat folgendes geschrieben: |
Mich wundert es nur, dass der Speicher dermassen schnell zu geht, das ich dabei zuschauen kann.
Vielleicht liegt es am dynamischen array.... vielleicht sollte ich alles in ein TList packen.
|
Tlist hat auch ein dynam. array als grundlage. So wie du dein Problem beschreibst, vermute ich eher, das du innerhalb deiner Schleife jedesmal setlength(array,größe_des_array+1) aufrufst und dein array jedesmal um eins erweitest ?
Mfg Frank
_________________ Lükes Grundlage der Programmierung: Es wird nicht funktionieren.
(Murphy)
|
|
Leto
Beiträge: 98
XP
D7 Enterprise
|
Verfasst: Sa 09.04.05 23:10
Wirkt sich dieses 1-Increment-Capacity-Management nicht eher nachteilig auf den Prozessezor denn auf den Speicher aus?
_________________ Carpe Noctem
|
|
Keldorn
Beiträge: 2266
Erhaltene Danke: 4
Vista
D6 Prof, D 2005 Pro, D2007 Pro, DelphiXE2 Pro
|
Verfasst: So 10.04.05 08:33
Das ist auch schon mehrfach hier diskutiert wurden. Eine gute Erklärung findet sich auch hier
Mfg Frank
_________________ Lükes Grundlage der Programmierung: Es wird nicht funktionieren.
(Murphy)
|
|
Leto
Beiträge: 98
XP
D7 Enterprise
|
Verfasst: So 10.04.05 12:20
Zitat: |
...oder man arbeitet mit einem großen Delta-Wert und einer RealLength Variable, die die effektive Länge des Strings / Arrays enthält.
|
Ich darf annehmen, damit sind Capacity und Count in Tlist und TObjectList gemeint? Sprich also die Realisierung eines Standard-Vectors für Pointer?
_________________ Carpe Noctem
|
|
Keldorn
Beiträge: 2266
Erhaltene Danke: 4
Vista
D6 Prof, D 2005 Pro, D2007 Pro, DelphiXE2 Pro
|
Verfasst: So 10.04.05 13:21
nein,
eher so (Pseudocode):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| Var arr:array of Tirgendwas; Laenge:integer; begin laenge:=-1; while irgendetwas do begin if laenge>=length(arr) then setlenght(arr,length(arr)+100); inc(laenge); arr[laenge]:=blablabla; end; setlenght(arr,laenge); end; |
_________________ Lükes Grundlage der Programmierung: Es wird nicht funktionieren.
(Murphy)
|
|
Tilo
Beiträge: 1098
Erhaltene Danke: 13
Win7 geg. WInXP oder sogar Win98
Rad2007
|
Verfasst: Mo 11.04.05 14:00
Wenn das dynamische Array nur zum Auflisten der Elemente führt, rate ich von dynamischen Arrays ab.
Nihm lieber Pointer.
z.b:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| PTrec:^Trec; Trec = reorcd elementA:irgendeeinTyp; elementB:irgendeeinTyp; elementC:irgendeeinTyp; .... nextRec:PTrec; end; |
nextrec beinhaltet dann die Speicheradresse des nächsten Elements.
Vorteil gegenüber Dynarray:
- schneller
- schmiert nicht bei sehr vielen Elmenten ab
|
|
Keldorn
Beiträge: 2266
Erhaltene Danke: 4
Vista
D6 Prof, D 2005 Pro, D2007 Pro, DelphiXE2 Pro
|
Verfasst: Mo 11.04.05 14:13
Tilo hat folgendes geschrieben: | Vorteil gegenüber Dynarray:
- schneller |
aber nur, wenn du dich von einem Element zum Nächsten hangelst. Was ist, wenn du 1000 Einträge hast, und den 1. 300. und 700. eintrag auslesen möchtest? Dann wird dein Geschwindigkeitsvorteil schnell dahin sein.
Tilo hat folgendes geschrieben: | - schmiert nicht bei sehr vielen Elmenten ab |
Das mußt du genauer erläutern. wieso soll ein dyn. array bei sehr vielen Elementen "abschmieren"?
Mfg Frank
_________________ Lükes Grundlage der Programmierung: Es wird nicht funktionieren.
(Murphy)
|
|
Tilo
Beiträge: 1098
Erhaltene Danke: 13
Win7 geg. WInXP oder sogar Win98
Rad2007
|
Verfasst: Mo 11.04.05 22:05
@Keldorn
ich hatt bei meinen Primgenerator das Problem, dass wenn ich am Anfang das Array auf ein hohes Index eingestellt hab ( 200000 oder noch 1, 2 Nullen) das das Programm abgebrochen wurde. Bei einem statischen Array dagegen kann ich ohne Probleme wesentlich höhere Indexe nehmen, Nachteil: Imenser Speicherbedarf
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Di 12.04.05 15:49
Kleine Anmerkung: Mit TFile.Create sollte gleich viel Speicher alloziert werden wie die Grösse des äquivalenten Records(+4). (Eine simple Klasse ist ja von der Idee her nichts anderes als ein record mit einer Ansammlung von Funktionen)
Übrigens: Wenn es dir um jedes Byte geht, mach ein dynamisches "Array of TFile", wobei TFile ein packed record ist. (PS: Es gibt auch packed classes. (Scheint aber nicht zu funktionieren))
//Edit: Änderungen im Text
Zuletzt bearbeitet von delfiphan am Di 12.04.05 23:24, insgesamt 1-mal bearbeitet
|
|
Motzi
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Di 12.04.05 17:27
delfiphan hat folgendes geschrieben: | PS: Es gibt auch packed classes |
Das musst du mal bitte näher erläutern..!
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Di 12.04.05 18:56
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| TTest = class B: Byte; I: Integer; end;
TTest = packed class B: Byte; I: Integer; end; |
|
|
Motzi
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Di 12.04.05 23:02
Hm.. kompilieren tut es (war mir neu, dass das geht), aber die Größe ändert sich trotzdem nicht.. Erstens hast du vergessen, dass alle Klassen intern automatisch von TObject abgeleitet werden und von daher automatisch schon mind. 4 Bytes groß sind, und zweitens sind bei mir beide Klassen 12 Bytes groß. Aber egal - packed classes würden IMHO sowieso keinen Sinn machen..!
Gruß, Motzi
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Di 12.04.05 23:10
Stimmt und stimmt. Sind beides mal 12 Bytes. Naja... Das packed ist wohl für Rückwärtskompatibilität oder so.
Gruss
|
|
Leto
Beiträge: 98
XP
D7 Enterprise
|
Verfasst: Di 12.04.05 23:22
Ich kannte packed immer nur im Zusamenhang mit Arrays (wo sie dort den gleichen Sinn erfüllen sollen). Ist dies auch redundant geworden?
_________________ Carpe Noctem
|
|
Motzi
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Mi 13.04.05 00:06
Im Zusammenhang mit Arrays weiß ich es nicht genau, da hab ich es auch noch nie verwendet. Wo es aber durchaus noch Verwendung findet und auch nicht redundant ist, ist bei Records. zB:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| type TMyRecord = record a: Byte; b: Word; end;
TMyPackedRecord = packed record a: Byte; b: Word; end; |
Der Grund, warum der erste Record um 1 Byte größer ist liegt darin, dass der Compiler versucht die Felder an Word oder Double-Word Grenzen auszurichten, da dann schneller darauf zugegriffen werden kann. Die Ausrichtung ist abhängig von der Größe des Feldes. Ein Word (2 Bytes) wird an einer Word-Grenze ausgerichtet, sollte also an einer Stelle im Speicher stehen, die durch 2 teilbar ist. Ein DWord (4 Bytes) hingegen an einer Double-Word-Grenze, also an einer Stelle, die durch 4 teilbar ist. Bei einzelnen Bytes ist es logischerweise egal..
Liegt ein DWord nun an einer Stelle die zwar durch 2, aber nicht durch 4 teilbar ist, so kann der Wert nicht auf einmal gelesen werden, sondern muss durch 2 Speicherzugriffe gelesen werden - nämlich als 2 Words (da ja die Adresse durch 2 teilbar ist). Im schlimmsten Fall muss zum Lesen eines DWords also 4 mal auf den Speicher zugegriffen werden.
Um dies zu verhindern, versucht der Compiler die Records eben zu optimieren, indem er Filler-Bytes einfügt um die Felder an den entsprechenden Speichergrenzen auszurichten. Durch geschicktes Anordnen kann man da aber auch selbst optimieren. zB:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| type TMyRecord = record a: Byte; b: Word; c: Byte; end;
TMyPackedRecord = packed record a: Byte; c: Byte; b: Word; end; |
Die Records enthalten beide dieselben Felder - nur in unterschiedlichen Reihenfolgen.
Gruß, Motzi
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|