|
|
|||||||||||||||||||||||||||||
|
WPF в Visual Studio 2010 - Часть 2 : Настройка производительностиИсточник: msvisualstudio2010
Эта публикация, вторая в серии статей об использовании WPF в Visual Studio 2010, охватывает ряд советов и приемов по оптимизации производительности приложений, написанных с использованием WPF, а также ряд настроек Visual Studio 2010, которые необходимо произвести, чтобы "выжать" максимум из WPF. ИнструментыОптимизация производительности начинается с измерений, и настройка WPF в Visual Studio не является исключением. Для измерения производительности в среде Visual Studio мы использовали следующие инструменты: The Visual Studio Profiler. Эта функция встроена непосредственно в Visual Studio. Очень удобно, что она всегда под рукой. У команды Profiler-a есть свой собственный блог: http://blogs.msdn.com/profiler/. Для более глубокого анализа событий и таймингов на уровне операционной системы, мы использовали xperf из пакета "Windows Performance Analysis Tools": http://msdn.microsoft.com/en-us/performance/cc825801.aspx (Скачайте последнюю версию Windows SDK и найдите ссылку "Установить Windows Performance Toolkit"). На самом деле, если бы мне пришлось выбирать единственный инструмент для анализа производительности, это был бы xperf. Неважно, где именно кроются проблемы производительности: в процессоре, диске, памяти или сети (или в их комбинации), им не спрятаться от этого инструмента. Также, в состав Windows Performance Toolkit входит комплекс "WPF Performance suite": http://msdn.microsoft.com/en-us/library/aa969767.aspx. Этот набор включает в себя утилиту "Visual Profiler", позволяющую получить подробное представление о процессах, происходящих внутри WPF-приложения. Дополнительную информацию можно получить, перейдя по ссылке http://windowsclient.net/wpf/perf/wpf-perf-tool.aspx. СоветыДля получения общего представления об оптимизации производительности WPF начните с этих тем в MSDN: http://msdn.microsoft.com/en-us/library/aa970683.aspx Изложенные ниже советы базируются на реальных проблемах производительности WPF, с которыми мы столкнулись в процессе разработки Visual Studio 2010. 1. Грамотное использование визуальных эффектов В инструменте "Perforator" из набора "WPF Performance suite" имеется так называемый "грязный" оверлей, который помогает отслеживать проблемы производительности Visual Studio при отображении теней. Добавление тени к элементу заставляет элемент "пачкаться" (и перерисовываться) полностью даже когда обновляется лишь небольшая часть этого элемента. Например, если добавить тень к панели инструментов, это заставит всю панель перерисовываться даже при изменении одной кнопки на этой панели. На одном из этапов разработки мы попробовали добавить тень ко всему контейнеру документа в Visual Studio, не понимая реальных последствий таких действий. С этого момента любой ввод текста в текстовом редакторе или движение курсора вызывало полную перерисовку всего контейнера документа. Даже мигание курсора в месте ввода текста вызывало "загрязнение" всей области ввода. Конечно, увидев это, мы убрали теневой эффект и производительность вернулась к норме. Из описанного выше следует вывод, что растровые эффекты, такие как DropShadowEffect, следует использовать очень умеренно и только для простых элементов. Эффект отбрасывания тени зачастую можно реализовать и другим способом - например, окружив элемент по периметру областями, заполненными градиентной заливкой и использовать градиент различной прозрачности для создания эффекта мягких теней. 2. Уменьшение количества IRT Инструмент "Perforator" из "WPF Performance suite" также выявил всплеск на графике "Hardware IRT" каждый раз, когда обновлялась панель инструментов. IRT (Intermediate Render Targets - промежуточные области отрисовки) используются при составлении конечного изображения в WPF. Каждая из них, не особо сложная сама по себе, все же требует выделения отдельной области памяти графического процессора и вносит вклад в общее время рендеринга. После нескольких экспериментов оказалось, что виновником оказался алгоритм отрисовки иконок для неактивных кнопок на панели инструментов. Для конвертирования иконки в режим градаций серого использовалась функцияFormatConvertedBitmap. Мы заменили алгоритм на аналогичную собственную процедуру для преобразования изображения в градации серого, оптимизированную для небольших иконок 16×16, которые мы используем на панели инструментов, тем самым полностью устранив IRT. 3. Ограничение сложности визуального дерева (Visual Tree) Одной из областей, которую мы не очень тщательно изучили, было сокращение глубины визуального дерева Visual Tree. Шаблоны стилей, особенно для элементов панели инструментов, являются весьма сложными. Визуализация элемента зависит от свойств в модели данных, например, видимый/невидимый, активен/неактивен, с иконкой/текстовый, и все это связано с поведением (property setters, установщиками свойств) в шаблонах. Даже простая на вид кнопка с иконкой содержит все границы, сетки и дочерние элементы, необходимые для обеспечения отрисовки элемента панели инструментов в самом общем виде, в котором это возможно. Это дает нам очень гибкую систему, но в результате приводит к чрезмерному усложнению визуального дерева. Мы могли бы заменить все это одним специальным элементом "кнопка панели инструментов", перенеся большинство стилей и поведение элемента из декларативной разметки (XAML) в код. Но, трудно точно предсказать, насколько это ускорит построение панели инструментов и уменьшит объем используемой памяти. 4. Оптимизация шаблонов стилей Одним из экспериментов, который мы все-таки провели, была попытка заменить все стили элементов панели инструментов простыми цветными прямоугольниками. Идея заключалась в том, чтобы посмотреть, сможем ли мы что-либо улучшить, заменив декларативные стили XAML императивным кодом C#. Этот эксперимент сам по себе показал кое-что интересное о применении стилей в WPF. Шаблон стиля анализируется и проверяется полностью, даже если элемент является невидимым. В шаблонах стилей видимость определяется с помощью установщика свойств (property setter), вызываемого через свойство IsVisible в модели данных элемента управления. Выглядит достаточно просто, но, что касается WPF, то тут свойство Visibility ничем не отличается от любого другого свойства. Анализируя результаты трассировки процедуры инициализации панели инструментов в xperf мы обнаружили, что применение шаблонов стилей к элементам управления, которые в конечном счете будут невидимыми, было значительной ошибкой и бессмысленным действием. Для исправления было достаточно добавить немного логики для элементов панели управления - всего лишь переместить установщик свойства Visibility и его триггер в код. Если выбран этот стиль, мы смотрим на свойство IsVisible и, если оно равно false, мы принудительно устанавливаем свойство Visibility в значение Collapsed и настраиваем listener на свойство IsVisible в модели данных. Когда свойство IsVisible изменяется на true, мы убираем listener и применяем оригинальный стиль. 5. Внесение массовых изменений через Application.ResourceDictionary В Visual Studio класс словаря ресурсов (ResourceDictionary) на уровне приложения настроен таким образом, что используется всеми компонентами приложения. В ресурсах хранятся цвета, кисти и стили для цветовой схемы, используемой в интерфейсе. Когда меняется цветовая схема операционной системы, например, при включении или выключении режима повышенной контрастности (HighContrast), эти ресурсы обновляются в соответствие с новыми цветами. Изначально, при смене ресурсов, происходила их последовательная смена, одного за другим, но это было невероятно медленно, поскольку каждое изменение вызвало волну уведомлений по всему визуальному дереву - почти каждый визуальный компонент реагировал на изменение в ресурсах и перестраивал свой стиль. Поэтому, решением стало создание нового, автономного ResourceDictionary с новыми цветами и, по окончании создания, переключение существующих ресурсов на этот новый набор за одну операцию. 6. Параметр availableSize метода MeasureOverride может использовать константу PositiveInfinity в качестве значения "не интересует" Если вы создаете элемент управления с нестандартным макетом, переопределяя MeasureOverride и ArrangeOverride, имейте в виду, что родительский элемент может передать константу Double.PositiveInfinity в качестве значения ширины или высоты (или того и другого) как указание, что его эти значения не интересуют. Это может быть сигналом для использования оптимизированного кода в Вашей вычислительной логике. Если ваша реализация предполагает вызов Measure для дочерних элементов, окажите любезность и им, передав PositiveInfinity в качестве значения. В качестве бонуса: класс TextBlocks в WPF оптимизирован таким образом, что если вы вызываете Measure более чем один раз с теми же размерами, то он вернет те же самые размеры, не делая сложных вычислений. 7. Исправление ошибок с путями в BindingExpression Если при отладке WPF-приложения Вы видите в окне вывода подобные ошибки:
то это, кроме очевидных проблем с привязкой данных (data binding), может свидетельствовать также и об имеющихся проблемах производительности. WPF пытается различными способами решить проблемы с путями, в том числе путем поиска прикрепленных свойств (attached properties), который является весьма ресурсоемким. Если избавиться от подобных предупреждений, ситуация станет намного проще. В Visual Studio 2010 появились новые возможности для отладки привязки данных в WPF. 8. Оптимизация сценариев Remote Desktop: Упрощение изображения При работе с удаленным рабочим столом (Remote Desktop) все содержимое WPF передается в виде растровой картинки. Это резко отличается от работы с GDI, где графические примитивы, такие как прямоугольники, или текст, передаются на сторону клиента и отрисовываются там. Способом повышения производительности удаленного рабочего стола является сведение к минимуму количества байт, передаваемых по сети. В протоколе удаленного рабочего стола уже реализован алгоритм сжатия данных, потому необходимо оптимизировать сцены WPF для сжатия. Простые сцены сжимаются гораздо лучше сложных. Для примера, сплошные цвета сжимаются лучше градиента или текстур. Для сценариев удаленного рабочего стола Visual Studio 2010 автоматически делает изображение проще путем отключения анимации и теней, а также используя сплошную заливку заднего фона. Получить дополнительные советы и справочную информацию Вы можете, прочитав блог Джозефа Гольдберга (Jossef Goldberg"s Blog). 9. Оптимизация сценариев Remote Desktop: Команды оптимизации скроллинга Еще один момент, касающийся работы с удаленным рабочим столом, где требовалась дополнительная поддержка WPF, был скроллинг в редакторе текста. Как я говорил ранее, при работе в удаленном сеансе, все содержимое WPF передается в виде растровой картинки. Когда текстовый редактор прокручивается на одну строку, вся область редактора передается повторно как растровая картинка. Это, понятное дело, весьма ресурсоемко - чем больше область редактирования текста, тем больше картинка и, соответственно, тем медленнее происходит обновление. К счастью, WPF 4.0 теперь умеет посылать команду "ScrollWindow" удаленному рабочему столу, что резко уменьшает объем передаваемой информации. То есть, сейчас должна быть передана только сама команда на прокрутку (очень короткая) и появившаяся при скроллинге строка. Чтобы воспользоваться преимуществами новой операции нужно использовать свойство VisualScrollableAreaClip. Существуют определенные ограничения на использование данной операции, так что, внимательно изучите документацию. 10. Оптимизация для виртуальных машин и устаревшего аппаратного обеспечения Как и при работе с удаленным рабочим столом, Visual Studio упрощает изображение при работе на виртуальной машине или устаревшем графическом устройстве. WPF сама определяет уровень рендеринга (rendering tier) , а мы просто добавили некоторые дополнительные эвристики для обнаружения виртуальной среды. При необходимости, изображение автоматически упрощается, как будто происходит работа с удаленным рабочим столом. Кроме этого, можно принудительно включить программный рендеринг с помощью появившегося в WPF 4.0 нового свойства RenderOptions.ProcessRenderMode. Во время тестирования мы обнаружили некоторые нетбуки, на которых даже при заявленной поддержке видеокартой уровня 2 (Tier 2), программный рендеринг был гораздо быстрее аппаратного. Для таких случаев автоматическое определение отключено, но пользователь всегда может изменить эти настройки на странице Tools/Options … Другие части Visual Studio, в том числе расширения, могут также реагировать на эту настройку либо путем проверки настройки "Visual Settings" непосредственно через новое свойство оболочки "VSSPROPID_VisualEffectsAllowed" или, в случае использования WPF, привязываясь к свойству "EnvironmentRenderCapabilities". Утечки памятиЗанимаясь всеми описанными выше улучшениями, легко было забыть про утечки памяти. Для анализа памяти, в частности, для выявления утечек, мы начали с VMMap. Этот инструмент позволяет определить, какой именно вид "утечки" памяти произошел: в изображениях (images), в кучах (managed heap и native heap), в отображаемых в памяти файлах (memory mapped files) или в выделяемой памяти (private bytes). Для простых утечек мы использовали UMDH. Для управляемых утечек использовалась комбинация CLRProfiler и расширения SOS для WinDbg. Методика описана уже многими авторами, включая Рико Мариани (Rico Mariani). Джозеф Голдберг из команды WPF сделал прекрасный отчет об отслеживании утечек памяти в WPF, который актуален сегодня так же, как и два года назад: http://blogs.msdn.com/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx. ЗаключениеВ Интернет имеется огромное количество информации об общих методах оптимизации производительности, но я надеюсь, эта статья осветила некоторые темы, которых Вы не найдете в другом месте. Я опустил несколько моментов, которые посчитал слишком специфическими для Visual Studio и не являющимися актуальными в других приложениях. Кроме того, 10 было приятным круглым количеством советов. Если в комментариях появится достаточная заинтересованность, я могу вернуться к вопросу производительности в следующих частях этой серии статей. Как всегда, пожалуйста, оставляйте свои комментарии и вопросы. Я постараюсь ответить на них или там же, в комментариях, или в будущих публикациях. Ссылки по теме
|
|