摘要
本文通過實驗測試對Linux、Solaris (for Intel)、FreeBSD和Windows 2000在運行高效能網路應用程式方面的速度進行了比較。描述了如何根據需要從您的軟體供應商那裡獲得相應效能和軟體體繫結構的產品,解釋了每個設計是如何產生不同的效能特性的,並且最終確定了對於每個通用的網路程式設計來說哪個作業系統最為合適。我們通過模擬和真實環境測試提出了作業系統基準,並對相應結果給出了評價。
我們發現,與其所依賴的作業系統相比較,軟體應用程式的體繫結構是決定其執行速度的更主要因素。我們的基準程式(benchmarks)證明,基於進程的體繫結構與非同步任務體繫結構之間會有12倍的效能差異。值得注意的是,即使使用最有效非同步體繫結構,不同作業系統間的效能差異也會相差75%以上。疑懼我們的度量基準,我們發現Linux是執行效能最好的作業系統,它比排在第二位的Solaris的效能高35%,然後才是Windows,排在最後的是FreeBSD。
背景
在Lyris技術公司,我們的任務是編寫高效能、跨平台、基於email的應用程式。眾所周知,更好的應用程式效能是決定競爭優勢的關鍵因素,所以我們花了大量的時間在影響應用程式效能的所有因素(軟體、硬體和作業系統)方面進行調整。我們的客戶經常問的一個問題是哪個作業系統最適合運行我們的軟體。另一種情況是,他們已經選擇了一個作業系統,他們會問怎樣才能讓他們的系統更快地運行我們的應用程式。另外,我們運營了一個主機託管(hosting)部門,我們希望在為我們的託管客戶提供最好效能的前提下盡量降低我們的硬體成本。
大多互連網應用程式都是按照下面的步驟進行工作的:
接受一個TCP/IP串連請求或者與另外一台電腦建立一個串連;
一旦建立了串連,通過TCP/IP 交換各種各樣基於文本的命令;
這些命令會引發不同的行為,如讀磁碟(例如瀏覽網頁)、寫磁碟(例如將一條接收到的電子郵件訊息進行排隊)或者調用外部功能(例如郵件過濾、反向DNS尋找等)。
通常,一個網路應用程式涉及的效能問題包括:
用儘可能快的速度完成大量的並發任務;
有效地處理大量的等待(由慢速的TCP/IP串連引起或者等待串連的另一端發送下一個命令);
有效地執行TCP/IP操作。
為了設計最佳效能的網路應用程式,應用程式的軟體設計人員必需選擇一個充分考慮了上面的效能準則的軟體體繫結構。兩個最主要的因素是任務體繫結構和TCP/IP調用體繫結構。
任務體繫結構
在任務體繫結構領域,有三個主要的技術:
每個任務一個進程(面向進程的)——運行程式的很多複本,每個複本每次只處理一個任務。有時候,每次建立一個新任務都需要相應地建立一個新的進程(如inetd、Sendmail),但有些也設計成可以重用進程(如Apache)。這種體繫結構在低負載的情況下會產生很好的效能。在運行中等程度的負載時,如果進程影象比較小(如qmail)、已經實現了針對該應用程式的效率改進或者該類型的應用程式不會建立太多的同時任務時,其效能也還可以。如果採用了進程緩衝技術並且同時啟動並執行進程總數不是很多(如中低程度的負載),就可以採用多CPU來解決問題。這種技術在所有的作業系統上都可以實現,然而,從實現的的角度講,UNIX明顯地要比Windows更有效。(Windows沒有fork()系統調用,很少有Windows應用程式採用這種技術,因為它在Windows上實在太慢了)。
每個任務一個線程(多線程)——只運行該程式的一個複本,在這個複本中,每個任務都由一個單獨的執行線程來處理。多線程應用程式在低-中負載情況下的效能非常好,在更高的負載下,其效能就明顯下降了(但通常也是可以接受的)。然而,超高負載會將多線程應用程式帶入死亡旋渦(death-spiral)。通常情況下,多線程應用程式最多可以處理500到1000個並發的任務。這在大多情況下是可以接受的。每個新認為使用一個新的線程,相對於新進程而言,新線程消耗更少的記憶體和更少的CPU處理能力。僅僅最流行的UNIX變種(通常是商用作業系統)才能夠在沉重的多線程負載下保持穩定,所以很少有開放源碼工程採用多線程技術。這種技術在多CPU上的效能可能比在單CPU上的效能還要差,因為多處理器電腦上的訊號量(semaphore)鎖處理的代價非常高。(多線程軟體的例子有Netscape Web server和Apache on Windows)
一個線程處理多個任務(非同步處理)——程式的一個複本運行固定數量的線程(通常每種類型的任務對應一個線程),每個線程通過一種稱為非同步(非阻塞)TCP/IP的技術處理大量的同類型任務。因為大多數程式根本不需要處理高負載,並且非同步編程非常困難,所以很少有支援這種技術的程式出現。非同步程式可以在多CPU電腦上平滑擴充,因為它們大多採用彼此獨立的長效線程。這種技術很少需要跨CPU的鎖,因此每個線程都可以永久且有效地分配給一個單獨的CPU(如DNS BIND守侯進程daemon)
TCP/IP調用體繫結構
第二個影響效能的主要因素是TCP/IP調用體繫結構。在作業系統級,有多種方法可以實現相同的網路操作。TCP/IP的速度與最佳化程式設計之間存在著權衡的問題(更快的技術對編程人員來說意味著更多的工作)。加之,更快的技術並不是對所有的平台都可用;更高的效能需求可能會限制平台選擇的自由度。
阻塞式TCP/IP調用
阻塞式TCP/IP調用等待所有被請求的操作完成後立即對結果進行操作。在任務數量比較少時,操作結果會在事件發生時立即予以響應;但在任務數量比較大的情況下,會導致作業系統的巨量環境切換系統開銷,這時的效率相當低。在低負載的情況下,阻塞(同步)TCP/IP調用產生很少的等待時間,這對低負載的Web伺服器一類的應用來說非常理想(頁面響應非常迅速,條件是負載永遠不會太高)。然而,如果使用的是面向進程的體繫結構,且每一個新的網路連接都要建立一個相應的新進程(如inetd)時,阻塞TCP/IP所帶來的等待時間上的效能就會被運行一個新進程所導致的顯著的系統調用迅速抵消。
非阻塞TCP/IP調用
非阻塞(非同步)TCP/IP調用發起一個操作後就轉而進行其它的工作。當操作完成或者發生了一個事件,這個進程就會獲得通知並進行相應的響應。這種兩不處理模式需要更多的編程工作,有時響應新事件會花費少量的時間(增加了等待延時)。在中-高負載的情況下,這種非阻塞技術會產生更好的執行效能,並且可以避免討厭的高負荷,但同阻塞TCP/IP調用相比,等待延時可能稍微長了點兒。
上述的每一個任務處理體繫結構都與相應的TCP/IP系統調用模型相匹配。面向進程和多線程的程式可能會趨向於採用阻塞TCP/IP調用,因為這是編程的最簡單方法並且大多數情況下只需要處理低負載。然而採用非同步任務體繫結構的應用程式就必需使用非阻塞的TCP/IP操作以處理多任務了:根本不能選擇阻塞TCP/IP。因此,如果您發現一個網路應用程式採用了高度可擴充的非同步任務體繫結構,您當然也是從其所採用的最具擴充性的TCP/IP調用體繫結構(非阻塞)上獲得了不少好處。
真實環境測試
為了評估不同作業系統和網路應用程式的效能,我們進行了三類不同的測試:真實環境、磁碟I/O和任務體繫結構比較。我們檢測的作業系統包括Linux (Red Hat 7.0, kernel 2.2.16-22)、Solaris 2.8 for Intel、FreeBSD 4.2和Windows 2000 Server。這些作業系統都是可獲得的商業版本中最新的,並且沒有重新編譯(或者說我們將作業系統軟體開包安裝後就進行了測試)。我們在相同的4-GB SCSI-3磁碟機上(IBM 型號是 DCAS-34330)安裝了上述作業系統,並且在相同的機器(ASUS P3B主板、Intel Pentium III 550-MHz處理器、384-MB SDRAM、Adaptec 2940UW SCSI控制器、ATI Rage Pro 3D顯卡、Intel EtherExpress Pro 10/100 Ethernet網卡)上進行測試。
我們採用測試我們自己開發的MailEngine軟體的郵件發送速度來進行真實環境測試。MailEngine是一個郵件發送伺服器,它可以移植到所有要進行測試的平台上(其實還包括Sparc上的Solaris),它採用的是非同步體繫結構(帶有採用poll()系統調用的非阻塞TCP/IP)。為了不將電子郵件真的發送到具有200,000個成員的測試清單,我們是在測試模式下啟動並執行MailEngine。在這種模式下,MailEngine實現發送郵件的所有步驟但是最後使用RSET命令而不是DATA命令。這樣就在真正發送資料之前退出QUIT了SMTP串連,這樣就不會向接收者發送任何電子郵件了。我們的工作量是將一條訊息發送到分布在9113個域下的200,000個電子郵件地址。因為是同一條訊息在記憶體中為所有的接收者進行排隊,所以磁碟I/O並不是影響效能的主要因素。我們緩慢地提高同時串連的數量,以便觀察負載的提升對效能的影響。
圖1(作業系統的比較)顯示了在MailEngine的測試模式中,不同作業系統上一定範圍的同時串連數量下的電子郵件傳輸速度的測試結果。Linux在速度上佔有明顯的優勢,它比排在第二位的Solaris快約35%。總體上來說,效能隨著串連數量的增加而提高,這張圖也顯示速度的增長邊際超過1500個串連。FreeBSD在加到多於1500個串連時的效能會有些下降。
在UNIX風格的作業系統上,需要適當的調整核心以便允許在一個進程中使用如此多的串連。除核心調整外,當負載超過2500個串連時,FreeBSD還會給出資源不足的警告並且停止運行。
檔案系統測試
許多網路應用程式同樣需要將資訊在硬碟上進行排隊以便以後處理(如Sendmail的郵件隊列)或處理溢出情況的能力。為了模仿在典型情況檔案系統的效率,我們寫了一個在單個目錄下建立、寫入和讀回10,000個檔案的C++程式,它每次只操作一個檔案。為了全面測試檔案系統對不同種類檔案的整體效率,測試檔案的大小從4 KB到128 KB遞增。
圖2 建立、寫和讀10,000個檔案所需要的時間
圖2顯示了檔案系統的測試結果。Linux和Windows的速度基本相同,它們都比另外兩個明顯地快得多:是FreeBSD的6倍、Solaris的10倍。每個作業系統所使用的檔案系統分別是:Linux - EXT2, Solaris - UFS, Windows 2000 - NTFS, FreeBSD - UFS。其它的檔案系統無疑將會產生不同的效能結果。如果您的軟體應用程式嚴重依賴於磁碟,我建議您使用Linux或Windows或者其它運行於FreeBSD或Solaris上的替代檔案系統。
應用程式體繫結構測試
最後,我們不同的網路應用程式體繫結構在每一作業系統上的表現進行了測試。我們寫了一個簡單的C++服務程式,它對每一個串連請求都給出一個"450 too busy"的響應訊息。我們程式測試的三個體繫結構是:(1)基於進程的體繫結構,對每個串連都產生一個新的進程予以處理;(2)多線程體繫結構,為每個進程分配一個線程;(3)非同步體繫結構,所有串連都使用非阻塞的TCP/IP予以應答。一個獨立的C++程式運行在另外一台電腦(使用Linux作業系統)上,它試圖以最快的速度串連我們的伺服器,緩慢增加同時的串連負載並且計算成功接收的響應資訊。對於本文來說,這麼多的測試結果圖(12個)顯然太多了,因此我們繪製了每個任務體繫結構的平均值以便顯示大體上的效能差異。
圖3 每種網路結構的平均輸送量
圖3顯示了每種類型的任務體繫結構在所有作業系統平台上的平均效能。雖然不同平台上效能的差異是非常明顯的,但其顯著性與體繫結構的選擇所造成的顯著性差異還是相去甚遠。最慢的網路應用體繫結構是基於進程的結構,它對串連的處理能力只有非同步方法呼叫的5%。在1000個同時串連的情況下,同基於線程的方法相比,非同步方法呼叫的負載能力要高出35%。趨勢線顯示,隨著負載的增長,多線程方法與非同步方法呼叫之間的效能差異將拉大。
為高效能應用進行核心調整
在預設配置中,我們測試的UNIX風格的作業系統並不支援多線程和非同步程式所需要的如此大量的同時TCP/IP串連。這種限制嚴重地限制了應用程式的效能,甚至錯誤地規勸系統管理員不要採用這些高效能體繫結構。值得慶幸的是,這些限制可以容易地通過核心調整來克服。在UNIX上,每個TCP/IP串連使用一個檔案描述符,所以您必需提高作業系統上可以提供的檔案描述符的總量,同時也要提高每個進程允許使用的描述符的最大值。所有的UNIX風格的作業系統都有一個ulimit的shell命令(sh和bash),這個命令可以讓使用它進行了適當的核心調整之後的shell上啟動並執行命令可以開啟更多的檔案描述符。我們建議使用ulimit -n 8192。這裡是我們推薦的核心調整過程:
在Linux上: echo 65536 > /proc/sys/fs/file-max改變系統可提供的檔案描述符的數量。
在FreeBSD上:將下面這些內容追加到 /etc/sysctl 檔案的末尾(或者,您可以使用 sysctl -w 命令來填加這些內容):
kern.maxfiles=65536
kern.maxfilesperproc=32768
在Solaris上:將下面的內容加入 /etc/system 檔案後reboot:
set rlim_fd_max=0x8000
set rlim_fd_cur=0x8000
提要
我們的真實環境測試顯示,作業系統最好與最差的效能差距達到了75%,Linux擁有比排在第二位的Solaris還要高出35%的卓越效能。更重要的是,非同步應用程式比基於進程的應用程式平均快12倍,比多線程的應用程式快35%。如果磁碟I/O佔用了應用程式已耗用時間的主要部分,那麼在Linux和Windows 2000上的磁碟I/O任務的處理速度要比Solaris快10倍以上,比FreeBSD也要快出6倍之多!
如果您正在評估一個網路應用軟體,並且它的最終效能對您來說至關重要,那麼軟體的體繫結構將是一個關鍵的評估準則(或者說,你應該選擇多線程或非同步結構)。