为什么 GetMessageW 会在我的 WPF 应用程序中占用大量 CPU 使用率?

发布于 2024-09-30 20:34:07 字数 1349 浏览 0 评论 0原文

我在这里遇到了一个严重的难题。我正在调查应用程序中 WPF 组件的性能问题。

我们的 .net 应用程序非常大,并且几乎完全采用 Windows 窗体。作为新计划的一部分,我们使用丰富的 WPF 用户界面重写了我们的核心组件之一。有很多 WinForms<-->WPF 互操作与此东西一起进行,以将其粘合在一起,我怀疑这可能与我所看到的内容有关。

当我在 ANTS 分析器中分析缓慢的操作时,我看到 UnsafeNativeMethods.IntGetMessageW 函数内部发生了很多活动。 ANTS 报告的 CPU 活动与我们所有业务逻辑和 wpf 渲染内容的总和一样多。该函数没有使用循环的托管代码下线,因此无论 IntGetMessageW 正在做什么,这就是我所追求的。

我不是特别精通 win32 编程,但我知道该上下文中消息循环的基础知识。不过,我在这里看到的一切都不是我们手动执行的任何操作 - 在我们的代码中,我们没有直接与底层消息循环本身或可以在 WPF 调度程序上访问的任何更复杂的内容进行交互。

这里讨论的 WPF 组件是从 Window 继承编写的(即,它不仅仅是一个控件/用户控件),并且我们使用 ShowDialog 从我们的更高级别逻辑中显示它,该逻辑用于在该组件的旧 WinForms 版本上调用 ShowDialog。我们在 WPF 组件内部使用了一些 WindowsFormsIntegrationHost 控件,以保持与某些无法在 WPF 中重写的现有部分的兼容性。

我已经研究了好几天了,但从未发现很多可以继续的东西。我一直在寻找模糊相关的帖子,谈论输入消息(鼠标和键盘),但我不知道我可以做什么来验证这一点;我已经尝试过删除代码以删除所有可以删除的鼠标/键盘操作。

我很难到达任何地方,主要是因为这行代码是完全隔离的(不是我可以指出的实际上来自我们代码的任何内容的父级或子级),并且对于它正在做什么完全不透明。

下面是 ShowDialog 函数的 ANTS 调用图的图像,显​​示了到达此处的调用路径: alt text

我完全意识到这可能是 WPF 中必须完成的事情(尽管我们还有其他组件)用 WPF 编写的不会显示此行为),或者这只是 ANTS 探查器中的一个非常奇怪的错误,但此时我需要以某种方式验证它。如果有人能告诉我这里正在发生或可能发生的事情,或者向我指出某种我可以自己解决的方法,我将为你指引各种善业。

更新: 为了回应下面的一些讨论,这是 ANTS 的另一种观点——这个观点更好地说明了我所遇到的困惑(这是“CPU 时间”模式下的 ANTS 视图)。我匆忙审查了部分代码,但没有与系统相关的功能:

alt text

感谢您的查看!

I've got a serious head-scratcher on my hands here. I'm investigating performance issues with a WPF component in our application.

Our .net application is very large, and almost entirely in windows forms. As part of a new initiative we rewrote one of our core components with a rich WPF ui. There is a lot of WinForms<-->WPF interop going on with this thing to glue it together, and I suspect that may be somehow related to what I'm seeing.

When I profile the slow operation in ANTS profiler, I see a lot of activity occurring inside the function UnsafeNativeMethods.IntGetMessageW. ANTS reports as much CPU activity going there as it does to all of our business logic and wpf rendering stuff combined. There is no managed code downline of that function that is using the cycles, so whatever IntGetMessageW is doing there is what I'm after.

I'm not particularly well-versed in win32 programming, but I know the basics of a message loop in that context. None of what I'm seeing here though is anything we do manually -- at no point in our code are we interacting directly with either the underlying messageloop itself, or any of the more complicated stuff that can be accessed on the WPF dispatcher.

Our WPF component in question here is written inheriting from Window (ie. it's not just a control/usercontrol), and we show it using ShowDialog out of our higher level logic that used to call ShowDialog on the old WinForms version of this component. There are some WindowsFormsIntegrationHost controls that we have used inside of the WPF component to preserve compatibility with some of our existing pieces that could not be rewritten in WPF.

I've been researching this for days, but have never found a whole lot to go on. I keep finding vaguely related postings that talk about input messages (mouse and keyboard), but I don't know what I can do to verify that; I've already tried butchering the code to remove every mouse/keyboard operation I could.

I'm having a hard time getting anywhere primarily because this line of code is completely isolated (not a parent or child of anything I can point out as actually coming from our code), and completely opaque about what it is doing.

Here is an image of an ANTS call graph of the ShowDialog function showing the path of calls to get here:
alt text

I fully realize that this could be something that just has to be done as part of WPF (although other componenets we have written in WPF don't display this behavior), or that this is just a very strange bug in ANTS profiler, but at this point I need to verify it one way or another. If anyone can tell me what is or might be going on here -- or point me to some way I would be able to figure it out myself, I will direct all kinds of good karma your way.

UPDATE:
In response to some discussion below, here is another view from ANTS -- this one better illustrates the confusion I am having (this is with the ANTS view in "CPU time" mode). I've hastily censored parts of our code, but none of the system related functions:

alt text

Thanks for looking!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

奢欲 2024-10-07 20:34:08

看起来你的消息泵正在抽水。看看你的消息队列中充满了什么样的消息可能会很有趣。你能在你的窗口上使用 Spy++ 看看发生了什么吗?

编辑

我误解了这个问题。

Hans Passant 是对的,您的程序只是在等待 GetMessage 来处理某些事件。

Looks like your message pump is pumping a lot. Could be interesting to see what kind of message your message queue is filled of. Can you use Spy++ on your window to see what is going on?

Edit

I've misunderstood the problem.

Hans Passant is right, your program is just waiting on GetMessage for some event to process.

帅冕 2024-10-07 20:34:07

是的,这是正常的。任何 GUI 应用程序总是执行 GetMessageW(),等待 Windows 向其发送消息。这样做实际上并没有消耗 CPU 周期,只是在内部同步对象上阻塞,直到发出某种 UI 事件信号为止。

这当然会让分析 UI 应用程序变得困难,您确实需要单元测试来测试应用程序的子组件。

Yes, this is normal. Any GUI app is always executing GetMessageW(), waiting for Windows to send it a message. It isn't actually burning CPU cycles doing this, just blocked on an internal synchronization object until some kind of UI event is signaled.

This of course makes profiling UI apps difficult, you really need unit tests that test the subcomponents of your app.

匿名的好友 2024-10-07 20:34:07

在分析应用程序时,您需要区分方法中花费的时间和消耗的 CPU 周期。许多分析工具都会向您显示花费的总时间在一个方法中 - 在像 GetMessageW 这样的情况下,它会非常高。 GUI 应用程序主线程的所有活动似乎都发生在该方法内。然而,这不一定是问题……这可能只是等待同步对象的主消息泵,而不是实际消耗的周期。

我建议您首先使用 ANTS 分析器中的采样功能来识别最常调用的方法,并将这些方法与消耗最多 CPU 周期的方法相关联。 完成此操作后,您可以决定在何处检测应用程序以进一步深入了解 CPU 密集度最高的地方的调用图。

您应该首先怀疑自己的代码。 像 WPF 或 Win32 基础结构这样的东西很少会导致性能差或 CPU 利用率高。问题可能出在您的实现中的某个地方 - 它可以帮助您全面了解 CPU 周期在程序中的使用情况。

我建议您也花一些时间学习探查器的功能才能最有效。探查器可能是复杂且令人困惑的工具,直到你明白他们想向你展示什么。如果您还没有这样做,RedGate 的链接教程应该是一个很好的起点。例如,“时间轴”视图实际上可能是开始查看高 CPU 活动发生位置并将分析限制在执行代码的那些片段的好地方。

alt text

调用图是 ANTS 中的另一个有用工具,因为它可以帮助您深入了解最昂贵的代码区域。关键是要确保您关注的是总体成本,而不仅仅是总体时间

替代文字

When profiling an application, you need to differentiate between time spent in a method and CPU cycles consumed. Many profiling tools show you the overall time spent in a method - which in the case of somthing like GetMessageW is going to be quite high. All of the activity of the main thread of a GUI application will appear to happen within that method. That's not necessarily a problem, however ... this may simply be the main message pump waiting on a synchronization objects and not actually consuming cycles.

I would suggest you start by using the sampling feature in the ANTS profiler to identify the methods that are called most often and correlate those to the methods that are consuming the most CPU cycles. Once you've done that, you decide where to instrument your application to dive in further and get an idea of what the call graphs look like for the places that are most CPU intensive.

You should start by suspecting your own code first. It's rare that something like the WPF or Win32 infrastructure is responsible for poor performance or high CPU utilization. Likely, the problem lies somewhere in your implementation - it helps you get an overall sense of where CPU cycles are spent in your program.

I suggest that you also spend some time learning the capabilities of the profiler to be most effective. Profilers can be sophisticated and confusing tools until you understand what it is that they are trying to show you. The linked tutoral from RedGate should be a good place to start, if you haven't done so already. For example, the Timeline view may actually be a good place to start to see where high-CPU activity is occurring, and confining your analysis to those segments of executed code.

alt text

The Call Graph is another useful tool in ANTS, as it helps you drill into areas of the code that are most expensive. The key is to make sure you are looking at the overall cost and not just the overall time.

alt text

耶耶耶 2024-10-07 20:34:07

我在搜索同一问题的信息时发现了这一点。我将添加我所知道的内容,看看是否有帮助:

我正在 WinXP 机器上运行 - WPF 框架在 Vista 和 Win7 中比在 XP 中集成得更多。部分原因可能是 WPF 在 XP 桌面“之上”运行,而不是在其中运行。

我正在运行一个纯本机 WPF 应用程序 - 没有 WinForms 或其他代码。

我遇到了这个问题,试图确定为什么简单地滚动窗口会消耗 100% CPU 并且在执行此操作时出现卡顿。

运行 AQtime 性能分析器,我发现 IntGetMessageW 占据了 100% CPU 使用率的最大部分。这不是由于 IntGetMessageW 等待消息,而是该函数实际上正在执行的操作。

我还没有研究过的一件事是,也许 IntGetMessageW 从来就不是一个快速方法,也许 WPF 只是过度使用了它。 WPF 中的数据绑定可能使用本机 Win32 消息泵来更新 WPF 中的依赖关系属性。如果是这种情况,可能是我的窗口有太多绑定。

I found this while searching for information on the same issue. I'll add what I know and see if it helps:

I'm running on a WinXP box - the WPF framework is more integrated in Vista and Win7 than it is in XP. Some of this could be due to how WPF runs "on top" of the XP desktop, rather than within it.

I'm running a pure native WPF application - with no WinForms or other code.

I ran into this trying to determine why simply scrolling a window would consume 100% CPU and stutter while doing it.

Running the AQtime performance profiler, I see that IntGetMessageW occupies the largest part of that 100% CPU usage. This is not due to IntGetMessageW waiting for a message, but something the function is actually doing.

The one thing I haven't looked into yet is that maybe IntGetMessageW has never been a fast method, and maybe WPF simply overuses it. It is possible that data bindings in WPF use the native Win32 message pump to update Dependency Properties within WPF. If that is the case, it could be that my window simply has too many bindings.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文