tag:blogger.com,1999:blog-1702873441071265539.post1187349984463330990..comments2023-05-29T19:02:20.570+03:00Comments on Блог GunSmoker-а: Основы оптимизации прикладных программ на DelphiGunSmokerhttp://www.blogger.com/profile/15611696588191431330noreply@blogger.comBlogger20125tag:blogger.com,1999:blog-1702873441071265539.post-45979136173919611342017-12-09T09:03:42.042+03:002017-12-09T09:03:42.042+03:00Лично я пользуюсь AQTime, так что: бесплатный - не...Лично я пользуюсь AQTime, так что: бесплатный - не порекомендую.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-57405568958920795442017-12-08T22:36:03.711+03:002017-12-08T22:36:03.711+03:00привет, какой профайлер порекомендуете для свежих ...привет, какой профайлер порекомендуете для свежих версий Delphi, уже не комплектуемых AQTime.Anonymoushttps://www.blogger.com/profile/12797475091853020984noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-70579582947913972532017-12-01T10:28:56.849+03:002017-12-01T10:28:56.849+03:00Несколько push/pop-в?! Это чертовски огромное преу...<b>Несколько push/pop-в</b>?! Это чертовски огромное преуменьшение!<br /><br />procedure MyProcedure(intf: IInvokable);<br />begin<br /> if intf <> nil then;<br />end;<br /><br />Вот асм<br />00727407 51 push ecx<br />00727408 8945FC mov [ebp-$04],eax<br />0072740B 8B45FC mov eax,[ebp-$04]<br />0072740E E8913DCEFF call @IntfAddRef<br />00727413 33C0 xor eax,eax<br />00727415 55 push ebp<br />00727416 683B747200 push $0072743b<br />0072741B 64FF30 push dword ptr fs:[eax]<br />0072741E 648920 mov fs:[eax],esp<br />FormMain.pas.1072: if intf <> nil then;<br />00727421 837DFC00 cmp dword ptr [ebp-$04],$00<br />FormMain.pas.1074: end;<br />00727425 33C0 xor eax,eax<br />00727427 5A pop edx<br />00727428 59 pop ecx<br />00727429 59 pop ecx<br />0072742A 648910 mov fs:[eax],edx<br />0072742D 6842747200 push $00727442<br />00727432 8D45FC lea eax,[ebp-$04]<br />00727435 E8F63CCEFF call @IntfClear<br />0072743A C3 ret <br />0072743B E934F5CDFF jmp @HandleFinally<br />00727440 EBF0 jmp $00727432<br />00727442 59 pop ecx<br />00727443 5D pop ebp<br />00727444 C3 ret <br /><br />Вызов двух функций (а значит, наверняка двойное обнуление кеша инструкций), плюс целая куча манипуляций.<br /><br />А вот вариант с const<br />00727404 55 push ebp<br />00727405 8BEC mov ebp,esp<br />00727407 51 push ecx<br />00727408 8945FC mov [ebp-$04],eax<br />FormMain.pas.1072: if intf <> nil then;<br />0072740B 837DFC00 cmp dword ptr [ebp-$04],$00<br />FormMain.pas.1074: end;<br />0072740F 59 pop ecx<br />00727410 5D pop ebp<br />00727411 C3 ret <br /><br />На тестах вариант с const быстрее <b>на порядок</b>. Да, возможно для больших функций этот прирост потеряется, но помимо того, что указание const почти ничего не стоит, оно еще и дисциплинирует (а то любят многие модифицировать эти параметры внутри функции, ибо лень заводить переменную), да еще и спасает от ошибок плана "возвращаем в параметре другое значение, но забыли указать var".<br />Что же касается отказа от функции - это уже вопрос за рамками темы. Не всегда это допустимо, да и дублирование кода это не сильно хорошо.Spellhttps://www.blogger.com/profile/03919939109914212376noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-16496663138829265032017-12-01T00:07:59.384+03:002017-12-01T00:07:59.384+03:00> Первые три без const добавляют автомагическую...<i> > Первые три без const добавляют автомагическую секцию try-finally, что просто убивает производительность</i><br /><br />Есть мнение, что если несколько push/pop-в прям ТАК портят производительность, то значительного прироста производительности можно добиться просто отказавшись от такой короткой функции.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-67337235033183638402017-11-30T19:14:48.545+03:002017-11-30T19:14:48.545+03:00Я бы добавил в перечень "изначальных" оп...Я бы добавил в перечень "изначальных" оптимизаций на этапе написания кода:<br />1. const в параметрах-строках, интерфейсах, дин.массивах, записях больше 8-10 байт. Первые три без const добавляют автомагическую секцию try-finally, что просто убивает производительность, а записи копируются, что тоже не улучшает ситуацию.<br />2. Скрытые автоуправляемые переменные - то же самое. Например, такой неочевидный вариант<br />begin<br /> Inc(Field);<br /> if ConditionThatNeverHappens then<br /> raise Exception.Create(Format('That''s bad: %d', [Field]));<br />end;<br />Решение - не использовать Format, вместо него вызывать CreateFmt.<br />3. Для коротких "оберточных" функций inline - наш друг.Spellhttps://www.blogger.com/profile/03919939109914212376noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-80370278662867555562017-11-30T19:04:18.473+03:002017-11-30T19:04:18.473+03:00Для двух десятков прямой перебор в самом деле може...Для двух десятков прямой перебор в самом деле может оказаться быстрее любого хеша (его ведь еще надо вычислить!) или двоичного поиска. А уж тем более если это идущие встык небольшие записи, которые прекрасно грузятся в кеш процессора.Spellhttps://www.blogger.com/profile/03919939109914212376noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-12968178013265744852013-01-25T18:26:45.134+04:002013-01-25T18:26:45.134+04:00AQTime -- один из самых ценных инструментов. Довол...AQTime -- один из самых ценных инструментов. Довольно часто находит такие узкие места, которые никогда и в голову не придут при "ручной" оптимизации.<br /><br />Что интересно, впервые с этим продуктом познакомился еще много лет назад в институте на серии лабораторных работ по многопоточной разработки.<br /><br />Кстати, настроить колонки от профилировщика в делфи можно -- надо щелкнуть правой кнопкой по этим колонкам и выбрать пункт "Profile" -> "Customize" -- откроется окно, где можно галочками указать, какие колонки показывать рядом с кодом.<br /><br />Более того, если делать отладку в отдельном приложении AQTime, а не в делфи, то дополнительно можно выбирать формат отображения тех значений, что рядом с кодом -- например, в виде прогресс-бара (чем больше величина, тем он длиннее) -- что очень удобно и наглядно (см. скриншот: https://dl.dropbox.com/u/6645734/CloudShot/shot_25012013_182150.png )JayDihttp://jaydi.runoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-48692586573347131822013-01-16T17:23:59.953+04:002013-01-16T17:23:59.953+04:00Кстати, кажется начиная с XE или XE2, появился мод...Кстати, кажется начиная с XE или XE2, появился модуль System.Diagnostics, и там есть секундомер TStopWatch. Очень удобная обёртка над GetTickCount, QueryPerformanceCounter.<br /><br />Просто вызвать .StartNew ... .Stop, а затем посмотреть Elapsed: TTimeSpan (операторы Implicit и Explicit перегружены, чтобы сразу выдавать строку)Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-53517351483425584792013-01-10T11:18:28.517+04:002013-01-10T11:18:28.517+04:00Имелось в виду только следующее: далеко разнесённы...Имелось в виду только следующее: далеко разнесённые по виртуальной памяти блоки могут оказаться рядом в физической памяти и поэтому быть выбраны в кэш как последовательные. Я просто встречал советы, которые, похоже, явно не учитывают что прикладные программы работают с виртуальной памятью, а не физической.<br /><br />Но, конечно, я не говорю, что улучшать локальность вообще не нужно.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-39836531071971420892013-01-10T10:28:21.154+04:002013-01-10T10:28:21.154+04:00>Поэтому всевозможные циклы по элементам будут ...>Поэтому всевозможные циклы по элементам будут несколько быстрее, чем проходы по разрознённым блокам. Впрочем, я не думаю, что в современном мире виртуальной памяти это может иметь такое уж сильное значение. <br /><br />С этим моментом рискну не согласиться.<br />Замена массива объектов на массив записей (в delphi) в случае часто вызываемых функций поиска (сотни тысяч раз) может существенно ускорить работу.<br />Профилировал программу, использовал GpProfile. По отчету профайлера немалое время занимали вызовы поиска по кэшу из двух десятков объектов. Заменил список объектов (TObjectList) на массив записей из ключа поиска и ссылки на объект, и искал теперь по этому массиву, не обращаясь при поиске непосредственно к объектам. Скорость программы выросла в десять раз.<br />В другой еще более часто вызываемой функции тоже нужный объект искался по нескольким ключам (Integer). Здесь замена трех независимых массивов на один массив записей увеличила скорость всей программы в пятьдесят раз. Причем пока массивов было два, программа работала не так уж медленно. <br /><br />Так что в некоторых случаях задержки при случайном доступе к памяти могут иметь существенное значение.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-88083761947885384072013-01-07T21:25:45.143+04:002013-01-07T21:25:45.143+04:00Хорошая статья. А я для определения узкого места и...Хорошая статья. А я для определения узкого места использую такой прием: если во время исполнения долгого кода нажать паузу то с высокой долей вероятности попадешь именно в тормозящий код, часто этого достаточно.Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-48301799280104660402013-01-06T23:22:12.745+04:002013-01-06T23:22:12.745+04:00Ну, в общем-то, смысл поста был не в том, что надо...Ну, в общем-то, смысл поста был не в том, что надо использовать именно AQTime, а в том, чтобы использовать хоть какие-то измерения. В частности, GetTickCount/QueryPerformanceCounter - это и есть примитивный профайлер.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-31314343481738756872013-01-06T22:41:13.421+04:002013-01-06T22:41:13.421+04:00Поддерживаю, я - за профайлер. Правда в простых сл...Поддерживаю, я - за профайлер. Правда в простых случаях, я частенько расставляю в контрольных точках банальный GetTickCount и вывод результатов в лог...Николай Зверевhttps://www.blogger.com/profile/08965247674233981930noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-4395129601654282042013-01-06T16:46:17.963+04:002013-01-06T16:46:17.963+04:00Довольно интересная тема, поделюсь своим небольшим...Довольно интересная тема, <a href="http://cooldelphi.blogspot.com/2013/01/blog-post.html" rel="nofollow">поделюсь своим небольшим примером</a><br /><br />Я использовал базовый AQTime в XE2, но позже понял, что сделать свой мини-профайлер не составит большого труда (конкретно под нужные цели).Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-30858034755517260052013-01-06T07:32:22.248+04:002013-01-06T07:32:22.248+04:00>А ты бесплатный AQTime использовал?
Платный и...>А ты бесплатный AQTime использовал? <br />Платный использовал. Но суть не в этом.<br /><br />А в том, что AQTime меня тогда как раз и показал, то, что функции Read/WritePrivateProfileString (точно не помню и мне сейчас лень смотреть какие там функции тогда были, поэтому предположу, что именно эти=)) съедают больше всего времени. И уже копая от этиъ функций в обратную сторону через дерево вызовов в том же AqTime я докопался до своего кода (точнее, там использовался чужой компонент (Ehlib-овский), который и использовал TIniFile).<br /><br />И если бы там был отключен анализ VCL-ных функций, я бы скорее всего даже не стал копать дальше, решив, что да, раз сохранение/загрузка размеров формы занимает столько времени, сколько оно занимает, а так как она выполняется всего один раз на окно, то ничего с этим и не поделать и ничего там особо не оптимизируешь.<br /><br />На поиски решения меня насторожил именно тот факт, что функции Read/WritePrivateProfileString выполнялись для каждой формы по несколько раз и занимали при этом довольно много времени.<br /><br />С тех пор, кстати, везде где нужна работа с ini-файлами, предпочитаю использовать TMemIniFile вместо TIniFile.Алексей Тимохинhttps://www.blogger.com/profile/11853041033911520876noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-14540491041876507722013-01-06T06:34:03.933+04:002013-01-06T06:34:03.933+04:00А ты бесплатный AQTime использовал? Pro-шка же пок...А ты бесплатный AQTime использовал? Pro-шка же показывает строки, и там сразу будет видно, что вызов такой-то строки (работа с ini) занимает много времени. Да если даже и бесплатную - если работа с ini в отдельной процедуре, то всё на неё укажет.<br /><br />Кстати, это пример оптимизации "оптом - дешевле". Read/WritePrivateProfileString в Win32/Win64 вынуждена на каждый вызов заново открывать файл, заново парсить его, заново искать значение. <a href="http://www.transl-gunsmoker.ru/2010/05/ini.html" rel="nofollow">Это было не так страшно в Win16</a>, где была вытесняющая многозадачность, и поэтому можно было кэшировать представление в памяти до момента передачи управления другой программе. Ну а TMemIniFile может один раз открыть и распарсить файл, а затем работать с представлением в памяти. Короче, типа BeginUpdate/EndUpdate получается.GunSmokerhttps://www.blogger.com/profile/15611696588191431330noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-38390105195382035912013-01-06T05:50:43.004+04:002013-01-06T05:50:43.004+04:00Кстати об AqTime и исключении VCL функций из профа...Кстати об AqTime и исключении VCL функций из профайлера. <br /><br />Как-то после очередного апдейта рабочей программы, пользователи начали жаловаться на то, что формы в программе стали открываться заметно медленнее. <br />Заметно для пользователей. На своем компьютере я особой разницы не замечал.<br /><br />Единстенное подозрение падало на функцию сохранения/загрузки размеров форм (и кое-каких других данных) в TIniFile. Как мне сначала показалось, это никак не могло заметно повлиять на производительность. Но всё же решил проверить профайлером. Оказалось, что больше всего времени в функции загрузки/сохранения потребляли именно WinApi функции чтения/записи в ini файл (TIniFile - это просто обёртка над стандатртными windows функциями для работы с Ini-файлом). Тут же захотелось сравнить, а не будет ли быстрее работать с TMemIniFile (реализация на чистом Delphi). Заменил код, прогнал профайлером. Оказалось, что да, TMemIniFile работает гораздо-гораздо быстрее. Выкатил новый апдейт. И, таки да, недовольные пользователи подтвердили, что формы стали открываться заметно быстрее.<br /><br />Не думаю, что такую оптимизацию удалось бы произвести отлюченив профайлер для VCL функций.<br /><br />Но это так, редкий случай.Алексей Тимохинhttps://www.blogger.com/profile/11853041033911520876noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-39327217937641317802013-01-06T05:34:24.035+04:002013-01-06T05:34:24.035+04:00Я раньше думал, что при включченной оптимизации, к...Я раньше думал, что при включченной оптимизации, компилятор самостоятельно выполняет оптимизации типа "вынесения общей переменной из цикла" или "замена сложного выражения локальной переменной". И лишь недавно узнал, что это было довольно наивно.<br /><br />Кстати, для обнаружения возможных мест для оптимизации, может помочь встроенная в IDE функция "Audits & Metrics". Как минимум, она умеет находить места, где можно упростить цикл и сделать код понятнее. Правда, для того, чтобы прочуствовать всю силу (и некоторую глючноть) этой функции, нужна Architect или Enterprise редакция Delphi.Алексей Тимохинhttps://www.blogger.com/profile/11853041033911520876noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-46462332449906429782013-01-06T05:24:58.939+04:002013-01-06T05:24:58.939+04:00Ссылка по теме:
Введение в технику оптимизации цик...Ссылка по теме:<br /><a href="http://habrahabr.ru/post/124910/" rel="nofollow">Введение в технику оптимизации циклов</a>Алексей Тимохинhttps://www.blogger.com/profile/11853041033911520876noreply@blogger.comtag:blogger.com,1999:blog-1702873441071265539.post-74756167256294554752013-01-05T15:08:21.825+04:002013-01-05T15:08:21.825+04:00Спасибо за статью. Когда-то писал мод, на Pawn, дл...Спасибо за статью. Когда-то писал мод, на <a href="http://ru.wikipedia.org/wiki/Pawn" rel="nofollow">Pawn</a>, для мультиплеера Gta - San Andreas, так вот каждый кто просил помощи на форуме сразу начинался срач, мол "ты нуб, зачем лезешь? ты не оптимизировал даже код". Каждый второй пользователь форума был болен этой оптимизацией. Часто мысли о преждевременной оптимизации мешают составлению основного алгоритма.Anonymousnoreply@blogger.com