2015-08-11 4 views
4

Мне нужно разбить строку на TStringList с подстроками фиксированной длины.Быстрый способ разделить строку на части фиксированной длины в Delphi

В настоящее время я использую:

procedure StrToStringList(ASource: string; AList: TStrings; AFixedLen: Integer); 
begin 
    Assert(Assigned(AList)); 
    while Length(ASource) > AFixedLen do 
    begin 
     AList.Add(LeftStr(ASource, AFixedLen)); 
     Delete(ASource, 1, AFixedLen); 
    end; 
    AList.Add(ASource); 
end; 

Это работает, но кажется медленным. Любая лучшая/более быстрая идея?

Отредактировано: Профилирование результатов:

прирост Скорость является весьма впечатляющим. Вот результаты моего (субъективного) профилирования.

Размер данных: 290KB, FixedLen: 100:

  • Оригинальный код: 58 мс
  • Хеффернэн: 1 мс
  • Deltics: 1 мс

Размер данных: 2805KB, FixedLen : 100:

  • Оригинальный код: 5803 мс
  • Heffernan: 5 мс
  • Deltics: 4 мс
+3

Очевидно, что вызов 'Delete' не нужен. –

+0

Это Руд-у, а не Руд-и. Держу пари, вы могли видеть эту разницу? –

ответ

5

Есть некоторые сразу очевидные оптимизаций возможных в вашем коде:

  1. Не модифицирует исходную строку, просто извлечь необходимые подстроки. Вы можете сделать параметр сопзИте входную строку, позволяя компилятор для дальнейшей оптимизации вызовов, сделанных в процедуру

  2. Поскольку вы имеете дело с фиксированной длиной и входной строкой известной длиной, то вы можете предварительно рассчитать требуемая емкость список строк и избежать перераспределения памяти при добавлении списка .

Вот как я бы об этом:

procedure StrToStringList(const aSource: String; 
          const aList: TStrings; 
          const aFixedLen: Integer); 
var 
    idx: Integer; 
    srcLen: Integer; 
begin 
    aList.Capacity := (Length(aSource) div aFixedLen) + 1; 

    idx := 1; 
    srcLen := Length(aSource); 

    while idx <= srcLen do 
    begin 
    aList.Add(Copy(aSource, idx, aFixedLen)); 
    Inc(idx, aFixedLen); 
    end; 
end; 

Расчет мощности здесь может привести к избыточной мощности 1, где входная строка делит ровно на фиксированной длины, но это незначительные накладные расходы и приемлемые, когда целью является оптимальная производительность (альтернатива является условной ветвью для изменения или расчета мощности по-разному для удовлетворения того, что может быть меньшим числом случаев).

+0

Принято, этот код кажется немного быстрее, чем другой. –

7

Я думаю, что это расточительно, чтобы изменить строку ввода. Избегайте этого:

var 
    Remaining: Integer; 
    StartIndex: Integer; 
begin 
    Remaining := Length(ASource); 
    StartIndex := 1; 
    while Remaining > 0 do 
    begin 
    AList.Add(Copy(ASource, StartIndex, AFixedLen)); 
    inc(StartIndex, AFixedLen); 
    dec(Remaining, AFixedLen); 
    end; 
end; 

Это уменьшит количество выделения кучи и копирование.

Однако я не удивлюсь, если вы заметите небольшое увеличение производительности. Чтобы выполнить какую-либо серьезную оптимизацию, нам, вероятно, нужно будет увидеть некоторые примерные входные данные.

+0

Да, ваше решение быстрее. Благодарю. Речь идет о предварительной обработке различных фрагментов данных COBOL, которые необходимо прочитать. Серьезная оптимизация, безусловно, не позволит разбивать данные на StringLists, но на данный момент я ищу способы оптимизации без перезаписи базовой структуры. –

+0

Если элементы фиксированной длины, то строковые списки могут быть не лучшим подходом. Массивы символов, скорее всего, будут быстрее. Я уверен, что вы можете сделать намного лучше кода здесь, но я не думаю, что у нас есть достаточно деталей, чтобы быть более конкретными. –

+0

После некоторого профилирования я впечатлен тем, насколько быстрым является подход на основе TStringList. Данные COBOL разделяются на данные с помощью функции здесь. После этого набор данных из набора данных анализируется из строкового списка. На данный момент нет необходимости в дальнейшей оптимизации на этом фронте. –

2

Не используйте delete внутри петли. Это приводит к перемещению всей строки. Используйте индексную переменную integer, начинающуюся с 1, и увеличивайте ее каждый раз на AFixedLen после того, как вы использовали copy, чтобы извлечь подстроку, пока не достигнете конца.

Смежные вопросы