ReceiveAsync与BeginReceive的性能
本文关键字:性能 BeginReceive ReceiveAsync | 更新日期: 2023-09-27 18:29:23
我目前正在编写一个客户端应用程序,我想知道是应该使用Socket类的ReceiveAsync还是BeginReceive方法。到目前为止,我一直在使用后者,但我发现它似乎给CPU带来了很大的压力。以下是我的接收循环的基本情况:
private void socket_ReceiveCallback(IAsyncResult result_)
{
// does nothing else at the moment
socket.EndReceive(result_);
byte[] buffer = (byte[])result_.AsyncState;
// receive new packet
byte[] newBuffer = new byte[1024];
socket.BeginReceive(newBuffer, 0, newBuffer.Length, SocketFlags.None,
socket_ReceiveFallback, newBuffer);
}
现在我一直在想我是否做错了什么,因为其他通信应用程序几乎不会给CPU带来压力。我还想知道使用SocketAsyncEventArgs和ReceiveAsync是否会更好。
以下是我的问题:
为什么我的循环给CPU带来如此大的压力?我应该使用SocketAsyncEventArgs和ReceiveAsync而不是BeginReceive吗?
BeginReceive
和EndReceive
是在C#5中引入现代async
和await
关键字之前使用的旧的遗留异步模式的残余。
因此,对于异步编程,您应该更喜欢使用ReceiveAsync
而不是BeginReceive
和EndReceive
。
对于真正的高性能场景,您应该使用SocketAsyncEventArgs
。这是为高性能而设计的,并由Kestrel网络服务器使用。
来自SocketAsyncEventArgs文档的备注部分
SocketAsyncEventArgs类是System.Net.Sockets.Socket类的一组增强功能的一部分,这些增强功能提供了一种可供专门的高性能套接字应用程序使用的替代异步模式。此类是专门为需要高性能的网络服务器应用程序设计的。应用程序可以专门使用增强异步模式,也可以仅在目标热点区域使用(例如,当接收到大量数据时)。
这些增强的主要特征是避免了在大容量异步套接字I/O期间重复分配和同步对象。System.Net.Sockets.Socket类当前实现的Begin/End设计模式要求为每个异步套接字操作分配一个System.IAsyncResult对象。
在新的System.Net.Sockets.Socket类增强中,异步套接字操作由应用程序分配和维护的可重用SocketAsyncEventArgs对象描述。高性能套接字应用程序最清楚必须维持的重叠套接字操作的数量。应用程序可以创建所需数量的SocketAsyncEventArgs对象。例如,如果服务器应用程序需要始终有15个套接字接受操作未完成以支持传入客户端连接速率,则它可以为此目的分配15个可重复使用的SocketAsyncEventArgs对象。
我一直在本地主机环回连接上对同步套接字与异步套接字进行基准测试。我的结果是异步版本大约慢了30%。考虑到异步IO现在风靡一时,这让我很惊讶。我用了多少线程并不重要。我可以使用128个线程,而且同步IO更快。
原因是,我相信异步IO需要更多的分配和更多的内核模式转换。
因此,如果您不希望有数百个同时连接,您可以切换到同步IO。
如果回答这个问题,您必须对应用程序进行概要分析。我想知道的是
- 为什么我看不到EndReceive
- 为什么根本不使用接收到的缓冲区以及
- 为什么一次又一次地分配新的缓冲区——这是这里唯一应该占用任何资源(CPU/内存)的操作
看看这个:http://msdn.microsoft.com/de-de/library/dxkwh6zw.aspx
我对最大负载进行了比较,结果以GB(每秒千兆字节)为单位:
- ReceiveAsync:约1,2GB
- 开始接收:~1.1GB
- 接收(在线程循环中):~1.4GB
注:
- 所有结果都是使用环回地址(localhost)和发送套接字的线程生成的
- 缓冲区大小为8192字节
对于大的负载传输,我建议在线程中使用Receive,但对于各种连接的更好CPU性能,我建议使用ReceiveAsync或BeginReceive。