【问题标题】:.NET IOCP ThreadPool overhead with async UDP operations.NET IOCP 线程池开销与异步 UDP 操作
【发布时间】:2011-05-25 21:41:04
【问题描述】:

我开发了一个 VoIP 媒体服务器,它与远程 SIP 端点交换 RTP 数据包。它需要很好地扩展 - 虽然我最初担心我的 C# 实现不会接近它所取代的 C++ 版本,但我使用了各种分析器来磨练实现并且性能非常接近。

我通过创建可重用对象池来限制大多数对象分配,我正在使用 ReceiveFromAsync 和 SendToAsync 来发送/接收数据报,并且我正在使用生产者/消费者队列在系统中传递 RTP 数据包。在具有 2 个 2.4GHz Xeon 处理器的机器上,我现在可以处理大约 1000 个并发流,每个流每秒发送/接收 50 个数据包。然而,迭代配置文件/调整/配置文件让我着迷 - 我确信那里的效率更高!

触发处理的事件是在 SocketAsyncEventArgs 上调用 Completed 委托 - 后者又通过处理管道发送 RTP 数据包。

剩下的令人沮丧的是,IOCP 线程池中的开销似乎很大。分析器显示只有 72% 的包容性采样时间在“我的代码”中 - 之前的时间似乎是线程池开销(下面的堆栈帧)。

所以,我的问题是:

  1. 我的理解是否遗漏了什么?
  2. 是否可以减少这种开销?
  3. 是否可以将异步套接字函数使用的线程池替换为使用开销较小的自定义轻量级线程池?
100% 媒体网关 95.35% 线程::intermediateThreadProc(void *) 88.37% ThreadNative::SetDomainLocalStore(类对象 *) 88.37% BindIoCompletionCallbackStub(unsigned long,unsigned long,struct _OVERLAPPED *) 86.05% BindIoCompletionCallbackStubEx(unsigned long,unsigned long,struct _OVERLAPPED *,int) 86.05% ManagedThreadBase::ThreadPool(struct ADID,void (*)(void *),void *) 86.05% CrstBase::Enter(无效) 86.05% AppDomainStack::PushDomain(struct ADID) 86.05% Thread::ShouldChangeAbortToUnload(class Frame *,class Frame *) 86.05% AppDomainStack::ClearDomainStack(无效) 83.72% ThreadPoolNative::CorWaitHandleCleanupNative(void *) 83.72% __CT??_R0PAVEEArgumentException@@@84 83.72% DispatchCallDebuggerWrapper(unsigned long *,unsigned long,unsigned long *,unsigned __int64,void *,unsigned __int64,unsigned int,unsigned char *,class ContextTransitionFrame *) 83.72% DispatchCallBody(unsigned long *,unsigned long,unsigned long *,unsigned __int64,void *,unsigned __int64,unsigned int,unsigned char *) 83.72% MethodDesc::EnsureActive(void) 81.40% _CallDescrWorker@20 81.40% System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint32,uint32,valuetype System.Threading.NativeOverlapped*) 76.74% System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(uint32,uint32,valuetype System.Threading.NativeOverlapped*) 76.74% System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(valuetype System.Net.Sockets.SocketError,int32,valuetype System.Net.Sockets.SocketFlags) 74.42% System.Threading.ExecutionContext.Run(类 System.Threading.ExecutionContext,类 System.Threading.ContextCallback,对象) 72.09% System.Net.Sockets.SocketAsyncEventArgs.ExecutionCallback(object) 72.09% System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(类 System.Net.Sockets.SocketAsyncEventArgs)

【问题讨论】:

  • 我不知道任何允许以任何方式调整 IOCP 线程池的配置选项或类似选项。我想你能做的就是优化代码中的 72%。
  • @dtb 不幸的是,大约 40% 的时间随后花在了 Socket.SendToAsync 上——所以我只剩下 32% 可以玩了——而且我想我已经差不多了尽我所能。也许我已经接近 .NET 的极限,只需要扩展硬件。
  • 这很可能只是分析的产物。您确定您推送的数据超出了服务器的处理能力吗?当负载足够大时,有很多乐观锁和自旋锁有助于提高性能,但在分析时会产生奇怪的数据。很可能那里根本没有做任何工作,只是在等待下一位数据。也可能只是偶然碰巧发生在 GC 冻结进程的地方。异步代码很难分析 - 我有很多“问题”,结果证明是分析本身的工件。

标签: c# udp threadpool rtp iocp


【解决方案1】:

在 Windows 上每秒 50,000 个数据包相当不错,我想说硬件和操作系统是更重要的扩展问题。不同的网络接口有不同的限制,英特尔服务器网卡主要是高性能和良好的跨平台驱动程序,但与 Linux 相比,博通在 Windows 上的记录并不好。仅当驱动程序支持这些功能时,Windows 的高级核心网络 API 才会启用,而 Broadcom 已被证明是一家只为较新硬件启用高级功能的公司,尽管其他操作系统的旧设备支持。

我将开始研究多个 NIC,例如使用四块英特尔服务器 NIC,并使用 Windows 高级网络 API 将一个 NIC 绑定到每个处理核心。理论上,您可以通过一个 NIC 发送 50,000 个,通过另一个 NIC 发送 50,000 个。

http://msdn.microsoft.com/en-us/library/ff568337(v=VS.85).aspx

但是,您似乎并没有真正的基准来衡量代码的效率。我希望看到与不运行 VoIP 负载、在 TCP 传输而不是 UDP 上运行以及在其他操作系统上运行以比较 IP 堆栈和 API 效率的服务器进行比较。

【讨论】:

  • 谢谢史蒂夫 - 我同意硬件是我的下一个停靠点,尽管我希望首先简化代码。不确定我是否理解您在基线方面的意思 - 没有有效负载,没有什么可分析的,RTP 不使用 TCP。
【解决方案2】:

也添加一些信息 - 我最近发现 IOCP 线程池中存在可能影响您的性能的错误:请参阅 http://support.microsoft.com/kb/2538826 中“原因”部分的第 3 点。它可能对您的情况有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-22
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    相关资源
    最近更新 更多