Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
KG / КГ_8глава.doc
Скачиваний:
99
Добавлен:
26.05.2014
Размер:
586.75 Кб
Скачать

8.1. Анализ и оптимизация программы

Каждую программу можно усовершенствовать. Можно попробовать умень­шить текст программы, уменьшить размер выполняемого файла, улучшить структурированность, модульность и так далее. В данном случае мы попыта­емся повысить скорость рендеринга — уменьшить время формирования кад­ров изображения.

Как оптимизировать программу по быстродействию? Для этого необходимо выполнить анализ работы программы. В результате анализа нужно обнару-

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

Для измерения времени выполнения операций в программе для Window можно воспользоваться функцией API GetLocalTime :

Необходимо предупредить, что миллисекунды измеряются не очень точно, поэтому для повышения точности измерения для некоторой отдельной опе­рации можно делать цикл из многих (сотен, тысяч, ...) одинаковых операций (если вспомогательные операции создания цикла сами по себе не длитель­ные). Кроме того, различные сеансы измерений могут давать различные зна­чения, поэтому необходимо как-то усреднять результаты. Понятно, что все] измерения должны выполняться на одном и том же компьютере и обязатель-; но в одинаковых условиях выполнения программы. Также необходимо учи- ? тывать, что в полночь измерение времени может дать ошибку, — если пере­ход на 0 часов случится в ходе измерений. Впрочем, я и не рекомендую вам по ночам засиживаться за компьютером — ночью надо спать.

Теперь приступим к анализу программы studex34. Вся работа по созданию объектов, их отображение в различных ракурсах и уничтожение объектов делается в теле функции DrawstudyExampie. Сделаем измерения времени ос-

новных операций. На создание объектов, открытие контекста, подготовку битмапа двойного буфера и создание Z-буфера расходуется менее десяти миллисекунд (измерения с точностью до процентов секунд дают 0.00). Таким образом, в ходе дальнейшего анализа сосредоточимся на цикле создания 361 кадра.

Как измерить время, расходуемое во всех 361 кадрах на выполнение функции ciearMyZbuf f er () ? Это сделаем способом, который можно назвать "способом контрольно-измерительного стенда". Такой "стенд" можно сделать на основе текста нашей программы, например, следующим образом:

//далее вычисляем разность в секундах и выводим результат

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

Время выполнения 361 операции ciearMyZbuffer составляет в среднем 2.1 секунды. Аналогично можно сделать измерения для PatBit— 0.06 сек., SetCameraviewMatrix — 0.00 сек., BitBit — 0.5 сек. Однако делать измерения времени для цикла отображения объектов таким "стендовым" способом нель­зя. Для корректного создания изображения обязательно выполнение всех подготовительных операций в полном объеме. Для измерений времени здесь можно предложить другой способ. Суть его такова. Вначале измеряем время выполнения полного цикла создания изображений:

5

i

А потом исключаем анализируемую операцию:

и измеряем время выполнения без нее. Полный цикл — 807 сек., без исклю­ченной операции — 2.6 сек. Назовем такой способ "временным исключени­ем". Необходимо заметить, что цифру 2.6 можно было бы получить и иначе, если от времени полного цикла вычесть уже измеренное время других опера­ций. Однако способ "временного исключения" предназначен в первую оче-j редь для тех случаев, когда измерение всех составных операций затруднено или не нужно. Продолжим измерения дальше.

Как мы видим, в цикле создания кадров на подготовительные операции рас­ходуется мало времени в сравнении с отображением объектов. В ходе ото­бражения объектов выполняется много операций. Какая из них самая дли­тельная? Осуществим поиск способом "временного исключения". Однако для применения этого способа есть много ограничений. Сформулируем основные условия корректного использования данного способа.

1. Временно исключить можно не любую операцию, а только ту, отсутствие которой не нарушает логику работы программы. Другие операции должны выполняться в полном объеме и в той же последовательности. Это главное условие. Остальные условия можно сформулировать как следствие.

2. Необходимо быть внимательными, если вы работаете с оптимизирующим компилятором. Он может сделать такие изменения в программе во время компиляции, о которых мы и не подозреваем. Мы можем исключить одну операцию — а компилятор исключит еще несколько и никак нам об этом не сообщит. Это приведет к иллюзии значительной роли временно исклю­ченной операции.

3. Нельзя изменять стратегию использования виртуальной памяти (если это специально не анализируется). Например, в функции DrawstudyExampie нами не используется ни одна из файловых операций. Однако в ходе вы­полнения этой функции, программа может часто обращаться к диску. Это может быть в случаях, когда открываются значительные по объему масси­вы, и операционная система делает перераспределение виртуальной памя­ти между оперативной памятью (RAM) и диском. А в данное время мы временно выключили эту операцию, и обращения к диску прекратились — это может быть свидетельством того, что отныне все массивы целиком размещаются в RAM. Последнее может привести к ускорению выполне­ния программы, поскольку обращение к RAM осуществляется намного быстрее, чем к диску. Кроме того, когда размера RAM недостаточно для полной программы, то даже уменьшение объема кода при исключении может привести к тому, что программа будет работать быстрее — так как отныне все размещается в RAM. Однако в этом случае уменьшения вре­мени не пропорционально времени выполнения исключенной функции, и это не позволит рассчитать ее вклад в общее время выполнения програм­мы. Таким образом, необходимо пользоваться правилом: объем оператив­ной памяти должен быть достаточным как для полной программы, так и программы с исключением.

Способ временного исключения отдельных операций можно трактовать так: если после исключения некоторой операции время выполнения про­граммы уменьшается не существенно, то эту операцию не нужно опти­мизировать в первую очередь. Однако если в результате анализа мы и об­наружим некоторую длительную операцию, то это еще не означает, что ее можно ускорить.

Продолжим дальше анализ программы. Временно выключить функцию MyPoiygon нельзя, поскольку тогда будет рисоваться только шар, а это озна­чает другой порядок заполнения пикселами Z-буфера и растра битмапа двой­ного буфера. По аналогичной причине нельзя временно выключить функцию

Sphere: : Draw (HDC hdc). А ЧТО же ТОГДа МОЖНО?

Рассмотрим функцию SetPixMyz. Если ее исключить, то прекратится запись пикселов в оба растра — Z-буфера и растра битмапа. Однако те функции, ко-

торые остались, выполняются так же, как и до исключения. Временно ш ключим setPixMyz. Результат— 57.5. То есть, из 807 секунд почти все врем расходуется на запись пикселов.

Составная часть функции SetPixMyz — вызовы функции SetPixel. Времен^ исключим ее. Результат— 70 секунд. Результаты временных исключен^ отобразим следующей диаграммой (рис. 8.3).

Рис. 8.3. Диаграмма исключения операций

По диаграмме исключения можно рассчитать, сколько времени расходуется» программе на выполнение отдельных операций:

Время выполнения функции SetPixel:

Необходимо учитывать, что функция SetPixel — вложенная по отношению |

К SetPixMyz.

поэтому целесообразно будет для анализа SetPixMyZ рассматривать долю времени, не относящегося к вызову setPixei:

Таким образом, мы уже можем рассмотреть результаты измерений для неко­торых отдельных операций:

Этот перечень неполон, можно анализировать еще некоторые функции, од­нако уже ясно, что основная причина низкой скорости — это использование функции setpixei. Ее мы никак не можем изменить, ибо это функция API Windows. Но можно попробовать обойтись без нее.

Один из способов работы с растром — непосредственный доступ к памяти, хранящей растровый массив. Такой способ достаточно известен. Он исполь­зовался при разработке почти всех быстрых графических программ для ко­гда-то популярной операционной среды MS-DOS. Среди функций MS-DOS предусмотрена функция рисования пиксела на экране, но она работала так же медленно, как и функция API Windows SetPixei. Поэтому для создания изо­бражений на экране часто использовались операции непосредственной запи­си в видеопамять. В операционной системе Windows обращение прикладных программ к видеопамяти запрещено, а вся растровая графика основывается на понятии контекста графического устройства. Через контекст мы рисуем на экране, через контекст— в растрах битмапов. Кажется, это и все. Однако разработчики Windows предусмотрели еще одну возможность работы с рас­трами — операции с растрами в формате DIB (Device Independent Bitmap).

Можно создавать растр, не привязанный к какому-то графическому устрой­ству по формату пикселов, а самое главное — предусмотрен доступ к растру по указателю на массив в памяти. С этим массивом можно производить лю­бые операции, поскольку становится известным его адрес в виртуальной па­мяти. В том числе и выполнять операции над отдельными байтами и битами,; которые представляют пикселы растра. Это открывает широкие возможности для создания собственных графических библиотек. Текст программы studex35. срр:

В программе использована одна из функций API Windows для работы с рас­трами DIB. Это функция stretchDiBits, которая осуществляет вывод растра из памяти по адресу pRastBuf в определенный контекст. Растровый буфер DIB здесь в формате 24-битного цвета— это позволяет достаточно просто моделировать различные интенсивности отражения света для цветных объек­тов. Для корректности сравнения работы обеих программ (studex34,35) все испытания следует производить в видеорежиме True Color 24 бит на пиксел.

В 24-битном режиме цвет каждого пиксела определяется тройкой байтов RGB. Как раз эти байты и записываются в память функцией setPixRastrMem.

Скомпилируйте и проверьте работу программы studex35. Полный оборот ка­меры в ней делается за 88 секунд в отличие от studex34, где полный оборот составлял 807 секунды. Таким образом, создание одного кадра выполняется в среднем за 88/361 = 0.24 секунды. Благодаря замене функции SetPixel на­шей собственной функцией SetPixRastrMem достигнуто уменьшение времени рендеринга более, чем в 9 раз. И это несмотря на то, что функция stretchDiBits работает медленнее, чем BitBit.

Необходимо отметить, что функция SetPixRastrMem предназначена только для 24-битных растров, a SetPixel корректно работает во всех цветовых форматах, поэтому она и работает медленнее.

Соседние файлы в папке KG