К. Касперски - Техника оптимизации программ, Эффективное использование памяти (1127752), страница 8
Текст из файла (страница 8)
Напротив! В программном коде такое встречается сплошь и рядом. Ь~фР~ф~ ффЕФ~О)~ЦТР~Щ11И';йРД~",'.ГГРсРТОРЫВ Уиае ~~1~йффРГ)ФффЩфф1)тянтйО 1)~~~3~-,3Ф~~~~~~РРВ~~;:='Ъ'."':1':::..'':::::'.::::.:,!:;::г нбг1енс ~рек*~И»'гп УУ О данный ннкя прсгсняется пссфнякрсвенксн 1.ВСО раз рвнб 00 = '!'; /У О данная инсгтукння звсгсняееся всего 11 Ваз Поэтому, обнаружив "горячую" точку в первую очередь убедитесь, что количество ее прогонов достаточно велико. В противном случае полученный результат с большой степенью вероятности окажется недостоверным. И тут мы плавно переходим к обсуждению подсчета числа вызовов каждой точки программы.
Впрочем нет, постойте. Нам еше предстоит разобраться со второй "горячей" точкой и на удивление медленной скоростью загрузки указателя в Опытные программисты, вероятно, уже догадались, в чем тут пело. Действительно, — строка р б~р~ = . — это первая строка тела цикла, получающая управление каждые Ох59 итераций, что намного превосходит "проницательность" динамического алгоритма предсказания ветвлений, используемого процессором для предотвращения остановки вычислительного конвейера. Следовательно, данное ветвление всегда предсказывается ошибочно и выполнение этой инструкции процессору приходится начинагь с нуля, А процессорный конвейер — длинный. Пока он заполниться...
Собственно, тут ВИНОВата ВОВСЕ НЕ КОМВНДВ еся ебк, РВОНО Ртя ~еьр~зс10 — ЛЮбаЯ ДРУГаЯ КО- манда на ее месте исполнялась бы столь же непроизволительно! "Паяльная гв Глава 1 горелка, до красна нагревающая" эту точку программы, находится совсем в другом месте! Поднимем курсор чуть выше, на инструкцию условного перехода, предше- ствующую этой команде, и дважды щелкнем мышкой. Профилировщик 'РТппе выдаст следующую информацию: Оесобег Мгпьицм С1осхя = 0 тактов Минимальное время деколирования — 0 Ресобег йчегаде С1осИв = 0 тактов Эбфектнвное время декодирования — 0 Оесобег Мак1ьзла С1осИа =. 4 такта Максимальное время декодирования — 4 йес1геиепс йчегаде С1осхз = 1 Эффективное время завершения — 1 такт Топа1 Сус1ва = 1011 (08,20%) (8,2$) Всего времени исполнения — 1010 тактов М1сго-Орз Гог ГЬгз 1пзпгпсптоп = 1 Кол-во микроопераций в инструкции— тЬе гпвтп1спгоп Ьаб Го нагл (8, 11.
1, 113) сус1еа Гог гг'з зопгсеа го Ье геабу ("Эта инструкция ждала минимально 8, максимально 113, а в основном 11,1 тактов пока ее операнды не были готовы") бупввбс РепаЗ.пу: Втв мгее Реваз.пу ; Верят типа ВТВ М'аа Ре аггу Таба 1пвпгцспгоп зпа11а Ьесапве ГЬе Ьгапсп иаз ппзргебгстеб. ("Эта инструкция простаивала потому, что ветвление не было предсказано") Сссцгапсев = 13 Такое случалось 13 раз Наша гипотеза полностью подтверждается. Это ветвление тринадцать раз предсказывалось неправильно, о чем РТцпе и свидетельствует! Постой, как тринадцать?! Ведь тело цикла исполняется только Одиннадцать! Да, правильно, одиннадцать. Но ведь процессор наперед этого не знал и лважды пытался передать на него управление, и лишь затем, "увидев", что ни одно из двух предсказаний не сбылось, "плюнул и поклялся", что никогда-никогда не поставит свои фишки на эту ветку.
ОК. Когда загадки. разрешаются — это приятно. Но главный вопрос несколько в другом: как именно их разрешать? Хорошо, что в нашем случае непредсказуемый условный переход находился так близко к "горячей" точке, но ведь в некоторых (и не таких уж редких) случаях "виновник" бывает расположен даже в других модулях программы! Ну, что на это сказать... 11одходите к профилировке комплексно и всегда думайте головой. Пожалуй, ничего более действенного я не смогу посоветовать.
Лрофилировка программ Определение количества вызовов Как мы только что показали, определение количества вызовов профилируемой точки необходимо уже хотя бы для того, чтобы мы могли убедиться в достоверности изменений. К тому же, оценивать температуру точки можно не только по времени ее выполнения, но и частоте вызова. Например, пусть у нас есть две "горячие" точки, в которых процессор проводит одинаковое время, но первая из них вызывается сто раз, а вторая— сто тысяч раз.
Нетрудно догадаться, что, оптимизировав последнюю хотя бы на 1%, мы получим колоссальный выигрыш в производительности, в то время как, сократив время выполнения первой из них вдвое, мы ускорим кашу программу всего лишь на четверть, Таким образом, часто вызываемые функции в большинстве случаев имеет смысл "инлайнить" (от английского Гп-йпе), т. е. непосредственно вставить их код в тело вызываемых функций, что сэкономит какое-то количество времени. Определять количество вызовов умеют практически все профилировщики, и туг нет никаких проблем, заслуживаюших нацгего внимания.
Определение степени покрытия Вообгце-то говоря, определение степени покрытия не имеет никакого отношения к оптимизации приложений, и это побочная функция профилировшиков. Но, поскольку она все-таки есть, мораль обязывает автора рассмотреть ее, хоть и кратко. Итак, покрытие — это процент реально выполненного кода программы в процессе его профилировки. Кому нужна такая информация? Ну, в первую очередь, тестерам', — должны же они убедиться, что весь код приложения протестирован целиком и в нем не осталось никаких "темных" мест. С другой стороны, оптимизируя программу, очень важно знать, какие именно ее части были профилированы, а какие нет.
В противном случае многих "горячих" точек можно просто не заметить только потому, что соответствующие им ветки программы вообще ни разу не получили управления! Рассмотрим, например, как может выглядеть протокол покрытия функций, сгенерированный профилировшиком ргой1е.ехе для нашего тестового примера рзччд.ехе (о самом тестовом примере см. разд, "Практический сеанс прафилиравки с )'Типе" этой главы) (листинг 1.7). ' Вега-тестерам — людям, занимаюшимся проверкой работоспособности программы и поиском ошибок.
Глава Г Ргодгаи Вгасгвсгся Статистика по програкме Совгвапб 1гпе аг 2002 йая 20 03;36: ряиб Са 1 берсп: 2 команлнал строка глубина вызовов: 2 Тота1 тцпссгопяс 5 Гцпстзоп соуегаде: 60,0$ всего функций: 5 покры.о Функцийс 60$ Мобц1е Бтап1ясгсв Гог рвиб.еке статистика по молулп рвиб Рцпсвтопя 1п вобц1ес 5 Функций в молулес 5 Функций покрыто: 605 Мобц1е тцгсв1оп соуегачес 60,0Ъ Сочегеб Рцпсс1оп гсокрытые Функции Ьесгурс (рвнс(.
оьз) геа1в)йв008 а00000000000000 (рвиб.оЬС) оеп ркиб (рвиб.аЬ3) иа1г. (рвнс(.оо)) ргтпс бов (рвиб,оЬ3) Из листинга 1.7 следует, что лишь 60% функций получили управление, а остальные 40% не были вызваны ни разу! Разумно убедиться: а вызываются ли эти функции когда-нибудь вообше или представляю~ собой "мертвый" код, который можно безболезненно удалить из программы, практически на половину уменьшив ее в размерах? Если же эти функции при каких-то определенных обстоятельствах все же получают управление, нам необходимо проанализировать исходный код, чтобы разобраться; что же это за обстоятельства и воссоздать их, чтобы профилировшик смог пройти и остальные участки программы. Имена по- КРЫТЫХ И НЕПОКРЫТЫХ фУНКЦИй ПЕРЕЧИСЛЕНЫ В СЕКЦИИ С б Р .
Г. оп. Покрытые отмечаются знаком "*", а непокрытые — "." Вообше же, для определения степени покрытия сушествует множество узко- специализированных приложений (например, ХММейа Соде Сочегаде), из- начально направленных на решение именно этой задачи, и со своей работой они справляются намного лучше любого профилировшика. ~Л)ИЕтйВГ';.'45У:.',При МЕрисрвтО~Ма4йфйаЫстйй "фуйнцу)йт~ааИВРИрОВВИНЫг"'Ыаз~)~; ~",:,'!:",", ".,'.'я -"фррфй~ирййщйксвга":.'рщтцат~~р! ":.,'в!;.в)~".,~: ' ' '-: ",:;,',;.-,:!!~,';-'-;)(.'-.„':,'~;"",,':::;:„';„":: -:;:,";~~~;:;,( Профипировка программ Фундаментальные проблемы профилировки вв малом" Профилировкой "в малом" мы будем называть измерение времени выполнения небольших фрагментов программы, а то и отдельных машинных команд. Профилировке в малом присущ ряд серьезных и практически неустранимых проблем, незнание которых зачастую приводит к грубым ошибкам интерпретации результата профилировки и как следствие — впустую потраченному времени и гораздо худшему качеству оптимизации.
Конвейеризация или пропускная способность чв.' латентность Начнем с того, что в конвейерных системах такого понятия, как "время выполнения одной команды" просто неги. Уместно провести такую аналогию. Допустим, некоторый приборостроительный завод выпускает шестьсот микросхем памяти в час. Ответьте: сколько времени занимает производство одной микросхемы? Шесть секунд? Ну конечно же нет! Полный технологический цикл составляет не секунды и даже не дни, а месяцы! Мы не замечаем этого лишь благодаря конвейеризации производства, т. е.
разбиении его на отдельные стадии, через которые в каждый момент времени проходит, по крайней мере, одна микросхема. Количество продукции, сходящей с конвейера в единицу времени, называют его пропускной способноппыо. Легко показать, что пропускная способность в общем случае обратно пропорциональна длительности одной стадии, — действительно, чем короче каждая стадия, тем чаще продукция сходит с конвейера.
При этом количество самих стадий (попросту говоря, длина конвейера) не играет абсолютно никакой роли. Кстати, обратите внимание, что практически на всех заводах каждая стадия представляет собой элементарную операцию — вроде "накинуть ключ на гайку" или "стукнуть молотком". И не только потому, что человек лучше приспосабливается к однообразной монотонной работе (наоборот, он, в отличие от автоматов, ее терпеть не может!).