|
Winsock五種I/O模型的性能分析 五種I/O模型的性能分析 重疊I/O模型的另外幾個(gè)優(yōu)點(diǎn)在于,微軟針對重疊I/O模型提供了一些特有的擴(kuò)展函數(shù)。當(dāng)使用重疊I/O模型時(shí),可以選擇使用不同的完成通知方式。 采用事件對象通知的重疊I/O模型是不可伸縮的,因?yàn)獒槍Πl(fā)出WSAWaitForMultipleEvents調(diào)用的每個(gè)線程,該I/O模型一次最多都只能支持6 4個(gè)套接字。假如想讓這個(gè)模型同時(shí)管理不止64個(gè)套接字,必須創(chuàng)建額外的工作者線程,以便等待更多的事件對象。因?yàn)椴僮飨到y(tǒng)同時(shí)能夠處理的事件對象是有限的,所以基于事件對象的I/O模型不具備伸縮性。 使用完成例程通知的重疊I/O模型,因?yàn)橐韵聨讉€(gè)原因,也不是開發(fā)高性能服務(wù)器的最佳選擇。首先,許多擴(kuò)展功能不允許使用APC(Asyncroneus Procedure Call,異步過程調(diào)用)完成通知。其次,由于APC在系統(tǒng)內(nèi)部特有的處理機(jī)制,應(yīng)用程序線程可能無限等待而得不到完成通知。當(dāng)一個(gè)線程處于“可警告狀態(tài)”時(shí),所有掛起的APC按照先進(jìn)先出的順序(FIFO)接受處理?,F(xiàn)在考慮這樣一種情況,服務(wù)器已經(jīng)建立起了一個(gè)連接,并且調(diào)用含有完成例程指針的WSARecv投遞了一個(gè)重疊I/O請求。當(dāng)有數(shù)據(jù)到達(dá)時(shí)(即I/O完成時(shí)),完成例程執(zhí)行并且再次調(diào)用WSARecv拋出另外一個(gè)重疊I/O請求。一個(gè)APC拋出的I/O操作需要一定的時(shí)間才能完成,所以這期間可能另外一個(gè)完成例程等待執(zhí)行(比如本次WSARecv還沒接收完時(shí),又有一個(gè)新的客戶接入并發(fā)來數(shù)據(jù)),因?yàn)檫€有更多的數(shù)據(jù)需要讀?。ㄉ弦粋€(gè)客戶發(fā)來的數(shù)據(jù)尚未讀完)。只要(投遞WSARecv的)那個(gè)套接字上還有“未決”(未接收完)的數(shù)據(jù),就會(huì)導(dǎo)致調(diào)用線程長久阻塞。 基于完成端口通知的重疊I/O模型是Windows NT系統(tǒng)提供的一個(gè)真正支持高伸縮性的I/O模型。在上一章中,探討了Winsock幾種常見的I/O模型,并且說明了當(dāng)應(yīng)對大規(guī)模客戶連接時(shí),完成端口是最佳的選擇,因?yàn)樗峁┝俗詈玫纳炜s性。 對不同Winsock I/O模型的性能測試結(jié)果如圖1所示。其中服務(wù)器采用Pentium 4 1.7 GHz Xeon的CPU,768M內(nèi)存;客戶端有3臺(tái)PC,配置分別是Pentium 2 233MHz ,128 MB 內(nèi)存,Pentium 2 350 MHz ,128 MB內(nèi)存,Itanium 733 MHz ,1 GB內(nèi)存。服務(wù)器、客戶端安裝的操作系統(tǒng)都是Windows XP。 ![]()
圖1 不同I/O模型的性能比較 1.分析圖表1提供的測試結(jié)果可知,在所用的I/O模型中,阻塞模式性能最差。這個(gè)測試程序中,服務(wù)器為每個(gè)客戶創(chuàng)建兩個(gè)線程:一個(gè)負(fù)責(zé)處理數(shù)據(jù)的接收,一個(gè)負(fù)責(zé)處理數(shù)據(jù)的發(fā)送。在多次測試中的共同問題就是,阻塞模式難以應(yīng)對大規(guī)模的客戶連接,因?yàn)樗趧?chuàng)建線程上耗費(fèi)了太多的系統(tǒng)資源。因此,服務(wù)器創(chuàng)建太多的線程后,再調(diào)用CreateThread函數(shù)時(shí),將返回ERROR_NOT_ENOUGH_MEMORY的錯(cuò)誤,這個(gè)錯(cuò)誤碼提示內(nèi)存不夠。那些發(fā)出連接請求的客戶則收到WSAECONNREFUSED的錯(cuò)誤提示,表示連接的嘗試被拒絕。 讓我們來看看監(jiān)聽函數(shù)listen,其原型如下: WINSOCK_API_LINKAGE int WSAAPI listen(SOCKET s, int backlog ); 參數(shù)一s已綁定了地址的監(jiān)聽套接字。 參數(shù)二backlog指定了正在等待連接的最大隊(duì)列長度。 參數(shù)backdog非常重要,?因?yàn)橥耆赡芡瑫r(shí)出現(xiàn)幾個(gè)對服務(wù)器的連接請求。例如,假定backlog參數(shù)為2時(shí)有三個(gè)客戶機(jī)同時(shí)發(fā)出連接請求,那么前兩個(gè)會(huì)被放在一個(gè)“等待處理”隊(duì)列中,以便應(yīng)用程序依次為它們提供服務(wù)。而第三個(gè)連接的請求就會(huì)造成一個(gè)WSAECONNREFUSED錯(cuò)誤。一旦服務(wù)器接受了一個(gè)連接請求,那個(gè)連接請求就會(huì)從隊(duì)列中刪去,以便可以繼續(xù)接收其他客戶發(fā)出的連接請求。即當(dāng)一個(gè)連接請求到來時(shí)隊(duì)列已滿,那么客戶將收到一個(gè)WSAECONNREFUSED錯(cuò)誤。而backlog參數(shù)本身的大小就存在著限制,這個(gè)限制是由協(xié)議提供者決定的。 故阻塞模式下,由于系統(tǒng)資源的限制,其并發(fā)處理量是極難突破的。 2.非阻塞模式表現(xiàn)出的性能要比阻塞模式稍好,但是占用了太多的CPU處理時(shí)間。測試服務(wù)器將所有客戶對應(yīng)的socket分類放到FD_SET集合中,然后調(diào)用select函數(shù)篩選出對應(yīng)集合中有事件發(fā)生的socket,并對集合更新。接下來調(diào)用FD_ISSET宏重新判斷一個(gè)套接字是否在原來加入的FD_SET集合中。隨著客戶連接數(shù)量的增多,這種模型的局限性逐漸凸現(xiàn)。僅僅為了判斷一個(gè)套接字是否有網(wǎng)絡(luò)事件發(fā)生,就需要對集合FD_SET執(zhí)行一次遍歷!使用迭代搜索來對select更新的FD_SET進(jìn)行掃描,性能可以得到一些提升。瓶頸在于,服務(wù)器必須能夠很快地掃描出FD_SET集合中的有網(wǎng)絡(luò)事件發(fā)生的套接字的相關(guān)信息。針對這個(gè)問題,可以使用更復(fù)雜的掃描算法,如哈希搜索,它的效率是極高的。還需要注意的一個(gè)問題就是,非分頁池(即直接在物理內(nèi)存中分配的內(nèi)存)的使用極高。這是因?yàn)锳FD(Ancillary Function Driver,由afd.sys提供的支持Windows Sockets應(yīng)用程序的底層驅(qū)動(dòng)程序,其中運(yùn)行在內(nèi)核模式下afd.sys驅(qū)動(dòng)程序主要管理Winsock TCP/IP通信)和TCP都將使用I/O緩存,因?yàn)榉?wù)器讀取數(shù)據(jù)的速度是有限的,相對于CPU的處理速度而言,I/O基本是零字節(jié)的吞吐量。 3.基于Windows消息機(jī)制的WSAAsyncSelect模型能夠處理一定的客戶連接量,但是擴(kuò)展性也不是很好。因?yàn)橄⒈煤芸炀蜁?huì)阻塞,降低了消息處理的速度。在幾次測試中,服務(wù)器只能處理大約1/3的客戶端連接。過多的客戶端連接請求都將返回錯(cuò)誤提示碼WSAECONNREFUSED,說明服務(wù)器不能及時(shí)處理FD_ACCEPT消息導(dǎo)致連接失敗,這樣監(jiān)聽隊(duì)列中待處理的連接請求不致于爆滿。然而,通過上表中的數(shù)據(jù)可以發(fā)現(xiàn),對那些已經(jīng)建立的連接,其平均吞吐量也是極低的(即使對于那些對比特率進(jìn)行了限制的客戶也如此)。 4.基于事件通知的WSAEventSelect模型表現(xiàn)得出奇的不錯(cuò)。在所有的測試中,大多數(shù)時(shí)候,服務(wù)器基本能夠處理所有的客戶連接,并且保持著較高的數(shù)據(jù)吞吐量。這種模型的缺點(diǎn)是,每當(dāng)有一個(gè)新連接時(shí),需要?jiǎng)討B(tài)管理線程池,因?yàn)槊總€(gè)線程只能夠等待64個(gè)事件對象。當(dāng)客戶連接量超過64個(gè)后再有新客戶接入時(shí),需要?jiǎng)?chuàng)建新的線程。在最后一次測試中,建立起了超過45,000個(gè)的客戶連接后,系統(tǒng)響應(yīng)速度變得非常緩慢。這時(shí)由于為處理大規(guī)模的客戶連接創(chuàng)建了大量的線程,占用了過多的系統(tǒng)資源。791個(gè)線程基本達(dá)到了極限,服務(wù)器不能再接受更多的連接了,原因是WSAENOBUFS:無可用的緩沖區(qū)空間,套接字無法創(chuàng)建。另外,客戶端程序也達(dá)到了極限,不能維持已經(jīng)建立的連接。 使用事件通知的重疊I/O模型和WSAEventSelect模型在伸縮性上差不多。這兩種模型都依賴于等待事件通知的線程池,處理客戶通信時(shí),大量線程上下文的切換是它們共同的制約因素。重疊I/O模型和WSAEventSelect模型的測試結(jié)果很相似,都表現(xiàn)得不錯(cuò),直到線程數(shù)量超過極限。 5.最后是針對基于完成端口通知的重疊I/O模型的性能測試,由上表中數(shù)據(jù)可以看出,它是所有I/O模型中性能最佳的。內(nèi)存使用率(包括用戶分頁池和非分頁池)和支持的客戶連接量與基于事件通知的重疊I/O模型和WSAEventSelect模型基本相同。真正不同的地方,在于對CPU的占用。完成端口模型只占用了60%的CPU,但是在維持同樣規(guī)模的連接量時(shí),另外兩種模型(基于事件通知的重疊I/O模型和WSAEventSelect模型)占用更多的CPU。完成端口的另外一個(gè)明顯的優(yōu)勢是,它維持更大的吞吐量。 對以上各種模型進(jìn)行分析后,可以會(huì)發(fā)現(xiàn)客戶端與服務(wù)器數(shù)據(jù)通信機(jī)制本身存在的缺陷是一個(gè)瓶頸。在以上測試中,服務(wù)器被設(shè)計(jì)成只做簡單的回應(yīng),即只是將客戶端發(fā)送過來的數(shù)據(jù)發(fā)送回去??蛻舳耍词褂斜忍芈氏拗疲┎煌5陌l(fā)送數(shù)據(jù)給服務(wù)器,這導(dǎo)致大量數(shù)據(jù)阻塞在服務(wù)器上與這個(gè)客戶端對應(yīng)的套接字上(無論是TCP緩沖區(qū)還是AFD的單套接字緩沖區(qū),它們都是在非分頁池上)。在最后三種性能比較好的模型中,同一時(shí)間只能執(zhí)行一個(gè)接受輸入操作,這意味著在大多數(shù)時(shí)間,還是有很多數(shù)據(jù)處于“未決”狀態(tài)??梢孕薷姆?wù)器程序使其以異步方式接受數(shù)據(jù),這樣一旦有數(shù)據(jù)達(dá)到,需要將數(shù)據(jù)緩存起來。這種方案的缺點(diǎn)是,當(dāng)一個(gè)客戶連續(xù)發(fā)送數(shù)據(jù)時(shí),異步接受到了大量的數(shù)據(jù)。這會(huì)導(dǎo)致其他的客戶無法接入,因?yàn)檎{(diào)用線程和工作者線程都不能處理其他的事件或完成通知。通常情況下,調(diào)用非阻塞異步接收函數(shù),先返回WSAEWOULDBLOCK,然后數(shù)據(jù)間斷性的傳輸,而不采取連續(xù)接收的方式。 從以上測試結(jié)果,可以看出WSAEventSelect模型和重疊I/O模型是性能表現(xiàn)最佳的。兩種基于事件通知的模型中,創(chuàng)建線程池來等待事件完成通知并作后續(xù)處理是很繁瑣的,但是并不影響以它們來架構(gòu)中型服務(wù)器的良好性能。當(dāng)線程的數(shù)量隨著客戶端連接數(shù)量而逐增時(shí),CPU將花費(fèi)大量時(shí)間在線程的上下文切換上,這將影響服務(wù)器的伸縮性,因?yàn)檫B接量達(dá)到一定數(shù)量后,便飽和了。完成端口模型提供了最佳的可擴(kuò)展性,因?yàn)镃PU使用率低,其支持的客戶連接量相對其他模型最多。 I/O模型的選擇 通過上一節(jié)對各種模型的測試分析,對于如何挑選最適合自己應(yīng)用程序的I/O模型已經(jīng)很明晰了。同開發(fā)一個(gè)簡單的運(yùn)行多線程的鎖定模式應(yīng)用相比,其他每種I/O模型都需要更為復(fù)雜的編程工作。因此,針對客戶機(jī)和服務(wù)器應(yīng)用開發(fā)模型的選擇,有以下原則。 1. 客戶端 若打算開發(fā)一個(gè)客戶機(jī)應(yīng)用,令其同時(shí)管理一個(gè)或多個(gè)套接字,那么建議采用重疊I/O或WSAEventSelect模型,以便在一定程度上提升性能。然而,假如開發(fā)的是一個(gè)以Windows為基礎(chǔ)的應(yīng)用程序,要進(jìn)行窗口消息的管理,那么WSAAsyncSelect模型恐怕是一種最好的選擇,因?yàn)閃SAAsyncSelect本身便是從Windows消息模型借鑒來的。采用這種模型,程序需具備消息處理功能。 2. 服務(wù)器端 若開發(fā)的是一個(gè)服務(wù)器應(yīng)用,要在一個(gè)給定的時(shí)間,同時(shí)控制多個(gè)套接字,建議采用重疊I/O模型,這同樣是從性能角度考慮的。但是,如果服務(wù)器在任何給定的時(shí)間,都會(huì)為大量I/O請求提供服務(wù),便應(yīng)考慮使用I/O完成端口模型,從而獲得更佳的性能。 說明: 本文主要譯自《Network programming for microsoft windows》一書的6.4節(jié)《服務(wù)器策略》。 |
|
|