Пусть есть такой код:
var S: String; List: TStringList; begin ... while {SomeCondition} do begin // Формируем строку: S := ''; while {SomeOtherCondition} do S := S + {...}; List.Add(S); end; ... end;В этом коде мы для каждого чего-то формируем строку и сохраняем её в список.
Этот код работает нормально. Но если мы изменим код так:
var S, D: String; List: TStringList; begin ... while {SomeCondition} do begin // Формируем строку: S := ''; while {SomeOtherCondition} do S := S + {...}; D := S; UniqueString(D); List.Add(D); end; ... end;То окажется, что новый код экономнее по расходу памяти на 30-50%.
Как это может быть? Объяснить видимое поведение.
Ответ на задачку будет выложен как обычно: примерно через месяц.
Ответ.
При конкатации и, как следствие, расширении (grow) выделенного под S блока памяти, программа будет его расширять с "запасом", т.е. как раз на те 30%-50% (зависит от деталей реализации; в теории "золотым коеффициентом" будет корень из двух).
ОтветитьУдалитьПри выполнении UniqueString копирование строки в новую область памяти "урежет" хвост, из-за чего и получим результирующую экономию в те самые 30%-50%.
Следует также учесть, что тут выполняется дополнительное выделение памяти под D, что способствует ее фрагментации, а также пиковому ее потреблению. А в случае очень длинной строки и малого количества итераций верхнего цикла, и вовсе ставит под сомнение такую опимизацию.
К словам предыдущего оратора можно добавить только рекомендацию использовать честный StringBuider, если уж хочется оптимизации.
ОтветитьУдалитьЗнать бы версию Дельфи еще для ответа.
ОтветитьУдалитьМенеджер памяти сейчас до семерки включительно сильно разные. Сейчас FastMM, раньше другой.
Да и вообще, как замерялся расход.
Я задачки люблю ) Но тут, я думаю, очень все зависит от деталей. Опять же, на FastMM сильно влияют размеры блоков. Я в свое время его неплохо, как я думаю, изучил.
Нужны детали. В абстрактном вакууме ответа не вижу.
Это задачка на телепатию, а не на точный ответ.
ОтветитьУдалитьP.S. Это не оптимизация, а просто человек заметил различное поведение, казалось бы, идентичного кода.
"..Чтобы получить уникальную ссылку для строки, состоящей из некоторой последовательности символов, можно воспользоваться функцией UniqueString. Это позволяет ускорить вычисления со строками, так как при этом можно будет сравнивать строки, просто сравнивая указатели на них. У таких строк refCnt всегда равен 1.." (с) http://www.rsdn.ru/article/Delphi/dynarrays.xml
ОтветитьУдалить>>Следует также учесть, что тут выполняется дополнительное выделение памяти под D, что способствует ее фрагментации
ОтветитьУдалитьА ещё дополнительное освобождение памяти при S := '' (FreeMem). В первом случае при List.Add(S) происходило увеличение ReferenceCounter'а у S, и при S := '' (_LStrClr) не было вызова FreeMem, лишь снова уменьшаеся ReferenceCounter.
Такие задачки просто не рождаются, видимо сильно критично была эта память.
ОтветитьУдалитьТот же легендарный 8 гигабайтный лог )
[code]procedure TForm1.Button1Click(Sender: TObject);
ОтветитьУдалитьvar
S, D: String;
List: TStringList;
i:integer;
begin
list:=tstringlist.Create;
while true do
begin
S := '';
for i:=0 to 100 do
S := S + inttostr(random(26787));
D := S;
UniqueString(s);
List.Add(s);
end;
end;[/code]
а как определить, экономнее ли код, ведь память и так забивается за счет заполнения списка?
гм... Очень любопытно, спасибо большое!
ОтветитьУдалитьА если у меня подобное но не `List.Add(S);` а `MyDinamicArrayOfStrings[i]:=S;` что-то изменится? Нужно будет UniqueString?
И да. Как бы самому померить стал ли расход памяти экономнее и на сколько %?
ОтветитьУдалитьЛюбопытно что будет если обернуть в функцию, чтоб без переменной D и как-то более понятно. И у меня цикл-мутант, не придумал как вынести:
SetLength(MyDinamicArrayOfStrings, maxN);
n:=0;
i:=1;
buf := '';
While (i <= maxI) And (n < maxN) Do
Begin
If (...) Then
Begin
MyDinamicArrayOfStrings[n] := buf;
buf := '';
Inc(n);
End
Else
buf := buf + ...;
Inc(i);
End;
If (n < maxN) Then
MyDinamicArrayOfStrings[n] := buf;