К. Касперски - Техника оптимизации программ, Эффективное использование памяти (1127752), страница 15
Текст из файла (страница 15)
Ь> ООВа1ОбО Е р б.ооо ОбаО1ОВО Г рвыб.оЬЗ ООО1:ОООООООО ООО1:ООООООЗО ООО1:ООООООВО оесгург Са1со1аьеСКС Сьесгскс Откомпилировал этот пример с максимальной оптимизацией, запустим его иа выполнение, чтобы убедиться, насколько хорошо справился со своей работой машинный оптимизатор. Прогон программы на Р-111 733 даст скорость перебора всего лишь порядка 30в)ысяч поролей в секунду! Да это меньше, чем совсем ничего, и такими темпами зашифрованный текст будет "ломаться" ну очень долго! Куда же уходят такты процессора? Для поиска узких мест программы мы воспользуемся профилировщиком 1п(е) "(/Топе. Запустим его (не забывая, что под %(п((о нз 2000//ХТ он требует для своей работы привилегий администратора) и, тем временем пока компьютер деловито шуршит винчестером, создадим п)аблииу символов (не путать с отладочной информацией!), без которой профилировщик ни за что не сможет определить, какая часть исполняемого кода к какой функции относится.
Для создания таблицы символов в командной строке компоновщика (линкеРа) достаточно Указать ключ /р е 1 . напРимеР, это может выглЯдеть таК; 1гпд /ргобг1е рвыб.оЬ1. ЕСЛИ ВСЕ СЛЕЛаНО ПраВИЛЬНО, ОбраэустСЛ файЛ рз(у((.пзар приблизительно следующего содержания: 60 Глава ! Ага, УТппе уже готов к работе и терпеливо жлет наших дальнейших указаний, предлагая либо открыть с)ииествуюи1ий проект — "Ореп Ех!ц!пй Рго1есС (но у нас нечего пока открывать), либо вызывать Мастера для создания нового проекта — ")Чеч Рго|ес! '1т1хаггГ (вот это, в принципе, нам подходит, но сумеем ли мы разобраться в настойках Мастера?), либо же выполнить быстрый анализ производительности приложения — "Оп!ей Рег(оппапсе Апа(узез",— выбираем последнее! В появившемся диалогом окне указываем путь к файлу рва.ехе и нажимаем кнопку СО (то есть Иди).
'хТппе автоматически запускает профилируемое приложение и начинает собирать информацию о времени его выполнения в каждой точке программы, сопровождая этот процесс симпатичной змейкой-индикатором. Если нам повезет, и мы не "зависнем", то через секунду-другую 1гТппе распахнет себя на весь экран и выведет несколько окон с полезной и не очень информацией. Рассмотрим их поближе (рис.
1.6). В левой части экрана находится Навигатор Проекта, позволяющий быстро перемешаться между различными его частями. Нам он пока не нужен и потому сосредоточим все свое внимание в центр экрана, где расположены окна диаграмм. Верхнее окно показывает, сколько времени выполнялась каждая точка кода, позволяя тем самым обнаружить "горячие" точки (Но! Кроа), т. е. те участки программы, па выполнение которых уходит наиболыдее количество времени. В данном случае профилировщик обнаружил !87 "горячих" точек, о чем и уведомил нас в правой части окна.
Обратите внимание на два пика, расположенных чуть левее середины центра экрана. Это не просто "горячие", а прямо-таки "адски раскаленные" точечки, съедающие львиную долю быстродействия программы, и именно с их оптимизации и надо начинать! Подведем курсор к самому высокому пику — УТппе тут же сообщит, что оно принадлежит функции аиы Постой! Какой с?! Мы ничего такого не вызывали! Кто же вызвал зту "нехорошую" функцию? (Несомненно, вы уже догадались, что зто сделала функция р~ьььг, но давайте притворимся, будто бы мы ничего не знаем, ведь в других случаях найти виновника не так просто).
Чтобы не рыскать без толку по всему коду, воспользуемся другим инструментом профилировщика — Свй авгаря, позволяющим в удобной для человека форме отобразить на экране иерархическую взаимосвязь различных функций (или классов, если вы пишите на С +-Р) (рис. !.7). В меню Квп выбираем пункт %!в32* Свй Старя Ргой!!пв Яевв!оп и вновь идем перекурить, пока 1гТцпе профилирует приложение. По завершению профилировки на экране появятся еще два окна.
Верхнее, содержащее электронную таблицу, мы рассматривать не будем (оно понятно и без слов), а вот к нижнему присмотримся повнимательнее. Пастельно-желтый фон украшают всего два ядовито-красных прямоугольника с надписями "Т!згеаг) 400" и "пзагппСКТбгапцр". Щелкнем по последнему из них два раза— Профилировкв программ профилировщик УТцпе тут же выбросит целый веер дочерних функций, вызываемых стартовым кодом приложения. Находим среди них (что будет очень просто, т. к.
только л выделен красным цветом) и щелкаем по нему еще раз. И будем действовать так до тех пор, пока не раскроем все дочерние функции процедуры и ~ГтГет ° ."'1~К~';ккв ~1~„,"~~гг Рис. 1.6. Содержимое окон Утесе сразу же после анализа приложения. В первую очередь нас интересует верхнее окно, "картографирующее" "горячие" точки, расположенные согласно их адресам. Нижнее окно содержит информацию об относи- тельном времени выполнения всех модулей системы. Обратите внимание, модуль рввб.ехе (на диаграмме он отмечен стрелкой) занял далеко не первое место и основную долю производительности "съел" кто-то другой.
Создается обманчивое впечатле- ние, что оптимизировать модуль равд.ехе бессмысленно, но зто не так... В результате выяснится, что функцию т действительно вызывает функция ртгпьт, а саму рттптт вызывает ос рзчо. Ну, да! Теперь мы "вспомнили", что использовали ее для вывода текущего тестируемого пароля на экран! Какая глупая идея! Вот оказывается куда ушла вся производительность! Глава 1 Рис.
1.7. Иерархия "горячих" функций, построенная Мастером Саа 6гари. Цвет символизирует "температуру" функции, а стоящее возле нее число указывает, сколько именно она вызвалась раз Шаг первый. Удаление функции рппгг Конечно, полностью отказываться от вывода текущего состояния программы — глупо (пользователю ведь интересно знать, сколько паролей уже перебрано, и потом надо ведь как-то контролировать машину — не "зависла" ли?), но можно ведь отображать не каждый перебираемый пароль, а, скажем, каждый шестисотый, а еше лучше — каждый шеститысячный. При ЭТОМ НаКЛадНЫЕ раСХОдЫ На ВЫЗОВ фуНКцИИ ргглгт ЗНаЧИтЕЛЬНО упадут, а тО и вовсе приблизятся к нулю.
Давайте перепишем фрагмент, ответственный за вывод текущего состояния, следующим образом (листинг 1.15). вхатзс 1пт х=с; /( вывод текущего состояния на терминал хб ]+ах>бббб] х=с; ртзпоб] "Спггепс рвнб: Ъ10в [Вбвб]1т",арвнб10],ргочтевв]; Батюшки мои! После перекомпиляции мы получаем скорость иереооуи спите полутора миллионов паролей в секунду] То есть скорость программы возросла более чем в пять раз! Программа выполняется так быстро, что "ЧУВСтВИтЕЛЬНОСтИ" ФУНКЦИИ с1 К УжЕ ОКаЗЫВаЕтСЯ НЕДОСтатОЧНО ДЛЯ ИЗМЕ- Прафилиравка программ бЗ рений и количество итераций приходится увеличивать раз в сто! И это, как мы убедимся в самом недалеком будущем, еше, отнюдь, не предел быстродействия! Шаг второй.
Вынос функции аИег! за тело цикла Повторный запуск "обновленной" программы под профилировщиком показывает, что количество "горячих" точек в ней уменьшилось с 187 до 10б. Конечно, это хорошо, но ведь "горячие" точки все еше есть! Щелкнув кнопкой мыши в области У!етчв, расположенной в правом верхнем углу диалогового окна Но(Броса 1п шос!п)е по переключателю Но(вро!в Ьу Рппсаоп (Сортировать "горячие" точки по функциями), мы узнаем, что 80% времени наша программа проводит в недрах функции сьт гвсе сас, затем с большим отрывом следует дел рь с — 12% и по 3%делятфункции сь х скс и аь р ш Ну, это никуда не годится! Какая-то там жалкая функция сьт гьсе сас без зазрения совести поглощает практически все быстродействие программы! Эх, вот бы еше узнать, какая именна часлгь функции в наибольшей степени влияет на производительность. И профилировщик чТцпе позволяет это сделать! Дважды щелкнем по красному прямоугольнику, чтобы увеличить его на весь экран.
Оказывается, внутри функции сьг сьсь сас насчитывается 18 "горячих" точек, три их которых наиболее "горячи" — 30%, 25% и 10% соответственно (рис. 1.8). Вот с первой из них мы и начнем. Дважды щелкнем по самому высокому из прямоугольников, и профилировщик Усцпе, обиженно пискнув, сообщит, что "!ч(о зоцгсе Гоцпд Гог оГТзес Охб9 1псо Р:с,.ОРТ!М!УЕ~згс~РгоГс!срзсчс1.ехе. Ргосеес1 сч11)с д1заззепзЫу оп1у?" (твсхадные «секслсы не найдены. Прадалжансь с отображением валька дизассемблернага «секста?9 Действительно, поскольку программа откомпилирована без отладочной информации, то профилировщик ч"Топе не может знать, какой байт ассемблерного когда какой строке соответствует, а компилятор не соглашается предоставить эту информацию в силу того, что в оптимизированной программе соответствие между исходным текстом и сгенерированным машинным кодом, в общем-то, не столь однозначно.
Конечно, можно профилировать и неоптимизированную программу, но какой в этом резон? Ведь это будет другая программа и с другими "горячими" точками! В любом случае, качественная оптимизация без знаний ассемблера невозможна, поэтому, прогнав все страхи прочь, смело нажмем на кнопку ОК, т. е.
"Да, мы соглашаемся работать без исходных текстов непосредственно с ассемблерным кодом". Профилировщик хсТцпе тут же "тыкает нас носом" в инструкцию авена зслвв. Не нужно быть провидцем, чтобы распознать в ней ядро функции ьс~1 . Использовали ли мы функцию . сст в исходном тексте программы? А то как же! Смотрим листинг 1.1б. Глава ( Рис. т.8. Распределение "температуры" внутри Функции сатсптате слс (снимок сделан с высоким разрешением) )Е~" (в))мпвВЕввв,"., (и(.",мпН:.„:; ( к'.„„е,!4вт(ЕМ::~ тпт Са1сп1атеСКС(спаг *рвиа) тпт а; ппт х = -1( О описка вычисления СРС тат (а =.