Einzelnen Beitrag anzeigen

Cyf

Registriert seit: 30. Mai 2008
407 Beiträge
 
Lazarus
 
#12

Re: Klasse oder Record auf sich selbst?

  Alt 18. Mär 2009, 01:45
Naja beim erzeugen eines Objektes pasiert schon ein wenig mehr, als beim reservieren von Speicher für einen neuen Record auf dem Heap, bei Objekten müssen ja immer auch Zeiger und Informationen auf die (ja, einmalig, statisch angelegten) RTTI angelegt werden zwecks Methodentabelle etc.
Wenn du im Debugger allein mal den Aufruf von TObject.Create verfolgst, dann kommt in etwa sowas:

Code:
TObject.Create:
00403808 84D2             test dl,dl
0040380A 7408             jz $00403814
0040380C 83C4F0           add esp,-$10
0040380F E830030000       call @ClassCreate
00403814 84D2             test dl,dl
00403816 740F            jz $00403827
00403818 E87F030000       call @AfterConstruction
0040381D 648F0500000000   pop dword ptr fs:[$00000000]
00403824 83C40C          add esp,$0c
00403827 C3               ret

@ClassCreate:
00403B44 52               push edx
00403B45 51               push ecx
00403B46 53               push ebx
00403B47 84D2             test dl,dl
00403B49 7C03             jl $00403b4e
00403B4B FF50F4           call dword ptr [eax-$0c] //<- NewInstance
00403B4E 31D2             xor edx,edx
00403B50 8D4C2410         lea ecx,[esp+$10]
00403B54 648B1A          mov ebx,fs:[edx]
00403B57 8919             mov [ecx],ebx
00403B59 896908           mov [ecx+$08],ebp
00403B5C C741046D3B4000   mov [ecx+$04],$00403b6d
00403B63 89410C          mov [ecx+$0c],eax
00403B66 64890A          mov fs:[edx],ecx
00403B69 5B              pop ebx
00403B6A 59               pop ecx
00403B6B 5A              pop edx
00403B6C C3               ret

TObject.NewInstance:
004037D0 53               push ebx
004037D1 8BD8             mov ebx,eax
004037D3 8BC3             mov eax,ebx
004037D5 E826000000       call TObject.InstanceSize
004037DA E8B1F4FFFF      call @GetMem
004037DF 8BD0             mov edx,eax
004037E1 8BC3             mov eax,ebx
004037E3 E85C000000       call TObject.InitInstance
004037E8 5B              pop ebx
004037E9 C3               ret
004037EA 8BC0             mov eax,eax

TObject.InstanceSize:
00403800 83C0D8           add eax,-$28
00403803 8B00             mov eax,[eax]
00403805 C3               ret
00403806 8BC0             mov eax,eax

@GetMem:
00402C90 85C0             test eax,eax
00402C92 7E13             jle $00402ca7
00402C94 FF151C474500     call dword ptr [$0045471c]
00402C9A 85C0             test eax,eax
00402C9C 7402             jz $00402ca0
00402C9E F3C3             rep ret
00402CA0 B001             mov al,$01
00402CA2 E939010000       jmp Error
00402CA7 31C0             xor eax,eax
00402CA9 F3C3             rep ret
00402CAB 90               nop

TObject.InitInstance:
00403844 53               push ebx
//[...]
00403899 C3               ret

@AfterConstruction:
00403B9C 55               push ebp
//[...]
00403BC6 83C408           add esp,$08
00403BC9 EB19             jmp $00403be4
00403BCB E930010000       jmp @HandleAnyException
00403BD0 B201             mov dl,$01
00403BD2 8B45FC          mov eax,[ebp-$04]
00403BD5 E812000000       call @BeforeDestruction
00403BDA E8DD040000       call @RaiseAgain
00403BDF E82C050000       call @DoneExcept
//[...]
00403BE9 C3               ret
Beim Anfordern von Heap-Speicher für einen Record via New(aPointer) wird das ganze hingegen (solange keine Strings o. ä. beteiligt sind, dann kommt noch ein Initialize hinzu) zu nur einem GetMem. Das mag bei kleinen Datenmengen nicht ins Gewicht Fallen, aber lass es mal große Datenmengen sein, dann macht das prozentual schon etwas aus, auch wenn das praktisch nur dann spürbar sein sollte, wenn diese ständig neu erzeugt werden müssen (z.B. irgendwelche Listen über Objekte die ihren Zustand ändern).

Folgender Testcode:

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  Timer: TQPCounter;
  i: Integer;
  p: PInteger;
begin
  Timer:= TQPCounter.Create; //eigene Klasse um die Zeit zu messen, unbedeutent
  for i := 0 to 9999999 do
  begin
    New(p);
    //oder: TObject.Create; über das memory Leak sei mal hinweggesehen, auch das Freigeben dauert beim Objekt länger
  end;
  Showmessage(Timer.ClockStr);
  Timer.Free;
end;
mit Zeiger: 240-330ms
mit Objekt: 670-690 ms

Wie weit das jetzt unter welchen Bedingungen spürbar ist, kann man sich streiten, das ist wahr, bloß der Code zum Verwalten des Baums muss sowieso irgendwo hin, ob jetzt in die Verwaltungsklasse, oder in die Nodes, ist dann egal, also nehm ich die schnellere Variante.
Natürlich gibt es bestimmt auch Situationen, wo die Klasse Sinn macht, etwa bei Kombinationen von verschiedenen Knotenarten, die irgendwie in Beziehung stehen und anders reagieren sollten.

[Edit]Zeiten mit Dispose bzw. mit Zuweisung des Objekts auf eine Variable und dann MyObj.Free:

leere Schleife: ca. 5,37 ms
Zeiger New und Dispose: ca. 150 ms (ja das ist weniger, liegt nicht an Auslastung im Hintergrund o. ä., habs mehrfach verglichen, es ist immer sehr viel weniger, vermute mal, weil nicht immer kleine Mengen Heap angefordert werden, sondern immer die selbe Stelle, die der Memory-Manager noch hält)
Objekt Create und Free: ca. 815 ms
Man kann einen Barbier definieren als einen, der alle diejenigen rasiert, und nur diejenigen, die sich nicht selbst rasieren.
Rasiert sich der Barbier?
  Mit Zitat antworten Zitat