我试图找到未充分利用 CPU 的 C# 服务器应用程序的瓶颈所在。我认为这可能是由于磁盘 I/O 性能不佳造成的,与应用程序本身无关,但我很难从这个假设中得出事实。
应用程序从本地 MSMQ 队列读取消息,对每条消息进行一些处理,并在处理消息后将响应消息发送到另一个本地 MSMQ 队列。
我正在使用异步循环从队列中读取消息,尽快将它们出队,并使用 Task.Run 调度它们进行处理,以启动每个消息的处理(并且不要等待此 Task.Run .. 只是附加一个延续仅对其进行故障记录错误)。每条消息都是并发处理的,即无需等待一条消息完全处理后再处理下一条消息。
在消息处理结束时,我使用 MessageQueue 的 Send 方法(某种程度上是异步的,但实际上并非如此,因为它必须在返回之前等待磁盘写入 -参见System.Messaging - 为什么 MessageQueue 不提供 Send 的异步版本 https://stackoverflow.com/questions/19746992/system-messaging-why-messagequeue-does-not-offer-an-asynchronous-version-of-se-).
对于基准测试,我将 100K 消息放入队列中(100K 消息的总大小约为 100MB),然后启动该程序。在我的两台个人计算机上(一台是 SSD HD,另一台是 SATA2 HD,具有 i7 CPU 四核 -8 个逻辑进程 -),在程序生命周期期间,我的 CPU 使用率达到了约 95%(使 100K 消息出队、处理它们并发送回复)。消息尽可能快地出队、尽可能快地处理(这里涉及 CPU),然后对发送到不同本地队列的每条消息进行响应。
现在在运行非 HT 双核 CPU 的虚拟机上(不知道底层磁盘是什么,但似乎性能远不如我的磁盘...在基准测试期间,使用 Perfmon 我可以看到平均磁盘秒/写入时间约为 10-15 毫秒VM,而在我的个人计算机上约为 2 毫秒)当我运行相同的工作台时,我仅达到 ~55% CPU(当我在计算机上运行相同的工作台而不向队列发送响应消息时,我达到 ~90% CPU) )。
我真的不明白这里有什么问题。似乎很明显,向队列发送消息是问题所在,并且会减慢程序的全局处理速度(以及要处理的消息的出队),但为什么要考虑到我正在使用 Task.Run 启动每个出队消息的处理并最终发送响应,我不希望 CPU 得到充分利用。除非一个线程发送消息时,它会在等待返回(磁盘写入)时阻止其他线程在同一核心上运行,在这种情况下,考虑到延迟比我的个人计算机上高得多,这可能是有意义的,但一个线程等待 I/O 不应阻止其他线程的运行。
我真的很想理解为什么我在这台机器上没有达到至少 95% 的 cpu 使用率。我盲目地说这是由于磁盘 I/O 性能较差,但考虑到我正在使用 Task.Run 同时运行处理,我仍然不明白为什么它会导致 CPU 利用率不足。这也可能是一些与磁盘完全无关的系统问题,但考虑到 MessageQueue.Send 似乎是问题所在,并且该方法最终将消息写入内存映射文件+磁盘,我不知道性能问题可能来自哪里除了磁盘之外。
当然,这肯定是一个系统性能问题,因为程序在我自己的计算机上最大化了 CPU 使用率,但我需要找到 VM 系统上的瓶颈到底是什么,以及为什么它会影响我的应用程序的并发/速度。
任何想法 ?