четверг, 15 сентября 2011 г.

PyWeek 13. Четвертый день конкурса.

Четвертый день конкурса PyWeek 13 был днем под названием «работа над ошибками», все дело в том, что проект пишется на двух различных машинах, и разница в скорости у них очень большая. Компьютер на работе – обычный двух ядерный ~2.8 GHz, 2 GB оперативной памяти и 64 Mb видео, мой же старый компьютер – одноядерный ~2.8 GHz, 512 Mb, 64 Mb. На работе тестовые примеры (которые я показывал в предыдущем посте), нагружают процессор на ~20%, я не знаю много ли это, но мой домашний компьютер нагружается на 100%. От этого дальнейшая разработка проекта кажется Адом. Домашний компьютер выдает 5-10 FPS вместо 60 FPS, и всего-то от 100 объектов, которые проецируются на 2D плоскость. Я понимаю, что Python не очень быстрый, но сортировка 100 объектов, и их проецирование плюс поворот в трехмерном пространстве, и так 60 раз в секунду – это ничто, и Python должен спокойно справится с этой проблемой, поэтому я думаю что я что-то накосячил, и надо было быстро с этим разобраться. В начале я рассчитывал что вся проблема в моей технике, да, она не нова, «четвертые пентиумы» давно канули в лету, но все же такая примитивная задача по зубам такой техники. У моего компьютера есть очень большая проблема, я несколько раз пользовался функцией «восстановление системы», в нем сгорела внутренняя сетевая карта, новая внешняя сетевая карта тоже сгорела от удара молнии, операционная система стоит уже ровно год. Я не знаю, могли ли эти обстоятельства повлиять на скорость работы техники, но видео на youtube я смотреть не могу, реагирование на «правую кнопку мыши» в «проводнике» имеет время отклика от 2  до 5 секунд. Списывать все на технику я не стал, ибо многие приложения так и работают нормально с момента установки системы, например FlashDevelop, игра «Казаки», а вот Flash Player вообще не работает так как надо, видео на youtube смотреть не возможно, одно слайдшоу, но мне все-таки придется оптимизировать проект, ибо разрабатывать дома я дальше его не смогу, и это как-то не профессионально, поэтому с чувством недоумения я занялся оптимизацией того что написал.
В начале я начал с округления результатов вращения в трехмерном пространстве, и результатов проекции на двухмерную поверхность, поначалу я думал, что после округления результат, все будет очень криво, но оказалось что все нормально, причем качественно. Далее я занялся структурой проекта. Структура PyGame следующая – есть один бесконечный цикл в котором должна крутится вся логика программы, но для того чтобы программа не зависла из-за бесконечного цикла, надо очищать стек событий, в целом структура главного цикла у меня была вот такая:
  • 1. Очистка буфера событий
  • 2. Прогон по массиву элементов
  • 2.1. Проецирование элемента на 2д плоскость
  • 2.2. Отрисовка элемента
  • 3. Обновление экрана
Вроде бы структура логичная, но все-же в этом цикле непонятно зачем вычисляется проецирование, тоесть если спираль ДНК не двигается и не вращается, а просто стоит на месте, то для всех ее элементов вычисляется проецирование, это плохо, поэтому я быстро вынес код проецирования за  цикл. Теперь структура стала следующей:
  • 1. Очистка буфера событий
  • 2. Если надо повернуть спираль, то поворачиваем и вычисляем проекцию и другие свойства
  • 3. Прогон по массиву элементов
  • 3.1. Отрисовка элемента
  • 4. Обновление экрана
Такая структура дала мне отвоевать 15 % нагрузки, 80% для отрисовки 100 элементов это много, и я занялся оптимизацией отрисовки. Отрисовка состояла из трех этапов:
  • Отрисовка соединения – банальная линия шириной в 5 px
  • Отрисовка элемента – банальный круг через pygame.draw.circle
  • Отлисовка обводки – опять же банальный круг через pygame.draw.circle, но уже без заливки.
В PyGame нет возможности нарисовать залитый цветом круг + обводку, поэтому мне пришлось использовать дважды pygame.draw.circle, я знаю, что так делать ненужно, но проект еще на стадии реализации, и я надеялся в то, что найду более подходящее решение позже. После отключения отрисовки соединения я отвоевал 10% нагрузки, с прескорбью я узнал о том, что чем толще линия, тем больше она есть ресурсов, после отключения отрисовки обводки – отвоевал еще 5%, после отключения отрисовки элемента, уровень нагрузки на процессор упал до 12-15%, но  экране ничего не отображалось, я посчитал, что это нормально, и при отключенной отрисовки я включил вычисление проекции и поворота, вот тут я и офигел, уровень нагрузки опять повысился до 100%, теперь пришло время оптимизации вот эти вычислений. Начал я с простого – удаление всех строк с константным определением «кортежей», позже, проведя несколько тестов, я понял, что «списки» работают быстрее «кортежей», и заменил «кортежи» «списками». Так же рассчитал заранее синусы и косинусы нужных направлений поворотов, покаместь их два, но я где то читал что создатели «Doom» тоже рассчитывали заранее все значения тригонометрических функций, видимо я тоже так поступлю. В итоге, потратив один день, я все же смог сбросить количество нагрузки до 50%, но при долгом вращении уровень нагрузки подымается опять на 100%, на лицо потеря памяти, но где – это еще придется найти.
Ну и в заключение я скажу, что рассчитывал на большую мощность PyGame и Python, интересно то, что на рабочем компьютере, на котором открыт Adobe Flash, FlashDevelop, Adobe Photoshop и локальный сокет-сервер SmartFox написанный на Java, все работает на ура, и количество нагрузки в пике - ~20. Почему так? Я не знаю, надеюсь в то, что все дело в «убитой» операционной системе моего компьютера, на выходных я попробую заняться перестановкой системы, снесу все, отформатирую диск, установлю все по новой и посмотрю в чем дело, в моих руках или в самой технике.
Ну, вот и все. Всем удачи. Пока.