Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   TDictionary<K,V> und Kapazität (https://www.delphipraxis.net/203611-tdictionary-k-v-und-kapazitaet.html)

ventiseis 4. Mär 2020 21:15

Delphi-Version: 10.2 Tokyo

TDictionary<K,V> und Kapazität
 
Hallo,

während einer längeren Debugging-Session ist mir etwas aufgefallen. Bekannt ist:
  • man kann ein TDictionary<K,V> mit einer bestimmten Kapazität über den Konstruktor erzeugen (siehe TDictionary.Create)
  • ein Dictionary hat immer einen gewissen Füllgrad
  • die Einträge des Dictionaries werden in ein Array aus Buckets einsortiert
  • ist der Füllgrad erreicht, dann muss das Bucket-Array vergrößert werden, ansonsten wird der Lookup im Dictionary ineffizient
In der Implementierung von TDictionary wurde das so umgesetzt:
  • die Kapazität ist immer die nächstgrößere Zweierpotenz, aber mindestens 4 Buckets
  • der Füllgrad wird berechnet: (Kapazität shr 1) + (Kapazität shr 2)

Das führt aber dazu, dass bei manchen Kapazitätswerten nicht mehr alle Einträge ins Dictionary passen und das Dictionary beim Hinzufügen von Elementen vergrößert werden muss, obwohl man eine ausreichende Kapazität angegeben hat:

Kapazität / Konstruktorverwendete KapazitätMax. Füllgrad 
443Kapazität im Konstruktor nicht ausreichend
203224Kapazität ausreichend
293224Kapazität wieder nicht ausreichend

Mein Fazit: falls man Reallokationen vermeiden will, sollte man die Kapazität des Dictionaries größer angeben als die Anzahl der Elemente, weil der Füllgrad bei der Kapazitätsberechnung nicht einbezogen wird.

Ist das auch schon mal jemand aufgefallen? Oder habe ich hier ein Verständnisproblem? :?

ventiseis 4. Mär 2020 21:17

AW: TDictionary<K,V> und Kapazität
 
Hier ein Testprogram zum Auslesen des Füllgrads für Delphi 10.2:

Delphi-Quellcode:
program Project7;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Generics.Collections, System.Rtti;

function GetGrowThreshold(AInstance: TObject): NativeUInt;
var
  LContext: TRttiContext;
  LType: TRttiType;
  LField: TRttiField;
begin
  LContext := TRttiContext.Create();
  LType := LContext.GetType(TDictionary<Integer, Integer>);
  LField := LType.GetField('FGrowThreshold');
  Result := LField.GetValue(AInstance).AsType<Integer>;
end;

var
  I: Integer;
  LDictionary: TDictionary<Integer, Integer>;

const

  C = 20;

begin
  LDictionary:=TDictionary<Integer, Integer>.Create(C);
  try

    for I := 1 to C do
    begin
      LDictionary.Add(i,i);
      WriteLn(IntToStr(i) + ': ' + GetGrowThreshold(LDictionary).ToString);
    end;
    ReadLn;
  finally
    FreeAndNil(LDictionary);
  end;
end.

Stevie 5. Mär 2020 09:49

AW: TDictionary<K,V> und Kapazität
 
Ja, das stimmt. Die Kapazität, die man angibt stimmt nicht mit der Anzahl der Elementen überein, die man ohne Grow hinzufügen kann.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:55 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz