.NET平台下几种SOCKET模型的简要性能供参考的讨论【转】

这个内容在cnblogs中也讨论过很多次了,这两天大概看了一些资料,看到一些简单的性能指标拿出来和大家讨论一下。

Socket + Threads/ThreadPool

大概性能:小于1500个连接 

实现:Accept一个Socket,就交给一个线程去管理,比较笨,但也比较有效,因为是同步方式,控制起来很方便。高级点的,就是交给一个线程池去管理,线程池由系统自动托管,省去了开销线程的时间。一般小型项目,用这个完全足够,开发也简单。但要注意,如果若干Socket长时间占用线程池中的线程,同时其它连接数又比较多,很容易出现提示说你没有足够的线程供使用。呵呵,让Socket少做点事,少占用时间,换一个快点的CPU是不错的方式。另外,如果有一些比较好的第三方线程池组件,也可以选择使用,比如SmartThreadPool。

Socket + Select

大概性能:大于1500个连接后性能下降 

实现:Select是很常用的一种模型。是在阻塞功能中轮询一个或多个Socket,将要处理的Socket放到一个IList中,当Select轮询结束后,然后我们再自己处理这个IList中的Socket。具体的用法可以看一下MSDN。Select的效率并不能说是高的,因为当队列中待处理的Socket比较多的时候,处理最后几个Socket相当于要遍历所有前面的Socket,非常不划算的。
 

Socket + Asynchronous

大概性能:约7500个客户端连接 

实现:BeginXXXX,EndXXXX,再熟悉不过了吧。异步Socket归根到底,还是用的线程池技术,用线程池来处理异步IO。这就又引出个问题,.NET的线程池又是用的什么实现方式,以前看过有人说,.NET的线程池是用的完成端口来实现的,我不知道这样的说法是不是正确,从查到的资料中也没有办法确认(希望这点有朋友可以告诉我)。异步Socket对于程序的处理流程来说比同步复杂了许多,异步回调函数的控制不如同步方式那样直观。但有一点我想应该是要注意的,就是回调函数应该轻装上阵,不应该处理过多的事务,对传递数据的处理,应该交给其它线程进行处理。


IOCP(完成端口)

大概性能:约20000~50000个客户端连接 

实现:现在.NET下有一些伪IOCP,大家可以去搜索一下,还没有见过开放出来的用这些伪IOCP来实现的SOCKET例子。我说的20000~50000个客户端连接,是指在C++下开发的情况,这样的情况下,需要用到的基本技术还包括内存池、查询算法等。

 

伪IOCP能实现多少最大连接,没有资料可以查,如果有朋友知道,可以讨论一下。另外上 面提到的许多数据,是从一些资料上摘抄下来的,我没有自己试过,仅仅是拿出来和大家讨论一下。我想,一个高性能的服务端程序,可能需要的技术不仅仅是采用 什么模型,还有许多细节需要注意,比如内存的处理,采用什么算法等等,当然,这仅仅是软件成本上的,硬件上肯定也是需要投入的。

posted on 2006-01-05 10:13 天生这样 阅读(7516) 评论(21)  编辑 收藏 所属分类: C#

评论

#1楼  2006-01-05 11:26 风满袖      

.net的ThreadPool不是完成端口。
Socket + ThreadPool 和Socket + Asynchronous本质上是一样的,性能自然也没有什么差别。   回复  引用  查看    

#2楼  2006-01-05 12:20 fisher      

我现在很关注这方面的动态.

  回复  引用  查看    

#3楼  2006-01-05 12:58 双鱼座      

.net上的完成端口实现很多呀,在CodeProject上随便一搜就是一大把。例如那个Sonic.net好象比较有名:http://www.codeproject.com/csharp/managediocp.asp

不过你那个“大概性能:XXXX连接”是完全不可信的。影响最大可连接数的因素很多很多,例如:CPU个数、工作者线程回调复杂度、内存容量等等。当然,使用何种Socket模型也有关系,MSDN的文章说IOCP是最好的,文章上报出的也只是6000个连接(非托管)。

当然,都知道IOCP是Windows2000/NT特有的Socket模型(http://msdn.microsoft.com/msdnmag/issues/1000/Winsock/default.aspx
),不具有广泛的兼容性,更别说Linux/Mono了。并且大部分情况下,托管代码都是通过AppServer(例如IIS)获取Socket服务端连接,自己写纯托管的Socket服务端好象没有多大必要。   回复  引用  查看    

#4楼 [楼主] 2006-01-05 13:11 天生这样      

Managed I/O Completion Ports这个我看过,我想应该算是伪IOCP
我没有找到.NET用IOCP来搞的Socket例子,真的很想见识一下。
性能方面,数值只是一个参考值,取决的因素很多,但MSDN上报的IOC-6000我不太清楚,我这里看过的单台游戏端服务器上万连接是常常的。



  回复  引用  查看    

#5楼  2006-01-05 15:05 Sumtec      

如果我没有记错的话,.NET里面的很多异步IO,背后都是用的IOCP的。也就是说,用BeginXXX/EndXXX实际上应该就是使用的IOCP。不太记得了,我要查一下。   回复  引用  查看    

#6楼  2006-01-05 15:16 Sumtec      

刚查过了,确认.NET背后的机制就是IOCP(个人认为)。用Reflector看System.Threading.IOCompletionCallback的callee,可以看到这么一条调用链:

System.Threading.IOCompletionCallback
Depends On
Used By
System.Net.Sockets.OverlappedCache..ctor(Overlapped, Object, IOCompletionCallback, Boolean)
Depends On
Used By
System.Net.Sockets.BaseOverlappedAsyncResult.SetUnmanagedStructures(Object) : Void
Depends On
Used By
System.Net.Sockets.OverlappedAsyncResult.SetUnmanagedStructures(Byte[], Int32, Int32, SocketAddress, Boolean) : Void
Depends On
Used By
System.Net.Sockets.OverlappedAsyncResult.SetUnmanagedStructures(Byte[], Int32, Int32, SocketAddress, Boolean, OverlappedCache&) : Void
Depends On
Used By
System.Net.Sockets.Socket.DoBeginReceive(Byte[], Int32, Int32, SocketFlags, OverlappedAsyncResult) : SocketError
System.Net.Sockets.Socket.DoBeginReceiveFrom(Byte[], Int32, Int32, SocketFlags, EndPoint, SocketAddress, OverlappedAsyncResult) : Void
System.Net.Sockets.Socket.DoBeginSend(Byte[], Int32, Int32, SocketFlags, OverlappedAsyncResult) : SocketError
System.Net.Sockets.Socket.DoBeginSendTo(Byte[], Int32, Int32, SocketFlags, EndPoint, SocketAddress, OverlappedAsyncResult) : Void

说错了不要扔板砖啊,大过年的。   回复  引用  查看    

#7楼  2006-01-05 15:43 双鱼座      

Overlap模型是异步Select模型的优化,是一个比IOCP兼容范围广(适宜所有的Windows版本),但性能比IOCP略逊的一种IO模型. 与IOCP模型的共同点是工作者线程回调中处理机制相同,都需要通过Overlap结构来承载数据;区别在于,不会通过调用 CreateIoCompletionPort建立端口,也不会调用GetQueuedCompletionStatus来轮询。
如果你没有找到所调用的那两个函数,就不能说明是否采用了IOCP。   回复  引用  查看    

#8楼 [楼主] 2006-01-05 15:49 天生这样      

比较同意 双鱼座 这个说法
从命名上来得出结论说服力不强
Winsock中IO模型有五种,这些可以参考相关文档。
  回复  引用  查看    

#9楼  2006-01-05 16:11 ccBoy      

不错的帖
我认为这篇文章不错,当然如果你只想用C#的话
http://www.theukwebdesigncompany.com/articles/iocp-thread-pooling.php

A good rule of thumb is to set the concurrency level to match the number of CPU’s in the system. If the machine our server is running on only has one CPU, then only one thread can be executing at any given time. It will require a task swap to have another thread get CPU time. We want to reduce the number of active threads at any given time to maximize performance. This also leads to scalability. As the number of CPU’s increase, we can increase the concurrency level because there is a CPU to execute that thread. This is a general rule and is always a good starting point for configuring our thread pools.
  回复  引用  查看    

#10楼 218.2.159.* 2006-01-05 16:26 问 题 男 [未注册用户]

不多说,只谈一点“有io completion不一定就是io completion ports(不是广义的,特指sdk中的概念)”

win32中ReadFileEx、WriteFileEx等api有一个参数是iocompletion routine,这个不是io completion ports,切不可混为一谈,ioc routine使用的是核心态apc+alertable io的技术。这个技术和其他异步io技术从执行效果看,和io completion ports差不多,都实现了异步,但是性能上存在着差距,拿ioc routine来说,它规定,只能在调用io api的线程中服务io完成后的工作,比如某线程调用了ReadFileEx(...callback),则这个callback函数的执行线程一定是调 用ReadFileEx的那个,而且系统只会在线程处于alertable态时才调用callback,这就意味着在io实际完成时即使有n多空闲的线 程,只要调用io的那个不处于alertable态,则callback便不会被调用,这显然是低效的。其他的诸如overlapped的异步技术也会受 到这样那样的的限制。与这些不同的是,使用io completion ports模型则高效得多,一个port关联着许多io handle,也关联了一些服务线程,当某一线程调用了io api,在io完成时,任何关联到此port的线程都可以为这个io handle服务,系统核心选出(使用最近使用的)其中一个然后把控制权交给它。可见,使用iocp能尽量发挥系统中cpu的能力,不至于部分忙死,部分 饿死。io completion ports的目的就是平衡使用所有cpu的能力

另外,io completion ports是核心对象,使用模拟方式实现的性能估计都达不到系统提供的,比如双鱼座兄提到的http://www.codeproject.com/csharp/managediocp.asp,不过.net如此实现将更具兼容性,且估计性能上差不了多少

应该说iocp也不总是高效的代名词,在单cpu系统中,他和其他一些异步技术不会有太大差别,原因很简单,即使开n多线程,在某一时刻也只能有 一个线程占据cpu,多线程的好处体现不出多少,多线程的切换反而会加重系统的负担,于是iocp对于普通异步技术的优势也体现不出来了

前来领砖,各位多多赐予,表客气

btw:

@双鱼座:
“IOCP是Windows2000/NT特有的Socket模型”——iocp不仅仅可用于socket,一般的磁盘io也可使用

@sumtec:
此io completion非彼io completion ports,参看前述
  回复  引用    

#11楼  2006-01-05 16:42 Sumtec      

@问 题 男:
哦,没有注意。受教了:)   回复  引用  查看    

#12楼  2006-01-05 17:22 ccBoy      

Inside I/O completion ports
http://www.sysinternals.com/Information/IoCompletionPorts.html

其实IOCP对应到Linux平台就是Linux AIO,这个在Linux 2.5之后版本才完全支持,但好像还不完全支持socket,其实IOCP这种模式下相当于对应每一个客户请求使用一个系统的线程,并且使用异步IO的方 式。这种方式下你的线程切换时间非常的少,几乎不切换。

  回复  引用  查看    

#13楼  2006-01-05 17:28 Sumtec      

@ 所有人:

大家别忙活了,我的记忆还是没有什么大问题。刚才搜了一片文章,微软的够权威了,确实是用的IoCompletionPort(除非微软撒谎),前提是操作系统支持:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/progthrepool.asp

请大家注意下面这段话:
 
CompletionPortThreads
This kind of thread is used for I/O operations, whenever is possible. Windows NT, Windows 2000, and Windows XP offer an object specialized on asynchronous operations, called IOCompletionPort. With the API associated with this object we can launch asynchronous I/O operations managed with a thread pool by the system, in an efficient way and with few resources. However, Windows 95, Windows 98, and Windows Me have some limitations with asynchronous I/O operations. For example, IOCompletionPorts functionality is not offered and asynchronous operations on some devices, such as disks and mail slots, cannot be performed. Here you can see one of the greatest features of the .NET Framework: compile once and execute on multiple systems. Depending on the target platform, the .NET Framework will decide to use the IOCompletionPorts API or not, maximizing the performance and minimizing the resources.


If you run this program on Microsoft Windows NT, Windows 2000, or Windows XP, you will see the following output:

Connected to localhost:80
WorkerThreads: 24, CompletionPortThreads: 25
Request sent to localhost:80
WorkerThreads: 25, CompletionPortThreads: 24

As you can see, connecting with a socket uses a worker thread, while sending the data uses a CompletionPort. This following sequence is followed:

  1. We get the local IP address and connect to it asynchronously.
  2. Socket performs the asynchronous connection on a worker thread, since Windows IOCompletionPorts cannot be used to establish connections on sockets.
  3. Once the connection is established, the Socket class calls the specified function ConnectCallback. This callback shows the number of available threads on the pool, this way we can see that it is being executed on a worker thread.
  4. An asynchronous request is sent from the same function ConnectCallback. We use for this the BeginSend method, after encoding the Get / request in ASCII code.
  5. Send/receive operations on a socket can be performed asynchronously with an IOCompletionPort, so when our request is done, the callback function SendCallback is executed on a CompletionPortthread. We can check this because the function itself shows the number of available threads and we can see that only those corresponding to CompletionPorts have been decreased.
If we run the same code on a Windows 95, Windows 98, or Windows Me platform, the result will be the same on the connection, but the request will be sent on a worker thread, instead of a CompletionPort. The important thing you should learn about this is that the Socket class always uses the best available mechanism, so you can develop your application without taking into account the target platform.
 
 
各位,可以盖棺定论了吗?
  回复  引用  查看    

#14楼  2006-01-05 18:46 ccBoy      

这种方式你不能根据CPU控制线程池中线程数的多少,你也不能控制并发的隔离等级。

关键是大家知道原理和优劣了,不要拘泥于某个结论:)这么说来,天生这样上面的四种分类和大概性能需xxx的结论要调整调整或重新思考了

还要好好再学习和细细体会,真是学无止境啊,Sumtec列的文章也看过,但是对文章中提的CompletionPort完全没有印象

Ps: Sumtec , 你记忆力真好,而且有信心保持自己的观点,赞一个。
  回复  引用  查看    

#15楼 [楼主] 2006-01-05 19:08 天生这样      

领教了:)

没想到这些日子来的疑惑今天都有了点头绪了。

性能上的问题,我最后也提到了,是一些资料的摘抄,我自己也没有试过。

但从原理和表层实现方式来上说,这些指标也是大概有个数的。

对于一般的程序设计,选择哪个模型也有一点意义的:)

  回复  引用  查看    

#16楼 10.171.16.* 2006-01-29 15:26 飞刀.Net [未注册用户]

我这几天安心看了一下完成端口方面的资料.

发现他的模型与Java中的nio差不多,应当nio的windows实现就是使用的完成端口.

我看了资料后发现,ThreadPool,Select在完成端口的实现中一个都没有少过,好像没有太新的东东(当然他上个世纪末就出现了),所以楼主对几种的分类,好像界限不对.

唯一的区别就是把用户态的线程调入了核心态,减少了可能的线程切换,更有利操作系统本身对CPU时间片的利用.

单就模型来说,在用户态中亦可实现,就像Sonic.Net一样以及java中的concurrent中线程池的实现.

其实楼上的争论似乎没有太大的意义,.Net中的线程似乎也是个本地线程(不像Java中是JVM的自身实现的线程),其本身的 ThreadPool就更不用说了,而且Windows的线程本身就是可以调入核心态的.从理论上说,用.net实现的IOCP确实可以获是和Win32 一样的性能,现实估计也差不了很多(Sonic.Net作者自己说的).

现在我想的问题的就是在Linux这样的一个分时系统之下,需要如何完成类似完成端口的东东,如何减少线程(进程)的切换?
ccBoy提供了一个aio,我不太清楚,我呆会儿查查.

不知道大过年的,有没有人还有兴趣和我讨论这些?   回复  引用    

#17楼  2006-01-31 14:12 neoragex2002      

to 飞刀.Net:
其实这个帖子所讨论的问题可以从平台实现机制和网络I/O模式两方面来看,尽管在linux/win32下具体机制略为不同,各有局限,但依然存 在着借助模式设计实现"跨平台完成端口"的可能,比如说ACE中的前摄器模式(proactor)。我想ACE就是你要找的东西。
  回复  引用  查看    

#18楼 219.133.154.* 2006-06-23 17:36 baoli [未注册用户]

终于在这里找到异步是使用iocp的最后说法了,但是随之而来的一个问题是,为什么很多地方都说异步的性能比不上真正的iocp 呢?

另外这个c#下的一个iocp实现:
Sonic.net
http://www.codeproject.com/csharp/managediocp.asp
测试了一下,发现有一个比较严重的bug,就是在队列数大的时候,工作时间长了,就会发生只有一个线程在运行,而且线程处于等待的情况。在性能方面通过和使用api的GetQueuedCompletionStatus等iocp函数对比,也还是有点差距......

难道dotnet真的不可能达到iocp性能?   回复  引用    

#19楼  2006-06-30 18:22 让变化成为计划的一部分      

http://devauthority.com/blogs/krys/archive/2006/06/13/HighPerformanceSocket.aspx,
High Performance Socket in .NET - using IOCP Thread & more..

嘿嘿,就是用IOCP做的Socket。
Sonic.net 的问题比较严重,经常会发现,只有一个线程在那里狂转,其他的线程闲着。这个很不符合IOCP的原意。所以,Sonic.net 还只能算是试图模拟IOCP,但模拟的并不完美。   回复  引用  查看    

#20楼 219.134.70.* 2007-01-31 14:42 byjove [未注册用户]

Sonic.net 在pause 时,也不能恢复.....
  回复  引用    

#21楼 222.43.70.* 2007-06-28 14:29 zzq [未注册用户]

这么说来根本就没有什么意义!   回复  引用    

posted on 2008-08-17 16:23  风荷小筑  阅读(1984)  评论(0编辑  收藏  举报