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

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

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


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

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

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

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

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


person Community    schedule 17.12.2008    source источник
comment
Вы должны указать конкретную проблему, чтобы мы могли попытаться ответить.   -  person Irfan Mulic    schedule 17.12.2008
comment
В общем, будь более конкретным.   -  person Serguzest    schedule 17.12.2008
comment
Я ищу все, что может помочь. Первые 3 ответа превосходны и уже дали мне новые идеи. Будьте изобретательны и просто подумайте, что помогло вам больше всего.   -  person lkessler    schedule 17.12.2008
comment
Помните, что преждевременная оптимизация Дональда Кнута - корень всего зла (или, по крайней мере, большей его части) в программировании.   -  person Gerry Coll    schedule 17.12.2008
comment
Да, но: и наоборот, при разработке программного обеспечения на системном уровне вопросы производительности всегда следует учитывать с самого начала. См .: acm.org/ubiquity/views/v7i24_fallacy.html   -  person lkessler    schedule 17.12.2008
comment
Хорошо, я добавил несколько. Многое зависит от ваших конкретных обстоятельств.   -  person Jim McKeeth    schedule 17.12.2008


Ответы (35)


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

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

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

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

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

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

person Community    schedule 29.07.2009
comment
Хотя на этот вопрос на самом деле нет ответа, я даю вам общепринятый ответ, потому что он больше, чем любой другой, выглядит как очень простое решение многих проблем, о котором я еще не слышал. Я знаю о тредах, и мне было неприятно влезать в них. Но если AsyncCalls настолько просты (да, я знаю, как держать их подальше от других данных), то программисты Delphi должны помнить об этом. Мги упоминал об этом раньше, но ваше описание превосходное. - person lkessler; 09.11.2009
comment
О боже, опрос и вводит ошибку повторного входа. - person Ian Boyd; 13.08.2011
comment
Нет необходимости в опросе. Эта строка делает то же самое лучше: MsgAsyncMultiSync ([AsyncInterface], False, SleepTime, QS_ALLINPUT или QS_ALLPOSTMESSAGE); - person Guy Gordon; 13.08.2011

.BeginUpdate;

.EndUpdate;

;)

person Community    schedule 17.12.2008
comment
В основном при обновлении элементов управления графическим интерфейсом. ListView.Items.BeginUpdate; ... ListView.Items.EndUpdate; - person Lars Truijens; 17.12.2008
comment
+1 - удивительно, какая разница, когда вы добавляете много вещей в списки, заметки и т. Д. - person robsoft; 17.12.2008
comment
Также может помочь при построении SQL с использованием qry.SQL.Add. Посмотрите, что происходит из TStringList.OnChange, если для запроса ADO назначено соединение - могут быть двусторонние обходы на уровень OLEDB - person Gerry Coll; 23.12.2008
comment
BeginUpdate и endupdate больше не помогут, если у вас есть тысячи элементов. Ваше приложение будет зависать на секунды или минуты, если вы не используете TListview или TListbox в виртуальном режиме. Вот тогда вы действительно заметите прирост производительности. - person Wouter van Nifterick; 11.08.2010
comment
Сможет ли кто-нибудь отредактировать этот ответ, чтобы немного лучше объяснить, для чего используются .BeginUpdate и .EndUpdate или когда их следует использовать? Я не знаком с этой функцией (пока), но она кажется полезной. - person Jessica Brown; 23.11.2014

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

person Community    schedule 17.12.2008
comment
Да, это лучшее решение на данный момент. И лучший профилировщик, который я видел, - это AQTime. Они предлагают старую версию для бесплатной загрузки. Это не последняя версия, но все еще работает очень хорошо. download.com/AQtime/3000-18487_4-50535.html - person Mason Wheeler; 18.12.2008
comment
AQtime не может быть лучшим для всех. См .: stackoverflow.com/questions/368938/delphi-profiling-tools, но спасибо за ссылку на бесплатная версия. Мне нужно посмотреть, работает ли он с Delphi 2009. - person lkessler; 18.12.2008
comment
Оно делает. Я уже это тестировал. - person Mason Wheeler; 18.12.2008
comment
@ Мейсон: Это не бесплатная загрузка. Старая загрузка теперь перенаправляет на пробную версию для 6.11. - person Fabricio Araujo; 18.12.2008
comment
+1. Менять рабочий код ради оптимизации без профилирования - это безумие. - person Caleb Hattingh; 28.01.2010

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

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

person Community    schedule 17.12.2008

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

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

person Community    schedule 17.12.2008

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

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

person Community    schedule 17.12.2008

  1. FastMM
  2. FastCode (библиотека)
  3. Используйте высокопроизводительные структуры данных, например хеш-таблицу и т. Д. Во многих местах быстрее создать один цикл, который составляет хеш-таблицу поиска для ваших данных. Использует довольно много памяти, но, безусловно, быстро. (это, возможно, самый важный, но два первых очень просты и требуют очень мало усилий)
person Community    schedule 17.12.2008
comment
Касательно 3: Ничего не меняйте, не измерив должным образом эффект, который это имеет. Использование структур данных с интенсивным использованием памяти может фактически замедлить работу, если шаблоны доступа к памяти постоянно делают недействительными строки кэша. - person mghie; 17.12.2008

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

person Community    schedule 17.12.2008
comment
+1 по этому поводу. Кроме того, даже если у вас есть весь файл в памяти, обрабатывайте его таким образом (последовательно), чтобы содержимое кеша сохранялось в максимально возможной степени. Применимо к размеру данных ›› размер кеш-памяти процессора, естественно. - person mghie; 17.12.2008
comment
Сопоставьте файл с памятью. Позвольте диспетчеру памяти обрабатывать считывание страниц и замену ненужных страниц. - person Ian Boyd; 13.08.2011

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

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

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

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

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

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

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

person Community    schedule 17.12.2008
comment
FastMM работает быстро, поэтому код управления для повторного использования объектов должен быть очень быстрым. Это определенно то, что вы хотите профилировать, но это может иметь огромное значение, если все сделано правильно. - person Jim McKeeth; 17.12.2008
comment
Дополнительные сведения о кешировании объектов и их воссоздании: stackoverflow.com/questions/257428 - person Jim McKeeth; 18.12.2008
comment
Следовательно, но убедитесь, что код управления для этого работает быстрее, чем диспетчер памяти! - ОК, если это что-то, что можно очистить с помощью FillChar или с помощью простого присваивания (используйте метод Initialise (params), а не Create (Params). В противном случае, вероятно, не стоит того. - person Gerry Coll; 23.12.2008

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

person Community    schedule 17.12.2008

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

Для этого не нужны классы, нет освобождения, и доступ к нему очень быстрый. Даже если ему нужно вырасти, вы можете это сделать - см. Ниже. Используйте 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;
person Community    schedule 29.07.2009

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

person Community    schedule 17.12.2008

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

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

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

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

person Community    schedule 17.12.2008
comment
›Отключите отладку. Это не влияет на размер кода или скорость кода. Отладочная информация хранится в файлах dcu и dcp, но не добавляется в исполняемый файл. - person Andreas Hausladen; 17.12.2008

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

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

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

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

person Community    schedule 18.12.2008
comment
Извините, но это ужасное «практическое правило». На самом деле, вы не можете знать, сколько вы получите от настройки и оптимизации, пока не профилируете. Затем вы увидите, подходит ли настройка или переделка. А если это опасно, значит, вы делаете это неправильно. Тест, рефакторинг, тест. - person Guy Gordon; 13.08.2011

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

person Community    schedule 21.04.2009

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

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

person Community    schedule 17.12.2008

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

person Community    schedule 17.12.2008

Возможно, воспользуйтесь преимуществами VCL FixPack от Андреас Хаусладен

Пакет исправлений VCL - это модуль Delphi, который исправляет ошибки VCL и RTL во время выполнения путем исправления исходных функций. Если вы хотите, чтобы в вашем приложении были исправлены все исправления IDE Fix Pack, этот модуль - то, что вам нужно. При добавлении модуля в ваш проект (Delphi и C ++ Builder) автоматически устанавливаются исправления, доступные для вашей версии Delphi / C ++ Builder.

person Community    schedule 17.12.2008
comment
Пакет исправлений VCL исправляет только ошибки RTL и VCL. Это не дает вам большей скорости. - person Andreas Hausladen; 17.12.2008
comment
Все, что убивает жалкие ошибки, ускоряет работу, даже если только повышает производительность. Спасибо за ваши программы и модули, такие как VCL Fix Pack, Andreas ;-) - person Fabricio Araujo; 18.12.2008

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

person Community    schedule 21.04.2009

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

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

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

person Community    schedule 28.01.2010

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

person Community    schedule 19.03.2010

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

person Community    schedule 20.03.2010