страничка tripsin'а


Главная | Статьи | Заметки | Файлы| Ссылки | я

Несколько советов по оптимизации кода при использовании строк.

Автоматическое преобразование типов.

При вызове строковых функций надо внимательно относиться к неявному преобразованию типов. Простой пример. Функция Pos, как ты знаешь, находит вхождение одной строки в другую и возвращает позицию первого символа искомой строки. Но что если надо найти положение не строки, а всего одного символа: Pos(TargetChar, SomeString). Дык вот в этом случае единичный символ сначала автоматически преобразуется в длинную строку, а потом уже подставляется в функцию. Значит из кучи будет выделена дополнительная память и будет написан дополнительный код инициализации и финализации скрытой строки (да еще с блоком обработки ошибок). Если эта операция будет производиться многократно (к цикле например), то потеря производительности будет довольно существенной. И все тут же примутся грешить на кривой компилятор у Дельфей ;)
Чтобы избежать этого косяка можно написать свою функцию поиска символа в строке, где не будет лишних преобразований.

function PosOfChar(Ch: AnsiChar; const S: String): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 1 to Length(S) do
  if S[i] = Ch then
  begin
    Result := i;
    Exit;
  end;
end;

Эта функция будет работать в 5 РАЗ быстрее, чем Pos с единичными символами. Кстати операция конкатенации (т.е. соединения) строк "+" работает тоже работает только со строками и единичные символы тоже будут приведены к строкам.

Ключевое слово const.

Посмотри еще раз на код который приведен выше. Строковый параметр S передается в функцию, как const. Зачем я так сделал? Потому что S в коде не изменяется, а я хочу повысить производительность. Объясняю.
Если ты читал заметку о длинной строке AnsiString, то видел, что это довольно сложная структура данных, и у нее кроме всего прочего имеется счетчик ссылок (RefCount). Если ты передашь строку в подпрограмму без слова const, то компилятор решит, что эта строка может в подпрограмме измениться. Он создаст на строку дополнительную ссылку (увеличит счетчик на 1) в начале, а в конце эту ссылку удалит (уменьшит счетчик ссылок на 1). Кроме того, чтобы гарантировать, что этот счетчик действительно уменьшится, в код скрытно от тебя вставляется блок обработки ошибок try..finally. Как ты понимаешь, производительность это не улучшает.
Чтобы всего этого не было, ты должен явно сказать компилятору, что эта переменная в коде функции изменяться не будет, т.е. передать параметр как const. Кстати, если ты все-таки попытаешься эту строку изменить, то вылетит ошибка (константы на то и константы, чтобы не менялись). Вот такая маленькая хитрость.

Переменная Result.

Как ты знаешь, в Delphi с помощью переменной Result можно оперировать значением, которое возвращает функция. Фактически это не локальная переменная, а скрытый параметр var. Если функция возвращает длинную строку (или динамический массив), то переменная Result должна быть инициализирована. Но Delphi почему-то делает это не всегда. Поэтому ты сам должен всегда инициализировать Result пустой строкой.

Орехов Роман aka tripsin


Опубликована на VR-online в 2006 г.

Hosted by uCoz