Какие простые изменения внесли наибольшие улучшения в ваши программы на Delphi

У меня есть программа на Delphi 2009, которая обрабатывает много данных и должна работать как можно быстрее и не использовать слишком много памяти.

Какие изменения маленький простой вы внесли в свой код Delphi, которые оказали наибольшее влияние на производительность вашей программы за счет заметного сокращения времени выполнения или использования памяти?


Спасибо всем за все ваши ответы. Множество отличных советов.

Для полноты картины я опубликую несколько важных статей по оптимизации Delphi, которые я нашел.

Прежде чем приступить к оптимизации кода Delphi на About.com

Скорость и размер: 10 главных приемов также на About.com

Основы оптимизации кода и Рекомендации по оптимизации Delphi в High Performance Delphi, относящиеся к Delphi 7, но все еще очень актуальные.

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

Irfan Mulic 17.12.2008 05:10

В общем, точнее.

Serguzest 17.12.2008 05:13

Я ищу все, что может помочь. Первые 3 ответа превосходны и уже дали мне новые идеи. Будьте изобретательны и просто подумайте, что помогло вам больше всего.

lkessler 17.12.2008 05:15

Помните, Дональд Кнут сказал: «Преждевременная оптимизация - корень всех зол (или, по крайней мере, большей их части) в программировании».

Gerry Coll 17.12.2008 06:12

Да, но: «и наоборот, при разработке программного обеспечения на системном уровне вопросы производительности всегда следует рассматривать с самого начала». См .: acm.org/ubiquity/views/v7i24_fallacy.html

lkessler 17.12.2008 06:33

Хорошо, я добавил несколько. Многое зависит от ваших конкретных обстоятельств.

Jim McKeeth 17.12.2008 09:31
За пределами сигналов Angular: Сигналы и пользовательские стратегии рендеринга
За пределами сигналов Angular: Сигналы и пользовательские стратегии рендеринга
TL;DR: Angular Signals может облегчить отслеживание всех выражений в представлении (Component или EmbeddedView) и планирование пользовательских...
Sniper-CSS, избегайте неиспользуемых стилей
Sniper-CSS, избегайте неиспользуемых стилей
Это краткое руководство, в котором я хочу поделиться тем, как я перешел от 212 кБ CSS к 32,1 кБ (сокращение кода на 84,91%), по-прежнему используя...
28
6
8 155
35
Перейти к ответу Данный вопрос помечен как решенный

Ответы 35

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

  • Выключите отладку

  • Включите оптимизацию

  • Удалите все ссылки на единицы, которые ты на самом деле не используешь

  • Ищите утечки памяти

> Выключите отладку. Это не влияет на размер кода или скорость кода. Отладочная информация хранится в файлах dcu и dcp, но не добавляется в исполняемый файл.

Andreas Hausladen 17.12.2008 14:34

Предварительное распределение списков и массивов, а не их увеличение с каждой итерацией.

Это, вероятно, оказало на меня наибольшее влияние с точки зрения скорости.

При работе с tstringlist (или подобным) устанавливайте «sorted: = false» до тех пор, пока не понадобится (если вообще). Кажется, нетрудно ...

Разумно используйте SetLength () для строк и массивов. Оптимизируйте инициализацию с помощью FillChar или ZeroMemory.

Локальные переменные, созданные в стеке (например, типы записей), работают быстрее, чем переменные, выделенные в куче (объекты и New ()).

Используйте объекты повторно, а не уничтожайте, а затем создавайте. Но убедитесь, что код управления для этого работает быстрее, чем менеджер памяти!

FastMM работает быстро, поэтому код управления для повторного использования объектов должен быть очень быстрым. Это определенно то, что вы хотите профилировать, но это может иметь огромное значение, если все сделано правильно.

Jim McKeeth 17.12.2008 09:06

Дополнительные сведения о кэшировании объектов и их воссоздании: stackoverflow.com/questions/257428

Jim McKeeth 18.12.2008 12:03

Следовательно, «Но убедитесь, что код управления для этого быстрее, чем диспетчер памяти!» - Хорошо, если это что-то, что можно очистить с помощью FillChar или с помощью простого присваивания (используйте метод «Initialise (params)», а не «Create (Params)». В противном случае, вероятно, не стоит.

Gerry Coll 23.12.2008 01:33

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

Если вам нужно использовать Application.processmesssages (или аналогичный) в цикле, попробуйте вызывать его только каждую N-ю итерацию.

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

.BeginUpdate;

.EndUpdate;

;)

В основном при обновлении элементов управления графическим интерфейсом. ListView.Items.BeginUpdate; ... ListView.Items.EndUpdate;

Lars Truijens 17.12.2008 10:55

+1 - удивительно, какая разница, когда вы добавляете много вещей в списки, заметки и т. д.

robsoft 17.12.2008 12:42

Также может помочь при построении SQL с использованием qry.SQL.Add. Посмотрите, что происходит из TStringList.OnChange, если для запроса ADO назначено соединение - могут быть двусторонние обходы на уровень OLEDB

Gerry Coll 23.12.2008 01:39

BeginUpdate и endupdate больше не помогут, если у вас есть тысячи элементов. Ваше приложение будет зависать на секунды или минуты, если вы не используете TListview или TListbox в виртуальном режиме. Вот тогда вы действительно заметите прирост производительности.

Wouter van Nifterick 11.08.2010 18:41

Сможет ли кто-нибудь отредактировать этот ответ, чтобы немного лучше объяснить, для чего используются .BeginUpdate и .EndUpdate или когда их следует использовать? Я не знаком с этой функцией (пока), но она кажется полезной.

Jessica Brown 23.11.2014 22:03

Используйте множество утверждений для отладки, а затем отключите их в коде доставки.

Отключите проверку диапазона и переполнения после тщательного тестирования.

Если вам действительно, действительно, действительно нужно быть легким, вы можете отказаться от VCL. Взгляните на KOL & MCK. Конечно, если вы это сделаете, то вы обмениваете функции на уменьшение занимаемой площади.

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

Воспользуйтесь преимуществом кода проект FastCode. Части этого были включены в собственно VCL / RTL (например, FastMM), но есть еще кое-что, что вы можете использовать!

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

Может воспользоваться Пакет исправлений VCL от Андреас Хаусладен

The VCL Fix Pack is a Delphi unit that fixes VCL and RTL bugs at runtime by patching the original functions. If you want all IDE Fix Pack fixes in your application this unit is what you need. Adding the unit to your project (Delphi and C++Builder) automatically installs the patches that are avilable for your Delphi/C++Builder version.

Пакет исправлений VCL исправляет только ошибки RTL и VCL. Это не дает вам большей скорости.

Andreas Hausladen 17.12.2008 14:30

Все, что убивает жалкие ошибки, ускоряет работу, даже если только повышает производительность. Спасибо за ваши программы и модули, такие как VLC Fix Pack, Andreas ;-)

Fabricio Araujo 18.12.2008 21:08

Используйте инструмент профилирования Delphi (Some здесь или здесь) и обнаруживайте свои собственные узкие места. Оптимизация неправильных узких мест - пустая трата времени. Другими словами, если вы примените все эти предложения здесь, но проигнорируете тот факт, что кто-то поместил сон (1000) (или аналогичный) в какой-то очень важный код, это пустая трата вашего времени. Сначала устраните ваши настоящие узкие места.

Да, это лучшее решение на данный момент. И лучший профилировщик, который я видел, - это AQTime. Они предлагают старую версию для бесплатной загрузки. Это не последняя версия, но все еще работает очень хорошо. download.com/AQtime/3000-18487_4-50535.html

Mason Wheeler 18.12.2008 00:49

AQtime может быть не лучшим для всех. См .: stackoverflow.com/questions/368938/delphi-profiling-tools - но спасибо за ссылку на бесплатную версию. Мне нужно посмотреть, работает ли он с Delphi 2009.

lkessler 18.12.2008 01:10

Оно делает. Я это уже тестировал.

Mason Wheeler 18.12.2008 02:21

@ Мейсон: Это не бесплатная загрузка. Старая загрузка теперь перенаправляет на пробную версию для 6.11.

Fabricio Araujo 18.12.2008 21:15

+1. Менять рабочий код ради оптимизации без профилирования - безумие.

Caleb Hattingh 28.01.2010 12:45

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

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

  1. Создать модульные тесты
  2. Убедитесь, что все тесты пройдены
  3. Профилируйте свое приложение
  4. Рефакторинг ищет узкие места и память
  5. Повторите с шага 2 (по сравнению с предыдущим проходом)

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

Вы можете посмотреть Библиотека OmniThread от Габра, но есть ряд библиотек потоков, разрабатываемых для Delphi. Вы можете легко реализовать свою собственную параллель для использования анонимных типов.

AsyncCalls [andy.jgknet.de/blog/?page_id=100] также заслуживает внимания с чисто функциональной точки зрения.

skamradt 17.12.2008 16:03

Для старой разработки BDE, когда я только начал Delphi, я использовал множество компонентов TQuery. Кто-то сказал мне использовать мастер-деталь TTable после того, как я объяснил ему, что я делаю, и это заставило программу работать намного быстрее.

Вызов DisableControls может пропустить ненужные обновления пользовательского интерфейса.

  1. FastMM
  2. FastCode (библиотека)
  3. Используйте высокопроизводительные структуры данных, такие как хеш-таблица и т. д. Во многих местах быстрее создать один цикл, который составляет хеш-таблицу поиска для ваших данных. Использует довольно много памяти, но, безусловно, быстро. (это, возможно, самый важный, но два первых очень просты и требуют очень мало усилий)

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

mghie 17.12.2008 15:02

Прежде чем что-либо делать, определите медленные части. Не трогайте рабочий код, который работает достаточно быстро.

Уменьшите количество операций с диском. Если памяти достаточно, загрузите файл полностью в ОЗУ и выполняйте все операции в памяти.

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

mghie 17.12.2008 14:59
карта файл в память. Позвольте диспетчеру памяти обрабатывать считывание страниц и замену ненужных страниц.
Ian Boyd 13.08.2011 03:35

Прекратите использовать TStringList для всего.

TStringList - это нет, структура данных общего назначения для эффективного хранения и обработки всего, от простых до сложных типов. Ищите альтернативы. Я использую Библиотека контейнеров и алгоритмов Delphi (DeCAL, ранее известный как SDL). Julians EZDSL также должен быть хорошей альтернативой.

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

Подумайте, действительно ли база данных СУБД - идеальный выбор. Если вы только читаете данные и никогда не меняете их, то простой файл с фиксированной записью может работать быстрее, особенно если путь к данным может быть легко отображен (например, один индекс). Тривиальный двоичный поиск по фиксированному файлу записи по-прежнему очень быстр.

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

  • BeginUpdate ... EndUpdate
  • ShortString против String
  • Используйте массивы вместо TStrings и TList

Но печальный ответ заключается в том, что настройка и оптимизация дадут вам, возможно, 10% улучшение (и это опасно); редизайн может дать вам 90%. Как только вы действительно поймете цель, вы часто сможете переформулировать проблему (и, следовательно, решение) в гораздо лучших терминах.

Ваше здоровье

Извините, но это ужасное «практическое правило». Реальная правда в том, что вы не можете знать, сколько вы получите от настройки и оптимизации, пока не профилируете. Затем вы можете увидеть, подходит ли настройка или переделка. А если это опасно, значит, вы делаете это неправильно. Тест, рефакторинг, тест.

Guy Gordon 13.08.2011 20:39

Осмотрите все шлейфы и найдите способы короткого замыкания. Если вы ищете что-то конкретное и находите это в цикле, используйте команду BREAK, чтобы немедленно выйти из строя ... нет смысла повторять цикл через все остальное. Если вы знаете, что у вас нет совпадения, используйте ПРОДОЛЖИТЬ как можно быстрее.

если вам нужно выполнить сравнение строк, используйте оптимизированные функции STRCOMP или TEXTCOMP. Для простого равенства используйте оптимизированные функции SAMESTR и SAMETEXT. Всегда выбирайте SAMESTR / STRCOMP, если вы знаете, что случай всегда будет одинаковым.

Ответ принят как подходящий

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

Хотя AsyncCalls может намного больше, я нашел его полезным для этой очень простой цели. Допустим, у вас заблокирована подпрограмма следующим образом: кнопка «Отключить», «Выполнить работу», «Включить». Вы перемещаете часть Do Work в локальную функцию (назовите ее AsyncDoWork) и добавляете четыре строки кода:

var  a: IAsyncCall;    
a := LocalAsyncCall(@AsyncDoWork);  
while (NOT a.Finished) do 
  application.ProcessMessages;  
a.Sync;

Для вас это запускает AsyncDoWork в отдельном потоке, в то время как ваш основной поток остается доступным для ответа на пользовательский интерфейс (например, перетаскивание окна или нажатие кнопки «Прервать»). Когда AsyncDoWork завершается, код продолжается. Поскольку я переместил его в локальную функцию, все локальные вары доступны, и код менять не нужно.

Это очень ограниченный тип «многопоточности». В частности, это двухпотоковая передача. Вы должны убедиться, что ваша функция Async и пользовательский интерфейс не имеют доступа к одним и тем же компонентам VCL или структурам данных. (Я отключаю все элементы управления, кроме кнопки остановки.)

Я не использую это для написания новых программ. Это просто действительно быстрый и простой способ сделать старые программы более отзывчивыми.

Хотя на этот вопрос на самом деле нет ответа, я даю вам «принятый ответ», потому что он больше, чем любой другой, выглядит как очень простое решение многих проблем, и тем не менее я не слышал о нем раньше. . Я знаю о тредах, и мне не терпелось попасть в них. Но если AsyncCalls настолько просты (да, я знаю, как держать их подальше от других данных), то программисты Delphi должны помнить об этом. Мги упоминал об этом раньше, но ваше описание превосходное.

lkessler 09.11.2009 01:27

О боже, опрос и вводит ошибку повторного входа.

Ian Boyd 13.08.2011 03:31

Нет необходимости в опросе. Эта строка делает то же самое лучше: MsgAsyncMultiSync ([AsyncInterface], False, SleepTime, QS_ALLINPUT или QS_ALLPOSTMESSAGE);

Guy Gordon 13.08.2011 20:44

Если у вас есть список, используйте динамический массив чего угодно, даже следующую запись:

Для этого не нужны классы, нет освобождения, и доступ к нему очень быстрый. Даже если ему нужно вырасти, вы можете это сделать - см. Ниже. Используйте TList или TStringList, только если вам нужна большая гибкость в изменении размера.

type
  TMyRec = record
    SomeString : string;
    SomeValue : double;
  end;

var
  Data : array of TMyRec;
  I : integer;

..begin
  SetLength( Data, 100 ); // defines the length and CLEARS ALL DATA
  Data[32].SomeString := 'Hello';
  ShowMessage( Data[32] );

  // Grow the list by 1 item.
  I := Length( Data );
  SetLength( Data, I+1 );

..end;

Избегайте использования TTable с полями подстановки, когда подойдет TQuery.

У меня был отчет, который работал очень медленно в большой базе данных. Он использовал TTable с кучей полей поиска. Я повесил сетевой монитор на свое приложение и обнаружил, что огромное количество данных текло по строкам, когда я проходил через этот TTable с полями поиска. Переход на TQuery резко сократил объем трафика и существенно изменил скорость.

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

Запустите SysInternals ProcessExplorer и FileMonitor и наблюдайте за поведением вашего приложения с точки зрения ОС. Вы найдете сюрпризы, такие как неожиданная активность диска и реестра. Если вы могли подумать, что сохраняете свои настройки в реестр или в файл .ini за одну операцию, возможно, вы выполняете 100 операций записи. Вы можете обнаружить, что для записи в базу данных требуется 30 операций записи, когда вы думали, что делаете 3. Некоторые из них можно настроить с помощью таких вещей, как транзакции и буферизация. Но не раньше, чем вы сначала найдете проблемные места. У меня было такое «пробуждение», когда я прошел сертификацию U3 для своего приложения (у накопителей SanDisk U3 есть собственная сертификация). Я никогда не зарабатывал много денег, имея версию своего приложения для U3, но упражнение того стоило.

По возможности избегайте thread.synchronize. Это останавливает все и ждет поток VCL. Мы изменили большую часть наших синхронизаций, чтобы использовать thread.queue, где они могли бы выполняться асинхронно. Здесь также помогает использование анонимных методов.

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