@grzegorz_so - w Delphi7 (w nowszych nie wiem) i bibliotece FPC taki przykład znajdziesz w klasie TStringList
; Pamięć dla listy alokowana jest większymi blokami, jesli Count
zrówna się z Capacity
- wtedy do akcji wkracza metoda Grow
(piszę o FPC), która oblicza nowy rozmiar i wywołuje SetCapacity
, aby realokować pamęć listy:
procedure TStringList.Grow();
var
NC: Integer;
begin
NC := FCapacity;
If NC >= 256 then
NC := NC + (NC Div 4)
else
if NC = 0 then
NC := 4
else
NC := NC * 4;
SetCapacity(NC);
end;
Tyle że całość nie opiera się na zwykłej tablicy - wzamian trzymany jest wskaźnik na poczatek bloku pamięci jako rezultat funkcji GetMem; Potem bloki pamięci są kopiowane za pomocą Move, pusta część jest zerowana, a cały stary blok pamięci jest zwalniany;
Bloki pamięci realokuje się rzadko, bo przenoszenie jednego obszaru pamięci do drugiego trochę trwa; Tym bardziej, jeśli blok jest duży; Każdorazowa relokacja pamięci znacznie spowalniała by program, dlatego całość bazuje na alokowaniu pamięci z dynamicznie obliczanym zapasem i sprawdzanie "pełności" bloku na podstawie pomocniczego pola;
Ale to jeśli chodzi o samo alokowanie pamięci z zapasem;
Nie wiem co lepsze w przypadku zwykłych tablic dynamicznych, ale powiększanie tablicy każdorazowo przy dodawaniu nowego elementu nie będzie sprzyjać przy dużych tablicach; Dla małych na pewno nie będzie to miało większego znaczenia, żeby się tym przejmować;
PS: W przypadku zmiany rozmiaru dynamicznej tablicy za pomocą SetLength, blok pamięci macierzy może zostać w całości przeniesiony w inne miejsce przez menedżer pamięci, albo i też nie; Widać jeśli obecny blok po powiększeniu zmieści się (jest na tyle wolnej pamięci obok), to zostanie on tylko powiększony; A jeżeli nie zmieści się - przeniesiony w całości w inne miejsce;
Duże tablice często mogą być przenoszone w całości, więc potrzeba będzie więcej czasu na ich powiększenie; Zresztą można sobie sprawdzić w zwykłej konsolówce:
var
arrTest: array of UInt8;
begin
SetLength(arrTest, 10);
WriteLn('Address: ', PtrInt(@arrTest[0]));
SetLength(arrTest, 20);
WriteLn('Address: ', PtrInt(@arrTest[0]));
end.
Wynikiem u mnie będzie:
Address: 347112
Address: 347112
Jak widać adres pierwszej komórki nie zmienil się - widać menedżer mógł tylko poszerzyć blok pamięci; Natomiast przy większej macierzy adres może się zmienić - test:
var
arrTest: array of UInt8;
begin
SetLength(arrTest, 5000000);
WriteLn('Address: ', PtrInt(@arrTest[0]));
SetLength(arrTest, 7000000);
WriteLn('Address: ', PtrInt(@arrTest[0]));
end.
Czego efektem będzie inny adres pierwszej komórki macierzy:
Address: 25362520
Address: 30474328
Adres zmienił się, choć nowy rozmiar tablicy to tylko 7MB.