Насколько заметна разница в производительности между TList, TObjectList и простым массивом, если ее можно оценить?

StackOverflow https://stackoverflow.com/questions/5352021

Вопрос

*< sizesОбобщение :

Ознакомьтесь с комментариями экспертов Delphi. Специально для меня я бы попытался использовать старый TList / TObjectList, как предложил Дэвид, и использовать жесткое приведение и свойство TObjectList.List, как предложил А.Бушез. Я попробую TDynArray при рефакторинге в будущем.

=====================================================================

Скажем, у меня есть класс TAtom, как определено в следующем коде. Есть примерно от hundreds до генерации кода TAtom во время выполнения, на данный момент thousands. Во время выполнения мне нужно выполнить простую математику с плавающей запятой на stored in a dynamic array всех существующих экземпляров TAtom чаще, чем TAtom.X/Y/Z раз в секунду.

Теперь мне нужно добавить возможность создания кодов кодовых кодов, кодовых кодов и кодовых кодов экземпляров TAtom во время выполнения. Похоже, что я могу выбрать (1) запросить большой массив; (2) придерживаться динамического массива и вручную SetLength; (3) перейти на обычный TList; (4) переключиться на обычный TObjectList.

Я хочу избежать (1), если в этом нет необходимости, потому что тогда мне придется изменить довольно много сигнатур функций. (2) тоже выглядит не очень хорошо, потому что TList / TObjectList, похоже, создан для этой задачи. Однако, поскольку требуется приведение типов с использованием обычного TList / TObjectList, может ли кто-нибудь прокомментировать возможное снижение производительности? Я имею в виду, что было бы лучше, если бы можно было оценить нагрузку на производительность, прежде чем я переписываю код. Если производительность заметно упадет, могу ли я использовать другие методы?

Кроме того, мне интересно, есть ли разница в производительности между использованием TList и TObjectList?

родовое слово
Это было полезно?

Решение

Если вы используете генерирующий код тегов и нет необходимости в преобразовании.

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

До тех пор, пока вы избегаете Generics.Collections.TObjectList<TAtom> и выбираете более разумную стратегию распределения, динамические массивы эквивалентны всем классам SetLength(A, Length(A)+1).

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

Все это несколько умозрительно, и вам действительно нужно измерить - иначе мы можем только догадываться.

Другие советы

Могу я добавить еще один вариант в ваш список?

Если вы не используете какую-либо функцию наследования для данных в TAtom, вы можете использовать record вместо class. Каждый экземпляр класса должен быть выделен в памяти, заполнен нулями и инициализирован индивидуально. Getmem/Freemem всегда требует затрат, и фрагментация памяти возрастает.

Предварительно назначенный динамический код генерирующего тега будет быстрее, чем добавление отдельных экземпляров класса. И данные лучше подходят для кэша L1 / L2 ЦП.

Для вставки и удаления массив таких записей будет медленнее, чем array of record, если у вас огромное количество элементов, потому что будет больше данных для удаления / вставки (оба TList поддерживают только список указателей). Для еще более быстрой вставки / удаления лучше использовать связанный список.

Из-за внутреннего уведомления в механизме TList/TObjectList возникают некоторые накладные расходы. механизм. И свойство TList/TObjectList может быть немного медленнее (из-за проверки диапазона), чем использование непосредственно динамического массива.

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

родовое слово

Работает с Delphi 6 до XE.

С более новой версией Delphi, поддерживающей дженерики, вам лучше пойти в этом направлении.

<цитата>

Однако, поскольку приведение типов необходимо использовать обычный TList / TObjectList, может кто-нибудь прокомментируйте возможную производительность ударил?

Если вы вводите приведение с помощью формы

родовое слово

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

родовое слово

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

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

Поскольку TObjectList является прямым потомком TList, производительность будет очень близка.

Первый вопрос: мы говорим о Classes.TList и Contnrs.TObjectList или мы говорим о Generics.Collections.TList, соответственно, Generics.Collections.TObjectList?

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


Если мы говорим о «старом» TList и TObjectList, то нам нужно сравнивать только TList с эквивалентом динамического массива, поскольку TObjectList является потомком TList, поэтому он наследует все его характеристики производительности. TList использует блок памяти, выделенный с помощью ReallocMem. Внутренне динамический массив делает то же самое, поэтому существенной разницы быть не должно!

Заключение

Если есть какая-либо разница в производительности между ними, вероятно, это связано с тем, что наивное использование динамического массива использует ужасный код SetLength(A, Length(A)+1), в то время как лучшая реализация во всех предоставленных Delphi контейнерах предварительно выделяет память в больших частях. При правильном коде между этими альтернативами не должно быть значительной разницы!

Создайте тестовый проект и измерьте время добавления, вставки и удаления тысяч экземпляров TAtom, используя четыре метода.Затем решите, какой из них использовать.

TList и TObjectList, вероятно, быстрее, чем динамический массив при добавлении, вставке и удалении, потому что динамический массив постоянно должен перераспределяться.Реализация TList и TObjectList этого не делает.

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

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

В конце концов, затраты на арифметические операции, выполняемые с помощью TAtom, должны преобладать во время выполнения, поэтому выбор dynarray или TListXXX не имеет значения.

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

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

  • связанный список, если вам не нужен доступ к атомам по индексу
  • дерево, если вы используете его для пространственного раздела ваших атомов, вы также можете использовать этот раздел для хранения ваших атомов, а не для массива.
  • разрешение элементов undefined / nil в вашем массиве / списке и поддержание стека элементов undefined / nil и индекса, если вам нужен отсортированный список (потенциально наиболее производительное решение, но также, вероятно, наиболее сложное для правильного использованияс точки зрения эффективности)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top