Объявить локально или глобально в Delphi?

У меня есть процедура, которую моя программа вызывает десятки тысяч раз, которая использует такую ​​общую структуру:

procedure PrintIndiEntry(JumpID: string);

type
  TPeopleIncluded = record
    IndiPtr: pointer;
    Relationship: string;
  end;

var
  PeopleIncluded: TList<TPeopleIncluded>;
  PI: TPeopleIncluded;

begin { PrintIndiEntry }

  PeopleIncluded := TList<TPeopleIncluded>.Create;

 { A loop here that determines a small number (up to 100) people to process }
  while ... do begin

    PI.IndiPtr := ...;
    PI.Relationship := ...;
    PeopleIncluded.Add(PI);

  end;

  DoSomeProcess(PeopleIncluded);

  PeopleIncluded.Clear;
  PeopleIncluded.Free;

end { PrintIndiEntry }

В качестве альтернативы я могу объявить PeopleIncluded глобально, а не локально, следующим образом:

unit process;

interface

type
  TPeopleIncluded = record
    IndiPtr: pointer;
    Relationship: string;
  end;

var
  PeopleIncluded: TList<TPeopleIncluded>;
  PI: TPeopleIncluded;

procedure PrintIndiEntry(JumpID: string);

begin { PrintIndiEntry }

 { A loop here that determines a small number (up to 100) people to process }
  while ... do begin

    PI.IndiPtr := ...;
    PI.Relationship := ...;
    PeopleIncluded.Add(PI);

  end;

  DoSomeProcess(PeopleIncluded);

  PeopleIncluded.Clear;

end { PrintIndiEntry }

procedure InitializeProcessing;
begin
  PeopleIncluded := TList<TPeopleIncluded>.Create;
end;

procedure FinalizeProcessing;
begin
  PeopleIncluded.Free;
end;

У меня вопрос, лучше ли в этой ситуации объявлять PeopleIncluded глобально, а не локально. Я знаю, что теория заключается в том, чтобы определять локально, когда это возможно, но я хотел бы знать, есть ли какие-либо проблемы, о которых стоит беспокоиться, в отношении выполнения десятков тысяч «create» и «free»? Сделав их глобальными, вы сделаете только одно создание и одно бесплатное.

Какой метод рекомендуется использовать в этом случае?

Если рекомендуемый метод по-прежнему состоит в том, чтобы определять его локально, то мне интересно, есть ли ситуации, когда лучше определять глобально, когда определение локально по-прежнему возможно.


person lkessler    schedule 30.06.2012    source источник


Ответы (3)


Что касается видимости процесса, я бы хотел создать class, содержащий все данные, один метод для инициализации и один деструктор, а затем один метод для вызова процесса. Затем оптимизируйте его по скорости с помощью профилировщика. Никогда не используйте глобальные переменные, но инкапсулируйте свой процесс в небольших повторно используемых и многопоточных классах.

Вкратце о скорости процесса: «Преждевременная оптимизация - корень всех зол», - цитирует Дональда Кнута, цитируя К.А.Р. Хора. Я уверен, что узкое место не в TList Create/Free, а в вашем основном цикле процесса.

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

Вы можете предварительно выделить переменные и / или кэшировать повторно используемые данные, использовать статические массивы вместо TList (или выделить их и повторно использовать с помощью внешней count переменной) и избежать выделения string экземпляров (и передачи их без параметра const). Но, возможно, не волшебное решение.

Чтобы ускорить процесс, изменение алгоритма почти всегда лучше, чем некоторые уловки низкоуровневой реализации, подобные тем, которые вы пробовали. Использование предварительно рассчитанных таблиц поиска, предварительное выделение памяти, избегайте создания временных string (например, используйте PosEx вместо copy субцепей; или смешивайте _10 _ / _ 11_), избегайте обращений к диску, API или БД, меняйте структуры памяти, сортируйте данные затем используют двоичный поиск, используют хеширование или циклы без конвейера, заставляют вас обрабатывать многопоточность и так далее ... невозможно угадать, что следует изменить, не имея всего исходного кода вашего процесса, и запустить профилировщик на реальные данные!

person Arnaud Bouchez    schedule 30.06.2012
comment
Я ценю ваше рассуждение, @Arnaud, о создании класса для размещения данных. Ваша работа впечатляет, и я собираюсь реализовать некоторые из ваших компонентов. Как программист старой школы времен до ООП, мое программирование хорошо структурировано и организовано, но мне не хватает каких-либо настраиваемых ООП, кроме использования Delphi и сторонних объектов. Мне трудно сделать этот первый шаг. Для этого олдтаймера это не очевидно. Могу ли я сделать TPeopleIncluded ‹T: class› = class (TList ‹T›? Где я могу использовать частные, защищенные, общедоступные, конструкторы, свойства. Еще сложнее искусство разделения работы на классы. Помощь. - person lkessler; 01.07.2012
comment
См. Также: stackoverflow.com/questions/11284677/ - person lkessler; 01.07.2012

Ваш первый код лучше. Хорошо скрыть любую структуру данных извне. Первый код показывает, что PeopleIncluded не используется вне вашей процедуры, поэтому лучше оставить его внутри. Если выставить его снаружи (в разделе интерфейса), он станет видимым для всех других устройств, использующих это устройство. Поступая таким образом, вы рискуете получить к нему доступ и изменить другие устройства. Это может произойти намеренно или непреднамеренно вами или другими людьми, использующими ваш код, и может привести к нежелательным результатам.

Если вы не можете или все еще хотите сделать его не локальным для вашей процедуры, лучше объявить и тип, и var в разделе реализации, а не в разделе интерфейса.

Вам не нужно беспокоиться о производительности. Операции с памятью выполняются быстро, и я предлагаю беспокоиться об этом только тогда, когда ДЕЙСТВИТЕЛЬНО возникает узкое место в производительности. Когда это происходит, вы можете профилировать свое приложение, чтобы найти узкое место, и оптимизировать ТОЛЬКО код, который вызывает узкое место. Это позволит избежать ненужных усилий по оптимизации кода, который не влияет (или минимально) на производительность вашего приложения.

person Hendra    schedule 30.06.2012
comment
ИМХО обе версии плохие. В таком случае всегда стоит создавать небольшие повторно используемые классы. - person Arnaud Bouchez; 30.06.2012

дело # 3

unit process;

interface

type
  TPeopleIncluded = record
    IndiPtr: pointer;
    Relationship: string;
  end;


procedure PrintIndiEntry(JumpID: string);
var
  PeopleIncluded: TList<TPeopleIncluded>;
  PI: TPeopleIncluded;

begin { PrintIndiEntry }

 { A loop here that determines a small number (up to 100) people to process }
  while ... do begin

    PI.IndiPtr := ...;
    PI.Relationship := ...;
    PeopleIncluded.Add(PI);

  end;

  DoSomeProcess(PeopleIncluded);

  PeopleIncluded.Clear;

end { PrintIndiEntry }

procedure InitializeProcessing;

Dosomeprocess ((PeopleIncluded) также должен знать эту запись. Объявление одной и той же записи в нескольких местах - это своего рода плохо. Вот почему вы хотите сделать ее доступной для Dosomeprocess. Однако переменные лучше объявлять локально.

Хотя, возможно, лучше всего будет создать небольшой класс.

person Pieter B    schedule 30.06.2012