Есть ли преимущество в использовании старого стиля `object` вместо` class` в Delphi? - PullRequest
10 голосов
/ 24 мая 2011

В Delphi в здравом уме люди используют class для определения объектов.
В Turbo Pascal для Windows мы использовали object, и сегодня вы все еще можете использовать object для создания объекта.

Разница в том, что object живет в стеке, а class живет в куче.
И, конечно же, object обесценивается.

Отложив все это в сторону:

есть ли преимущество, которое нужно получить, используя скорость object вместо класса?

Я знаю, что object не работает в Delphi 2009, но у меня есть особый вариант использования 1) , где важна скорость, и я пытаюсь найти, используя object сделает мою вещь быстрее, не делая ее глючной
Эта кодовая база находится в Delphi 7, но я могу перенести ее на Delphi 2007, пока не определилась.


1) Игра жизни Конвея

Длинный комментарий
Спасибо всем, что указали мне в правильном направлении.

Позвольте мне объяснить немного больше. Я пытаюсь сделать более быструю реализацию hashlife , см. Также здесь или здесь для простого исходного кода

Текущий рекордсмен - golly , но golly использует прямой перевод исходного кода Билла Госфера (который великолепен в качестве алгоритма, но не оптимизирован на микроуровне). Hashlife позволяет вычислять время генерации в O (log (n)).

Это делается с использованием компромисса между пространством и временем. И по этой причине hashlife требует много памяти, гигабайты не являются неслыханными. В свою очередь вы можете вычислить генерацию 2 ^ 128 (340282366920938463463374607431770000000), используя генерацию 2 ^ 127 (170141183460469231731687303715880000000) за время o (1).

Поскольку hashlife должен вычислять хэши для всех подшаблонов, которые встречаются в более крупном шаблоне, распределение объектов должно быть быстрым.

Вот решение, на котором я остановился:

Оптимизация распределения
Я выделяю один большой блок физической памяти (настраиваемый пользователем), скажем, 512 МБ. Внутри этого объекта я выделяю то, что я называю сырными стопками . Это обычный стек, в котором я нажимаю и извлекаю, но всплывающее окно также может быть из середины стека. Если это произойдет, я отмечу его в списке free (это обычный стек). При нажатии я сначала проверяю список free, если ничего не освобождается, я нажимаю как обычно. Я буду использовать записи в соответствии с рекомендациями, это выглядит как решение с наименьшими затратами.

Из-за того, как работает hashlife, очень мало пингов pop и большого количества push es. Я храню отдельные стеки для структур разных размеров, следя за тем, чтобы доступ к памяти был выровнен по границам 4/8/16 байтов.

Другие оптимизации

  • рекурсивное удаление
  • оптимизация кеша
  • использование inline
  • предварительный расчет хешей (сродни радужным таблицам)
  • выявление патологических случаев и использование резервного алгоритма
  • использование графического процессора

Ответы [ 4 ]

13 голосов
/ 24 мая 2011

Для использования обычного программирования ООП вы всегда должны использовать class вид .У вас будет самая мощная объектная модель в Delphi, включая интерфейс и дженерики (в более поздних версиях Delphi).

1.Записи, указатели и объекты

Записи могут быть злыми (медленное скрытое копирование, если вы забыли объявить параметр как const, записать скрытый медленный код очистки, a fillcharсделает любую строку в записи утечкой памяти ...), но иногда они очень удобны для доступа к двоичной структуре (например, к небольшому значению) через указатель.

Динамический массив крошечныхзаписи (например, с одним целым и одним двойным полем) будут на намного быстрее, чем TList небольших классов;с нашей TDynArray оболочкой вы получите высокоуровневый доступ к записям с сериализацией, сортировкой, хэшированием и т. д.

Если вы используете указатели, вы должны знать, что вы делаете,Определенно предпочтительнее придерживаться классов и TPersistent, если вы хотите использовать волшебную «модель владения компонентами VCL».

Наследование не допускается для записей.Вам нужно либо использовать «вариант записи» (используя ключевое слово case в его определении типа), либо использовать вложенные записи.При использовании C-подобного API вам иногда придется использовать объектно-ориентированные структуры.Использование вложенных записей или вариантов записей ИМХО гораздо менее понятно, чем в старой доброй модели наследования «объекта».

2.Когда использовать объект

Но в некоторых местах объекты являются хорошим способом доступа к уже существующим данным.

Даже объектная модель лучше, чем новая модель записи, поскольку онаобрабатывает простое наследование.

В записи в блоге прошлым летом я опубликовал некоторые возможности по-прежнему использовать объекты:

  • Файл отображения памяти, которыйЯ хочу очень быстро разобрать: указатель на такой объект просто великолепен, и у вас все еще есть методы под рукой;Я использую это для TFileHeader или TFileInfo, которые отображают заголовок .zip в SynZip.pas; ​​

  • Структура Win32, как определено вызовом API, в который я помещаю удобные методы для простотыдоступ к данным (для этого вы можете использовать запись, но если в структуре есть какая-то объектная ориентация - что очень распространено - вам придется вкладывать записи, что не очень удобно);

  • Временная структура, определенная в стеке, используется только во время процедуры: я использую ее для TZStream в SynZip.pas или для наших классов, связанных с RTTI, которые отображают сгенерированный Delphi RTTI в объектно-ориентированном виде, а не какTypeInfo, который ориентирован на функцию / процедуру.Благодаря непосредственному отображению содержимого памяти RTTI наш код работает быстрее, чем использование новых классов RTTI, созданных в куче.Мы не создаем никакой памяти, которая для ORM-фреймворка, подобного нашей, хороша для своей скорости.Нам нужно много информации RTTI, но она нужна нам быстро, нам это нужно напрямую.

3.Как реализация объекта нарушена в современном Delphi

Тот факт, что объект поврежден в современном Delphi, является позором, ИМХО.

Обычно, если вы определяете запись в стеке, содержащуюнекоторые переменные с подсчетом ссылок (например, строки) будут инициализированы магическим кодом компилятора на начальном уровне метода / функции:

type TObj = object Int: integer; Str: string; end;
procedure Test;
var O: TObj
begin // here, an _InitializeRecord(@O,TypeInfo(TObj)) call is made
  O.Str := 'test';
  (...)
end;  // here, a _FinalizeRecord(@O,TypeInfo(TObj)) call is made

Эти _InitializeRecord и _FinalizeRecord will "подготовить "затем" выпустить "переменную O.Str.

В Delphi 2010 я обнаружил, что иногда этот _InitializeRecord () не всегда создавался.Если запись не имеет каких-либо открытых полей, скрытые вызовы иногда не генерируются компилятором.

Просто создайте исходный код заново, и будет ...

Единственное решение, которое явыяснилось, что вместо объекта использовалось ключевое слово record.

Вот как выглядит полученный код:

/// used to store and retrieve Words in a sorted array
// - is defined either as an object either as a record, due to a bug
// in Delphi 2010 compiler (at least): this structure is not initialized
// if defined as a record on the stack, but will be as an object
TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif}
public
  Values: TWordDynArray;
  Count: integer;
  /// add a value into the sorted array
  // - return the index of the new inserted value into the Values[] array
  // - return -(foundindex+1) if this value is already in the Values[] array
  function Add(aValue: Word): PtrInt;
  /// return the index if the supplied value in the Values[] array
  // - return -1 if not found
  function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif}
end;

{$ifdef UNICODE}record{$else}object{$endif} ужасен ... но ошибка генерации кодане происходило с тех пор ..

Полученные изменения в исходном коде невелики, но немного разочаровывают. Я обнаружил, что более старая версия IDE (например, Delphi 6/7) не может проанализировать такое объявление, поэтому иерархия классов будет нарушена в редакторе ...: (

Обратная совместимость должна включать в себя регрессионные тесты. Многие пользователи Delphi остаются с этим продуктом из-за существующего кода. Нарушающие функции очень проблематичны для будущего Delphi, ИМХО: если вам нужно переписать много кода, не стоит ли просто переключить проект на C # или Java?

7 голосов
/ 24 мая 2011

Object не был методом Delphi 1 для настройки объектов; это был недолговечный метод настройки Turbo Pascal, который был заменен моделью Delphi TObject в Delphi 1. Он был сохранен для обратной совместимости, но его следует избегать по нескольким причинам:

  1. Как вы заметили, в более поздних версиях он сломан. И AFAIK нет планов это исправить.
  2. Это концептуально неправильная объектная модель. Весь смысл объектно-ориентированного программирования, единственное, что действительно отличает его от процедурного программирования, это подстановка Лискова (наследование и полиморфизм), а типы наследования и значения не смешиваются.
  3. Вы теряете поддержку многих функций, которые требуют потомков TObject.
  4. Если вам действительно нужны типы значений, которые не нужно динамически выделять и инициализировать, вы можете вместо этого использовать записи. Вы не можете наследовать от них, но вы не можете сделать это очень хорошо с object, так что вы ничего здесь не потеряете.

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

6 голосов
/ 24 мая 2011

Когда предоставляется выбор между «быстрым и возможно сломанным» и «быстрым и правильным», всегда выбирайте последнее.

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

1 голос
/ 24 мая 2011

В более ранних версиях Delphi, которые не поддерживали записи с методами, тогда использование object было способом размещения ваших объектов в стеке. Очень редко это приносило бы ощутимые преимущества в производительности. В наше время record лучше. Единственная функция, отсутствующая в record, - это возможность наследовать от другого record.

Вы очень много сдаётесь, когда переходите с class на record, поэтому учитывайте это только в том случае, если выигрыш в производительности огромен.

...