46

我的问题是与我的同事就 C++ 与 C# 解决争论。

我们已经实现了一个接收大量 UDP 流的服务器。该服务器是用 C++ 开发的,使用异步套接字并使用完成端口重叠 I/O。我们使用 5 个完成端口和 5 个线程。该服务器可以轻松处理千兆网络上的 500 Mbps 吞吐量,而不会丢失任何数据包/错误(我们的测试没有超过 500 Mbps)。

我们尝试在 C# 中重新实现相同类型的服务器,但我们无法达到相同的传入吞吐量。我们使用异步接收 usingReceiveAsync方法和池SocketAsyncEventArgs来避免为每个接收调用创建新对象的开销。每个SAEventArgs都设置了一个缓冲区,因此我们不需要为每个接收分配内存。池非常非常大,所以我们可以排队超过 100 个接收请求。此服务器无法处理超过 240 Mbps 的传入吞吐量。超过这个限制,我们会在 UDP 流中丢失一些数据包。

我的问题是:我应该期望使用 C++ 套接字和 C# 套接字具有相同的性能吗?我的观点是,如果在 .NET 中正确管理内存,它应该具有相同的性能。

附带问题:有人会知道一篇很好的文章/参考资料来解释 .NET 套接字如何在后台使用 I/O 完成端口吗?

4

3 回答 3

8

有人会知道一篇很好的文章/参考资料来解释 .NET 套接字如何在后台使用 I/O 完成端口吗?

我怀疑唯一的参考是实现(即反射器或其他程序集反编译器)。这样,您会发现所有异步 IO 都通过一个 IO 完成端口,回调在 IO 线程池(与普通线程池分开)中处理。

使用 5 个完成端口

我希望使用单个完成端口将所有 IO 处理到单个线程池中,每个池一个线程为完成提供服务(假设您正在异步执行任何其他 IO,包括磁盘)。

如果您有某种形式的优先级,那么多个完成端口将是有意义的。

我的问题是:我应该期望使用 C++ 套接字和 C# 套接字具有相同的性能吗?

是或否,取决于您定义“使用...套接字”部分的范围。就从异步操作开始到完成发布到完成端口的操作而言,我预计没有显着差异(所有处理都在 Win32 API 或 Windows 内核中)。

然而,.NET 运行时提供的安全性会增加一些开销。例如。将检查缓冲区长度,验证委托等。如果应用程序的限制是 CPU,那么这可能会产生影响,并且在极端情况下,很小的差异很容易加起来。

此外,.NET 版本偶尔会因 GC 而暂停(.NET 4.5 会进行异步收集,因此将来会变得更好)。有一些技术可以最大限度地减少垃圾累积(例如,重用对象而不是创建它们,利用结构同时避免装箱)。

最后,如果 C++ 版本可以工作并且满足您的性能需求,为什么要移植?

于 2011-12-11T17:04:28.323 回答
6

您不能直接将代码从 C++ 移植到 C# 并期望获得相同的性能。在内存管理 (GC) 和确保代码安全(边界检查等)方面,.NET 比 C++ 做得更多。

我将为所有 IO 操作分配一个大缓冲区(例如 65535 x 500 = 32767500 字节),然后为每个操作分配一个块SocketAsyncEventArgs(以及发送操作)。内存比CPU便宜。使用缓冲区管理器/工厂为所有连接和 IO 操作提供块(享元模式)。Microsoft 在他们的 Async 示例中这样做了。

Begin/End 和 Async 方法都在后台使用 IO 完成端口。后者不需要为提高性能的每个操作分配对象。

于 2011-12-11T18:25:56.257 回答
1

我的猜测是您没有看到相同的性能,因为 .NET 和 C++ 实际上在做不同的事情。您的 C++ 代码可能不安全,或者检查边界。此外,您是否只是在测量接收数据包而不进行任何处理的能力?或者您的吞吐量是否包括数据包处理时间?如果是这样,那么您为处理数据包而编写的代码可能效率不高。

我建议使用分析器来检查花费最多的时间并尝试对其进行优化。实际的套接字代码应该非常高效。

于 2011-12-11T17:29:24.953 回答