ReceiveAsync与BeginReceive的性能

时间:2012-03-28 20:23:43

标签: c# performance sockets asynchronous tcp

我正在编写客户端应用程序,我想知道是否应该使用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吗?

4 个答案:

答案 0 :(得分:6)

我一直在localhost环回连接上对同步和异步套接字进行基准测试。我的结果是异步版本慢了大约30%。考虑到异步IO现在风靡一时,这对我来说是令人惊讶的。我用了多少线程并不重要。我可以使用128个线程,但同步IO更快。

我认为,原因是异步IO需要更多分配和更多内核模式转换。

因此,如果您不希望有数百个同时连接,那么您可以切换到同步IO。

答案 1 :(得分:1)

要回答这个问题,你必须对你的申请进行分析。 我想知道的是

  • 为什么我看不到EndReceive
  • 为什么你根本不使用收到的缓冲区 和
  • 为什么要一次又一次地分配新缓冲区 - 这是唯一需要占用任何资源(CPU /内存)的操作

看看这个:http://msdn.microsoft.com/de-de/library/dxkwh6zw.aspx

答案 2 :(得分:1)

BeginReceiveEndReceive是在C#5中引入现代asyncawait关键字之前使用的旧的旧式异步模式的残余。

因此,您应该更喜欢在ReceiveAsyncBeginReceive上使用EndReceive来进行异步编程。

对于真正高性能的场景,应使用SocketAsyncEventArgs。这是专为高性能而设计的,由Kestrel Web服务器使用。

SocketAsyncEventArgs documentation的备注部分

  

SocketAsyncEventArgs类是对System.Net.Sockets.Socket类的一组增强的一部分,这些增强提供了可供专业的高性能套接字应用程序使用的替代异步模式。此类是专门为要求高性能的网络服务器应用程序而设计的。应用程序可以单独使用增强的异步模式,也可以仅在目标热点区域(例如,在接收大量数据时)使用

。      

这些增强功能的主要特征是避免了在大容量异步套接字I / O期间对象的重复分配和同步。当前由System.Net.Sockets.Socket类实现的Begin / End设计模式要求为每个异步套接字操作分配一个System.IAsyncResult对象。

     

在新的System.Net.Sockets.Socket类增强中,异步套接字操作由应用程序分配和维护的可重用SocketAsyncEventArgs对象描述。高性能套接字应用程序最了解必须持续的重叠套接字操作量。应用程序可以创建所需的任意多个SocketAsyncEventArgs对象。例如,如果服务器应用程序需要始终具有15个未完成的套接字接受操作以支持传入的客户端连接速率,则可以为此目的分配15个可重用的SocketAsyncEventArgs对象。

答案 3 :(得分:0)

我对最大负载做了比较,结果以GB(每秒千兆字节)为单位:

  • ReceiveAsync:〜1,2GBs
  • 开始接收:〜1,1GB
  • 接收(在线程循环中):约1,4GB

注意:

  • 所有结果都是使用环回地址(localhost)并使用线程作为发送套接字的结果
  • 8192个字节用于缓冲区大小

对于大负载传输,我建议在线程中使用Receive,但是为了在各种连接下获得更好的CPU性能,请使用ReceiveAsync或BeginReceive。