Многие классы, у которых есть список элементов, имеют свойство "Capacity" или его аналог. Можно всю жизнь программировать и не догадываться о его существовании. Но оно есть. Так зачем оно нужно и как его использовать?
Каждое добавление нового элемента в список, приведет к перераспределению памяти, которая выделена под массив элементов этого списка. Когда элементов не много - это не важно, но когда их много, постоянное перераспределение памяти может замедлить вашу программу.
Что бы избежать перераспределения памяти при добавлении нового элемента в список и необходимо свойство Capacity. Оно позволяет заранее распределить память под массив элементов и тем самым повысить производительность программы. Проведем небольшой тест:
Как вы видите, предварительное распределение памяти под массив элементов списка дает существенное увеличение производительности.
Если количество элементов точно не известно, то можно распределить память под какое-то приблизительное количество элементов. Если список будет заполнен до предела, то свойство Capacity при добавлении нового элемента увеличивается автоматически. Подобное увеличение свойства Capacity можно сделать и "руками" вызвав метод Expand, который, если список заполнен, сам рассчитает новое значение Capacity. Для использованного в моем примере TList<integer> автоматическое расширение Capacity увеличивает его значение на 50% уже начиная с Capacity=66. Поэтому, при миллионах элементов можно распределить много лишней памяти или даже поймать "Out of memory". Значит свойство Capacity лучше контролировать самому и увеличивать его с определенным шагом. Например:
Таким образом, знать о свойстве Capacity и уметь с ним работать может быть весьма полезно.
Каждое добавление нового элемента в список, приведет к перераспределению памяти, которая выделена под массив элементов этого списка. Когда элементов не много - это не важно, но когда их много, постоянное перераспределение памяти может замедлить вашу программу.
Что бы избежать перераспределения памяти при добавлении нового элемента в список и необходимо свойство Capacity. Оно позволяет заранее распределить память под массив элементов и тем самым повысить производительность программы. Проведем небольшой тест:
uses
System.Classes,
System.SysUtils,
System.Generics.Collections,
System.Diagnostics;
const
ciCount = 1000000;
procedure CapacityTest;
var
l: TList<integer>;
sw: TStopwatch;
i, j: integer;
begin
sw := TStopwatch.StartNew;
for j := 1 to 10 do
begin
l := TList<integer>.Create;
try
for i := 1 to ciCount do
l.Add(i);
finally
l.Free;
end;
end;
sw.Stop;
WriteLn(FormatFloat('0.0', sw.ElapsedMilliseconds / 10));
sw := TStopwatch.StartNew;
for j := 1 to 10 do
begin
l := TList<integer>.Create;
l.Capacity := ciCount;
try
for i := 1 to ciCount do
l.Add(i);
finally
l.Free;
end;
end;
sw.Stop;
WriteLn(FormatFloat('0.0', sw.ElapsedMilliseconds / 10));
end;Результаты:| Количество элементов | Разрядность программы, бит | Время без Capacity, мс | Время с Capacity, мс | Прирост скорости, % |
| 1 000 000 | 32 | 5,2 | 3,0 | 42,31 |
| 1 000 000 | 64 | 5,1 | 3,2 | 37,26 |
| 10 000 000 | 32 | 59,2 | 31,5 | 46,79 |
| 10 000 000 | 64 | 61,4 | 34,9 | 43,16 |
| 10 0000 000 | 32 | 528,8 | 323,1 | 38,90 |
| 10 0000 000 | 64 | 525,6 | 350,8 | 33,26 |
Если количество элементов точно не известно, то можно распределить память под какое-то приблизительное количество элементов. Если список будет заполнен до предела, то свойство Capacity при добавлении нового элемента увеличивается автоматически. Подобное увеличение свойства Capacity можно сделать и "руками" вызвав метод Expand, который, если список заполнен, сам рассчитает новое значение Capacity. Для использованного в моем примере TList<integer> автоматическое расширение Capacity увеличивает его значение на 50% уже начиная с Capacity=66. Поэтому, при миллионах элементов можно распределить много лишней памяти или даже поймать "Out of memory". Значит свойство Capacity лучше контролировать самому и увеличивать его с определенным шагом. Например:
Если вы добавили все элементы в список, а их количество оказалось меньше, чем Capacity, то не используемую память необходимо освободить с помощью присвоения свойству Capacity значения свойства Count:l.Capacity := l.Capacity + 1000000;
или можно вызвать реализующий это присвоение inline-метод TrimExcess.l.Capacity := l.Count;
Таким образом, знать о свойстве Capacity и уметь с ним работать может быть весьма полезно.
Комментариев нет:
Отправить комментарий