我们有一台运行自定义工作负载的 Windows 2003 R2 Enterprise 64 位服务器,它遇到了奇怪的性能问题。下面的精简版遇到的问题较少,但质量上是一样的。
我们将其简化为一个简单的小应用程序,其功能仅包括:
- 监听套接字
- 加入多播组
- 监听进入该组的数据包
- 读取并丢弃数据包
测试应用程序本身是Boost ASIO 多播接收器示例,所以实际上应该不会出现太多错误。实际代码如下……
在负载下运行此程序时,该进程的 CPU 会不时地加速,因为内核代码中会发生所有的处理:
(这里仅显示 CPU 6。在此测试期间(3 小时 17 分钟),所有其他处理器都处于空闲状态)
从图中可以看出,当负载达到峰值时,所有的处理时间都发生在内核代码中。所花费的时间大部分花在延迟过程调用(最大 16.8%)和处理中断(最大 8.5%)上。看起来有某种延迟清理正在发生,但我们不知道它是什么。
据我们所知,它仅发生在 W2K3E-64 上。
它发生在不同的硬件上(HS21,HS22,HS22V,HP DL380)。
在 Windows 2008 上运行测试应用程序可以在较小程度上证明该问题(更频繁,但峰值更小)。
我们如何才能修复这个问题或者下一步应该去哪里寻找呢?
示例中的实际代码:
void handle_receive_from(const boost::system::error_code& error,
size_t bytes_recvd)
{
if (!error)
{
++m_receivedPackets;
m_receivedBytes += bytes_recvd;
m_last64TotalBytes += bytes_recvd;
if ( ( m_receivedPackets & 0x3F ) == 0 )
{
printf( "Received %u bytes in %u packets. The average size of the last 64 packets was %u bytes, and the last byte received was %x.\n",
m_receivedBytes, m_receivedPackets, m_last64TotalBytes / 64, m_buffer[ bytes_recvd - 1 ] );
m_last64TotalBytes = 0;
}
m_socket.async_receive_from(
boost::asio::buffer(m_buffer, max_length), m_senderEndpoint,
boost::bind(&receiver::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
std::cerr << "An error occurred when performing an asyncronous read." << std::endl;
m_socket.get_io_service().stop();
}
}
答案1
“看起来正在进行某种延期清理工作,但我们不知道具体是什么。”
这可能是垃圾收集,但我不确定垃圾收集是否显示为特权时间。如果这是一个 .NET 应用程序,您可以查看.NET CLR Memory
性能计数器(尤其是 Gen 2 很昂贵)。
最后猜测可能的问题似乎有点倒退。最好的办法是分析你的应用程序,看看它在此期间做了什么,看看应用程序做了什么调用。你可能只需使用进程监控观察系统调用。
答案2
我假设系统正在接收多播数据包。您可以尝试阻止它接收数据包,看看是否会出现同样的问题吗?
加入多播组但没有监听数据包会怎么样?
您说这种情况发生在不同的系统上,但实际的 NIC 硬件呢?在那些不同的系统上,情况可能相同。
更新:如果所有系统都使用 Broadcom NIC,则问题可能出在 NIC 上。具体来说,Microsoft 提供的 Broadcom 驱动程序很差劲;Broadcom 网站上的驱动程序要好得多。
答案3
您可以查看两件事:您的线程量以及导致您的 DPC(延迟过程调用)的原因。
线程量子很容易解决(可能是一个转移注意力的话题,但最好检查一下);
- 右键单击“我的电脑”
- 选择属性
- 选择“高级”选项卡
- 在“性能”下选择“设置...”
- 在新窗口中选择“高级”选项卡(现在我们已双重高级!)
- 在“处理器调度”下选择哪个?“程序”还是“后台服务”?
最有可能的是选择了“后台服务”,请尝试选择“程序”。这将减少中断之间的时间量,并允许在处理器上以相同的时间量运行更多线程。您会获得更多中断,但处理时间会减少。
延迟过程调用的诊断稍微有点困难;
正如 @wfaulk 所说,这通常表示驱动程序存在问题。有一个方便的工具叫做DPC 延迟检查器这将帮助您诊断这些问题。即使这种情况发生在多个硬件平台上,它们可能仍然共享一个驱动程序。运行 DPC Checker 并按照其网站上的说明进行操作。
三个后续问题:
您是否在使用组合网卡?它们使用 TCP/IP 堆栈相互通信,可能会导致严重的 DPC 问题。
您的 NIC 是否支持 TCP 卸载?它已启用吗?
(完全是瞎猜)您的测试服务器是域的一部分吗?默认情况下,GPO 每 90 分钟刷新一次...