Почему в моем приложении WPF GetMessageW заняло значительное использование процессора?

У меня здесь серьезный скребок головы. Я исследую проблемы производительности с компонентом WPF в нашем приложении.

Наше приложение.net очень большое и почти полностью в форме Windows. В рамках новой инициативы мы переписали один из наших основных компонентов с богатым пользовательским интерфейсом WPF. Существует множество взаимодействий WinForms<->WPF с этой штукой, чтобы связать их вместе, и я подозреваю, что это может быть как-то связано с тем, что я вижу.

Когда я профилирую медленную операцию в профилировщике ANTS, я вижу много действий, происходящих внутри функции UnsafeNativeMethods.IntGetMessageW. ANTS сообщает о той же активности процессора, что и все наши бизнес-логики и рендеринг wpf вместе взятых. Нет управляемого кода той функции, которая использует циклы, поэтому, что бы ни делал IntGetMessageW, это то, что мне нужно.

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

Наш рассматриваемый здесь WPF-компонент написан наследующим от Window (т. Е. Это не просто элемент управления / usercontrol), и мы показываем его с помощью ShowDialog из нашей логики более высокого уровня, которая раньше вызывала ShowDialog в старой версии WinForms этого компонента. Существует несколько элементов управления WindowsFormsIntegrationHost, которые мы использовали внутри компонента WPF для сохранения совместимости с некоторыми из наших существующих частей, которые нельзя переписать в WPF.

Я исследовал это в течение нескольких дней, но никогда не находил много интересного. Я постоянно нахожу смутно связанные посты, в которых говорится о вводимых сообщениях (мышь и клавиатура), но я не знаю, что можно сделать, чтобы это проверить; Я уже пытался разделить код, чтобы удалить все операции с мышью / клавиатурой, которые мог.

Мне трудно добраться куда-нибудь, в первую очередь потому, что эта строка кода полностью изолирована (не является родителем или потомком чего-либо, что я на самом деле могу выделить из нашего кода), и совершенно непрозрачна в том, что она делает.

Вот изображение графика вызовов ANTS функции ShowDialog, показывающее путь вызовов, чтобы добраться сюда:альтернативный текст

Я полностью понимаю, что это может быть чем-то, что просто необходимо сделать как часть WPF (хотя другие компоненты, которые мы написали в WPF, не отображают это поведение), или что это просто очень странная ошибка в профилировщике ANTS, но в этот момент мне нужно проверить это так или иначе. Если кто-то может сказать мне, что происходит или может происходить здесь - или указать мне на то, как я смог бы сам это выяснить, я направлю все виды хорошей кармы на ваш путь.

ОБНОВЛЕНИЕ: В ответ на некоторые обсуждения ниже, есть еще одно мнение от ANTS - это лучше иллюстрирует путаницу, которую я испытываю (это с представлением ANTS в режиме "CPU time"). Я поспешно подверг цензуре части нашего кода, но ни одна из функций, связанных с системой:

альтернативный текст

Спасибо за внимание!

4 ответа

Решение

Я нашел это во время поиска информации по той же проблеме. Я добавлю то, что знаю, и посмотрю, поможет ли это:

Я работаю на коробке WinXP - платформа WPF более интегрирована в Vista и Win7, чем в XP. Отчасти это может быть связано с тем, что WPF работает "поверх" рабочего стола XP, а не внутри него.

Я запускаю чистое родное приложение WPF - без WinForms или другого кода.

Я столкнулся с этим, пытаясь определить, почему простая прокрутка окна потребляет 100% ресурсов процессора и заикается при этом.

Запустив профилировщик производительности AQtime, я вижу, что IntGetMessageW занимает большую часть этого 100% использования ЦП. Это происходит не из-за того, что IntGetMessageW ожидает сообщения, а из-за того, что функция фактически выполняет.

Одна вещь, которую я еще не изучал, это то, что, возможно, IntGetMessageW никогда не был быстрым методом, и, возможно, WPF просто злоупотребляет им. Вполне возможно, что привязки данных в WPF используют собственный насос сообщений Win32 для обновления свойств зависимостей в WPF. Если это так, возможно, мое окно просто имеет слишком много привязок.

Да, это нормально. Любое приложение с графическим интерфейсом всегда выполняет GetMessageW(), ожидая, пока Windows отправит ему сообщение. На самом деле это не сжигание циклов ЦП, а просто блокировка на внутреннем объекте синхронизации до тех пор, пока не будет сообщено какое-то событие пользовательского интерфейса.

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

При профилировании приложения необходимо различать время, затрачиваемое на метод, и количество циклов процессора. Многие инструменты профилирования показывают общее время, потраченное на метод, что в случае чего-то вроде GetMessageW будет довольно высоким. Вся активность основного потока приложения с графическим интерфейсом будет происходить в этом методе. Это не обязательно проблема, однако... это может быть просто основной насос сообщений, ожидающий объекты синхронизации и фактически не потребляющий циклы.

Я бы предложил вам начать с использования функции выборки в профилировщике ANTS, чтобы определить методы, которые вызываются чаще всего, и соотнести их с методами, которые потребляют наибольшее количество циклов ЦП. После того, как вы это сделаете, вы решите, где дать инструменту свое приложение для дальнейшего погружения, и получите представление о том, как выглядят графы вызовов для мест, наиболее интенсивно использующих процессор.

Сначала вы должны заподозрить ваш собственный код. Редко, когда что-то вроде инфраструктуры WPF или Win32 отвечает за низкую производительность или высокую загрузку ЦП. Вероятно, проблема лежит где-то в вашей реализации - она ​​помогает вам получить общее представление о том, где циклы ЦП расходуются в вашей программе.

Я предлагаю вам также потратить некоторое время на изучение возможностей профилировщика, чтобы быть наиболее эффективным. Профилировщики могут быть сложными и запутанными инструментами, пока вы не поймете, что они пытаются вам показать. Связанный туториал от RedGate должен быть хорошим началом, если вы еще этого не сделали. Например, представление "Таймлайн" может быть хорошим местом для начала, чтобы увидеть, где происходит высокая загрузка ЦП, и ограничить ваш анализ этими сегментами исполняемого кода.

альтернативный текст

Граф вызовов - это еще один полезный инструмент в ANTS, так как он помогает вам углубиться в самые дорогие области кода. Ключ должен убедиться, что вы смотрите на общую стоимость, а не только общее время.

альтернативный текст

Похоже, ваш насос сообщений много качает. Может быть интересно посмотреть, каким типом сообщений заполнена ваша очередь сообщений. Можете ли вы использовать Spy++ в своем окне, чтобы увидеть, что происходит?

редактировать

Я неправильно понял проблему.

Ханс Пассант прав, ваша программа просто ожидает в GetMessage какое-то событие для обработки.

Другие вопросы по тегам