dar*_*key 11 c# performance msmq async-await
我试图找到C#服务器应用程序的瓶颈在哪里未充分利用CPU.我认为这可能是由于磁盘I/O性能不佳而与应用程序本身无关,但我无法从这个假设中得出一个事实.
应用程序从本地MSMQ队列中读取消息,对每条消息进行一些处理,并在处理完消息后,将响应消息发送到另一个本地MSMQ队列.
我正在使用异步循环从队列中读取消息,尽可能快地将它们出列并使用Task.Run调度它们进行处理以启动每个消息的处理(并且不要等待此Task.Run ..只是附加一个延续只记错了记录错误).每个消息被同时处理,即在处理下一个消息之前不需要等待消息被完全处理.
在处理消息的最后,我使用MessageQueue的Send方法(不知何故异步但不是真的因为它必须等待磁盘写入才能返回-see System.Messaging - 为什么MessageQueue不提供异步版本的Send).
对于基准测试,我在队列中排队100K消息(100K消息的总大小约为100MB)然后我启动程序.在我的两台个人计算机上(一台SSD HD和另一台SATA2 HD,i7 CPU四核-8逻辑处理器)我在程序生命周期内达到~95%的CPU使用率(将100K消息出列,处理它们和发送回复).消息尽可能快地出列,尽可能快地处理(这里涉及的CPU),然后对发送到不同本地队列的每个消息进行响应.
现在在运行非HT双核CPU的虚拟机上(不知道什么是底层磁盘,但似乎远不如地雷......在基准测试期间,使用Perfmon我可以看到avg disk sec/write arround 10-15 ms on this虚拟机,而在我的个人计算机上大约2ms)当我运行相同的工作台时,我只能达到~55%的CPU(当我在机器上运行相同的工作台而不向队列发送响应消息时我达到~90%CPU ).
我真的不明白这里有什么问题.似乎很清楚,向队列发送消息是问题并且减慢了程序的全局处理(以及要处理的消息的队列化),但为什么会考虑我使用Task.Run来启动每个出列消息的处理并最终响应发送,我不希望CPU未被充分利用.除非当一个线程正在发送消息时它阻止其他线程在等待返回(磁盘写入)时在同一个核心上运行,在这种情况下,考虑到延迟远远高于我的个人计算机,它可能是有意义的,但是一个线程等待I/O不应该阻止其他线程运行.
我真的想了解为什么我没有达到这台机器至少95%的CPU使用率.我盲目地说这是由于较差的磁盘I/O性能,但我仍然不明白为什么它会导致CPU利用不足,因为我正在使用Task.Run同时运行处理.它也可能是一些与磁盘完全无关的系统问题,但考虑到MessageQueue.Send似乎是问题,并且此方法最终将消息写入内存映射文件+磁盘,我看不出性能问题可能来自何处除了磁盘.
当然,由于程序最大限度地提高了我自己计算机上的CPU使用率,因此确实存在系统性能问题,但我需要找到VM系统上的瓶颈,以及为什么它会影响我的应用程序的并发/速度.
任何的想法 ?
要检查磁盘和/或 CPU 利用率不佳,只有一种工具:Windows Performance Toolkit。有关如何使用它的示例,请参见此处。您应该从 Windows 8.1 SDK 获取最新版本(需要 .NET 4.5.1),它为您提供大部分功能,但 Windows 8 SDK 也可以。
您可以在此处获得 CPU 利用率百分比和磁盘利用率百分比图表。如果其中一个为 100%,而另一个则较低,那么您就找到了瓶颈。由于它是一个系统范围的分析器,您可以检查 msmq 服务是否严重使用光盘或者您或其他人(例如,病毒扫描程序是一个常见问题)。
您可以直接访问调用堆栈并检查哪个进程和线程唤醒了应该全速运行的工作线程。然后,您可以跳转到准备线程并进行处理并检查它在准备线程之前做了什么。这样你就可以直接验证是什么阻碍了它这么久。
不用再猜测了。您可以真正看到系统在做什么。
要进一步分析,请在 CPU 使用率精确视图中启用以下列:
然后深入查找进程中的调用堆栈,以查看在应该全速运行的线程中哪些地方确实出现了较长的等待时间。您可以深入到一个事件,直到无法再进一步为止。然后您将看到 Readying Process 和 ReadyingThreadId 中的值。转到该进程/线程(可以是您自己的)并重复该过程,直到您最终陷入某些阻塞操作,该操作涉及磁盘 IO 或睡眠或长时间运行的设备驱动程序调用(例如病毒扫描程序或虚拟机驱动程序)。
| 归档时间: |
|
| 查看次数: |
961 次 |
| 最近记录: |