天天看點

Winsock五種I/O模型的性能分析

五種I/O模型的性能分析

重疊I/O模型的另外幾個優點在于,微軟針對重疊I/O模型提供了一些特有的擴充函數。當使用重疊I/O模型時,可以選擇使用不同的完成通知方式。

采用事件對象通知的重疊I/O模型是不可伸縮的,因為針對發出WSAWaitForMultipleEvents調用的每個線程,該I/O模型一次最多都隻能支援6 4個套接字。假如想讓這個模型同時管理不止64個套接字,必須建立額外的工作者線程,以便等待更多的事件對象。因為作業系統同時能夠處理的事件對象是有限的,是以基于事件對象的I/O模型不具備伸縮性。

使用完成例程通知的重疊I/O模型,因為以下幾個原因,也不是開發高性能伺服器的最佳選擇。首先,許多擴充功能不允許使用APC(Asyncroneus Procedure Call,異步過程調用)完成通知。其次,由于APC在系統内部特有的處理機制,應用程式線程可能無限等待而得不到完成通知。當一個線程處于“可警告狀态”時,所有挂起的APC按照先進先出的順序(FIFO)接受處理。現在考慮這樣一種情況,伺服器已經建立起了一個連接配接,并且調用含有完成例程指針的WSARecv投遞了一個重疊I/O請求。當有資料到達時(即I/O完成時),完成例程執行并且再次調用WSARecv抛出另外一個重疊I/O請求。一個APC抛出的I/O操作需要一定的時間才能完成,是以這期間可能另外一個完成例程等待執行(比如本次WSARecv還沒接收完時,又有一個新的客戶接入并發來資料),因為還有更多的資料需要讀取(上一個客戶發來的資料尚未讀完)。隻要(投遞WSARecv的)那個套接字上還有“未決”(未接收完)的資料,就會導緻調用線程長久阻塞。

基于完成端口通知的重疊I/O模型是Windows NT系統提供的一個真正支援高伸縮性的I/O模型。在上一章中,探讨了Winsock幾種常見的I/O模型,并且說明了當應對大規模客戶連接配接時,完成端口是最佳的選擇,因為它提供了最好的伸縮性。

對不同Winsock I/O模型的性能測試結果如圖1所示。其中伺服器采用Pentium 4 1.7 GHz Xeon的CPU,768M記憶體;用戶端有3台PC,配置分别是Pentium 2 233MHz ,128 MB 記憶體,Pentium 2 350 MHz ,128 MB記憶體,Itanium 733 MHz ,1 GB記憶體。伺服器、用戶端安裝的作業系統都是Windows XP。

Winsock五種I/O模型的性能分析

圖1 不同I/O模型的性能比較

1.分析圖表1提供的測試結果可知,在所用的I/O模型中,阻塞模式性能最差。這個測試程式中,伺服器為每個客戶建立兩個線程:一個負責處理資料的接收,一個負責處理資料的發送。在多次測試中的共同問題就是,阻塞模式難以應對大規模的客戶連接配接,因為它在建立線程上耗費了太多的系統資源。是以,伺服器建立太多的線程後,再調用CreateThread函數時,将傳回ERROR_NOT_ENOUGH_MEMORY的錯誤,這個錯誤碼提示記憶體不夠。那些發出連接配接請求的客戶則收到WSAECONNREFUSED的錯誤提示,表示連接配接的嘗試被拒絕。

讓我們來看看監聽函數listen,其原型如下:

WINSOCK_API_LINKAGE int WSAAPI listen(SOCKET s, int backlog );

參數一s已綁定了位址的監聽套接字。

參數二backlog指定了正在等待連接配接的最大隊列長度。

參數backdog非常重要, 因為完全可能同時出現幾個對伺服器的連接配接請求。例如,假定backlog參數為2時有三個客戶機同時發出連接配接請求,那麼前兩個會被放在一個“等待處理”隊列中,以便應用程式依次為它們提供服務。而第三個連接配接的請求就會造成一個WSAECONNREFUSED錯誤。一旦伺服器接受了一個連接配接請求,那個連接配接請求就會從隊列中删去,以便可以繼續接收其他客戶發出的連接配接請求。即當一個連接配接請求到來時隊列已滿,那麼客戶将收到一個WSAECONNREFUSED錯誤。而backlog參數本身的大小就存在着限制,這個限制是由協定提供者決定的。

故阻塞模式下,由于系統資源的限制,其并發處理量是極難突破的。

2.非阻塞模式表現出的性能要比阻塞模式稍好,但是占用了太多的CPU處理時間。測試伺服器将所有客戶對應的socket分類放到FD_SET集合中,然後調用select函數篩選出對應集合中有事件發生的socket,并對集合更新。接下來調用FD_ISSET宏重新判斷一個套接字是否在原來加入的FD_SET集合中。随着客戶連接配接數量的增多,這種模型的局限性逐漸凸現。僅僅為了判斷一個套接字是否有網絡事件發生,就需要對集合FD_SET執行一次周遊!使用疊代搜尋來對select更新的FD_SET進行掃描,性能可以得到一些提升。瓶頸在于,伺服器必須能夠很快地掃描出FD_SET集合中的有網絡事件發生的套接字的相關資訊。針對這個問題,可以使用更複雜的掃描算法,如哈希搜尋,它的效率是極高的。還需要注意的一個問題就是,非分頁池(即直接在實體記憶體中配置設定的記憶體)的使用極高。這是因為AFD(Ancillary Function Driver,由afd.sys提供的支援Windows Sockets應用程式的底層驅動程式,其中運作在核心模式下afd.sys驅動程式主要管理Winsock TCP/IP通信)和TCP都将使用I/O緩存,因為伺服器讀取資料的速度是有限的,相對于CPU的處理速度而言,I/O基本是零位元組的吞吐量。

3.基于Windows消息機制的WSAAsyncSelect模型能夠處理一定的客戶連接配接量,但是擴充性也不是很好。因為消息泵很快就會阻塞,降低了消息處理的速度。在幾次測試中,伺服器隻能處理大約1/3的用戶端連接配接。過多的用戶端連接配接請求都将傳回錯誤提示碼WSAECONNREFUSED,說明伺服器不能及時處理FD_ACCEPT消息導緻連接配接失敗,這樣監聽隊列中待處理的連接配接請求不緻于爆滿。然而,通過上表中的資料可以發現,對那些已經建立的連接配接,其平均吞吐量也是極低的(即使對于那些對比特率進行了限制的客戶也如此)。

4.基于事件通知的WSAEventSelect模型表現得出奇的不錯。在所有的測試中,大多數時候,伺服器基本能夠處理所有的客戶連接配接,并且保持着較高的資料吞吐量。這種模型的缺點是,每當有一個新連接配接時,需要動态管理線程池,因為每個線程隻能夠等待64個事件對象。當客戶連接配接量超過64個後再有新客戶接入時,需要建立新的線程。在最後一次測試中,建立起了超過45,000個的客戶連接配接後,系統響應速度變得非常緩慢。這時由于為處理大規模的客戶連接配接建立了大量的線程,占用了過多的系統資源。791個線程基本達到了極限,伺服器不能再接受更多的連接配接了,原因是WSAENOBUFS:無可用的緩沖區空間,套接字無法建立。另外,用戶端程式也達到了極限,不能維持已經建立的連接配接。

使用事件通知的重疊I/O模型和WSAEventSelect模型在伸縮性上差不多。這兩種模型都依賴于等待事件通知的線程池,處理客戶通信時,大量線程上下文的切換是它們共同的制約因素。重疊I/O模型和WSAEventSelect模型的測試結果很相似,都表現得不錯,直到線程數量超過極限。

5.最後是針對基于完成端口通知的重疊I/O模型的性能測試,由上表中資料可以看出,它是所有I/O模型中性能最佳的。記憶體使用率(包括使用者分頁池和非分頁池)和支援的客戶連接配接量與基于事件通知的重疊I/O模型和WSAEventSelect模型基本相同。真正不同的地方,在于對CPU的占用。完成端口模型隻占用了60%的CPU,但是在維持同樣規模的連接配接量時,另外兩種模型(基于事件通知的重疊I/O模型和WSAEventSelect模型)占用更多的CPU。完成端口的另外一個明顯的優勢是,它維持更大的吞吐量。

對以上各種模型進行分析後,可以會發現用戶端與伺服器資料通信機制本身存在的缺陷是一個瓶頸。在以上測試中,伺服器被設計成隻做簡單的回應,即隻是将用戶端發送過來的資料發送回去。用戶端(即使有比特率限制)不停的發送資料給伺服器,這導緻大量資料阻塞在伺服器上與這個用戶端對應的套接字上(無論是TCP緩沖區還是AFD的單套接字緩沖區,它們都是在非分頁池上)。在最後三種性能比較好的模型中,同一時間隻能執行一個接受輸入操作,這意味着在大多數時間,還是有很多資料處于“未決”狀态。可以修改伺服器程式使其以異步方式接受資料,這樣一旦有資料達到,需要将資料緩存起來。這種方案的缺點是,當一個客戶連續發送資料時,異步接受到了大量的資料。這會導緻其他的客戶無法接入,因為調用線程和工作者線程都不能處理其他的事件或完成通知。通常情況下,調用非阻塞異步接收函數,先傳回WSAEWOULDBLOCK,然後資料間斷性的傳輸,而不采取連續接收的方式。

從以上測試結果,可以看出WSAEventSelect模型和重疊I/O模型是性能表現最佳的。兩種基于事件通知的模型中,建立線程池來等待事件完成通知并作後續處理是很繁瑣的,但是并不影響以它們來架構中型伺服器的良好性能。當線程的數量随着用戶端連接配接數量而逐增時,CPU将花費大量時間線上程的上下文切換上,這将影響伺服器的伸縮性,因為連接配接量達到一定數量後,便飽和了。完成端口模型提供了最佳的可擴充性,因為CPU使用率低,其支援的客戶連接配接量相對其他模型最多。

     I/O模型的選擇

通過上一節對各種模型的測試分析,對于如何挑選最适合自己應用程式的I/O模型已經很明晰了。同開發一個簡單的運作多線程的鎖定模式應用相比,其他每種I/O模型都需要更為複雜的程式設計工作。是以,針對客戶機和伺服器應用開發模型的選擇,有以下原則。

1. 用戶端

若打算開發一個客戶機應用,令其同時管理一個或多個套接字,那麼建議采用重疊I/O或WSAEventSelect模型,以便在一定程度上提升性能。然而,假如開發的是一個以Windows為基礎的應用程式,要進行視窗消息的管理,那麼WSAAsyncSelect模型恐怕是一種最好的選擇,因為WSAAsyncSelect本身便是從Windows消息模型借鑒來的。采用這種模型,程式需具備消息處理功能。

2. 伺服器端

若開發的是一個伺服器應用,要在一個給定的時間,同時控制多個套接字,建議采用重疊I/O模型,這同樣是從性能角度考慮的。但是,如果伺服器在任何給定的時間,都會為大量I/O請求提供服務,便應考慮使用I/O完成端口模型,進而獲得更佳的性能。