趙玉龍,王 雷,王 歡
(北京航空航天大學(xué)計(jì)算機(jī)學(xué)院,北京100191)
操作系統(tǒng)的可伸縮性,是指操作系統(tǒng)隨著CPU核數(shù)的增加性能增長(zhǎng)的能力。如果隨著CPU核數(shù)的增加系統(tǒng)整體性能呈現(xiàn)線性增長(zhǎng)(理想狀況)或類(lèi)似線性增長(zhǎng),則系統(tǒng)的可伸縮性較好。反之,如果隨著CPU核數(shù)的增加,系統(tǒng)整體性能增長(zhǎng)趨勢(shì)緩慢,或者呈現(xiàn)降低趨勢(shì),則系統(tǒng)的可伸縮性不好。
在學(xué)術(shù)界和工業(yè)界研究類(lèi)Unix系統(tǒng)在共享內(nèi)存多處理器系統(tǒng)上的伸縮性已有很長(zhǎng)歷史。研究性項(xiàng)目如 Stanford FLASH[1]以及 SGI、IBM、Sun等公司都已生產(chǎn)擁有幾十到幾百個(gè)處理器的共享內(nèi)存機(jī)器,運(yùn)行著各種Unix變種。有很多技術(shù)用來(lái)促使軟件伸縮到多核機(jī)器上,包括可伸縮鎖[2]、無(wú)等待 同 步[3]、多 處 理 器 調(diào) 度 器[4]、NUMA結(jié)構(gòu)內(nèi)存[5,6]和基于共享內(nèi)存的快速消息傳遞[7]等。
針對(duì)搜索服務(wù)的Linux內(nèi)核的伸縮性,提供搜索服務(wù)的反向代理服務(wù)器是本文研究的重點(diǎn)。首先通過(guò)伸縮性實(shí)驗(yàn),分析系統(tǒng)性能數(shù)據(jù),找出Linux系統(tǒng)在多核系統(tǒng)下的效率瓶頸,并對(duì)Linux內(nèi)核進(jìn)行了改進(jìn),取得了一定的研究成果。具體的研究?jī)?nèi)容和取得的主要成果包括:
(1)伸縮性研究實(shí)驗(yàn)設(shè)計(jì)。對(duì)于運(yùn)行Nginx反向代理的64CPU核Linux服務(wù)器,每次實(shí)驗(yàn)通過(guò)設(shè)置Linux內(nèi)核的啟動(dòng)參數(shù)maxcpus來(lái)啟用不同數(shù)目的CPU核數(shù),對(duì)比不同CPU核數(shù)下系統(tǒng)性能差異,實(shí)驗(yàn)結(jié)果表明該Linux服務(wù)器伸縮性不佳。
(2)網(wǎng)卡中斷處理負(fù)載均衡改進(jìn)。本文使用ftrace和systemtap等Linux上的動(dòng)態(tài)追蹤、性能分析工具來(lái)獲取系統(tǒng)實(shí)驗(yàn)時(shí)的一些微觀數(shù)據(jù),通過(guò)這些數(shù)據(jù)發(fā)現(xiàn)瓶頸在于服務(wù)器上所有網(wǎng)卡中斷和軟中斷處理都集中在一個(gè)CPU核上。對(duì)比了Linux系統(tǒng)的RPS(Receive Packet Steering)機(jī)制、網(wǎng)卡的 RSS(Receive Side Scaling)機(jī)制和 Flow Direction機(jī)制,發(fā)現(xiàn)對(duì)于本系統(tǒng)的64核平臺(tái)來(lái)說(shuō),F(xiàn)low Direction機(jī)制的性能是最優(yōu)的,結(jié)合Linux中斷的親和性配置,可以把網(wǎng)絡(luò)數(shù)據(jù)包的接收處理分散到所有的CPU核上,可使系統(tǒng)平均吞吐量提升33%左右。
(3)TCP連接親和性改進(jìn)。多核環(huán)境下網(wǎng)卡中斷負(fù)載均衡無(wú)法保證TCP連接的親和性,不能保證一個(gè)TCP連接的數(shù)據(jù)包的底層網(wǎng)卡硬件中斷、軟中斷處理與處理該TCP連接的Nginx worker進(jìn)程在同一個(gè)CPU核上運(yùn)行。本文通過(guò)修改Linux內(nèi)核代碼,把內(nèi)核中TCP監(jiān)聽(tīng)套接字的全局icsk_accept_queue隊(duì)列拆分成per-core accept隊(duì)列,解決了TCP連接的非親和性問(wèn)題,吞吐量在之前提升的基礎(chǔ)上,又提升了9%。
Kleen[8]主要介紹了Linux的多核伸縮性。Linux從2.0版本開(kāi)始支持SMP系統(tǒng),整個(gè)內(nèi)核包括中斷處理程序都在一個(gè)大內(nèi)核鎖控制下,內(nèi)核代碼不能被多個(gè)處理器并行執(zhí)行,任何時(shí)候最多只有一個(gè)處理器可以運(yùn)行在內(nèi)核狀態(tài)下,用戶空間代碼可以并行執(zhí)行。
在Linux 2.2內(nèi)核,大內(nèi)核鎖仍然被用在大部分內(nèi)核代碼上,除了中斷處理程序有了自己的自旋鎖,允許中斷處理程序并行執(zhí)行。
到Linux 2.4內(nèi)核,越來(lái)越多的子系統(tǒng)從大內(nèi)核鎖的控制下移出。一些數(shù)據(jù)鎖被引入,但是仍然有很多代碼鎖(鎖住整個(gè)子系統(tǒng)的自旋鎖)。這在很大程度上提升了伸縮性,Linux已經(jīng)可以在大型系統(tǒng)上很好地運(yùn)行,但是也有很多瓶頸問(wèn)題未解決。
在Linux 2.6系列版本中,極少的代碼是在大內(nèi)核鎖的控制下,大多數(shù)在公共執(zhí)行路徑上的代碼鎖被轉(zhuǎn)換成數(shù)據(jù)鎖,并且有許多伸縮性的優(yōu)化來(lái)消除共享緩存的抖動(dòng)。對(duì)于關(guān)鍵的子系統(tǒng),使用了許多先進(jìn)的消除鎖的技術(shù),比如RCU(Read-Copy Update)[9]。引入了一個(gè)新的多隊(duì)列CPU 調(diào)度器,避免了對(duì)被多個(gè)處理器共享的全局運(yùn)行隊(duì)列的競(jìng) 爭(zhēng),降 低 了 調(diào) 度 多 核 并 行 程 序 的 負(fù) 載[10,11]。Linux 2.6內(nèi)核對(duì)NUMA架構(gòu)的支持也越來(lái)越完善,在調(diào)度器、存儲(chǔ)管理、用戶級(jí)API等方面進(jìn)行了大量的 NUMA優(yōu)化工作[12,13]。大量的共享數(shù)據(jù)被設(shè)計(jì)成per-CPU結(jié)構(gòu),以避免被多個(gè)核爭(zhēng)用。
Boyd-Wickizer S等人[14]探討了傳統(tǒng)操作系統(tǒng)內(nèi)核設(shè)計(jì)是否適合現(xiàn)在的多核環(huán)境,他們以Linux內(nèi)核作為實(shí)驗(yàn)對(duì)象,測(cè)試分析了七個(gè)系統(tǒng)軟件 (Exim、memcached、Apache、PostgreSQL、gmake、Psearchy和 MapReduce)在運(yùn)行l(wèi)inux-2.6.35-rc5的48核系統(tǒng)上的伸縮性。結(jié)果表明,除了gmake之外,其他六個(gè)軟件都觸發(fā)了Linux伸縮性方面的瓶頸。發(fā)現(xiàn)的瓶頸包括:對(duì)共享數(shù)據(jù)的爭(zhēng)用,比如共享的隊(duì)列、共享計(jì)數(shù)器、false sharing和一些不必要的鎖。解決的辦法包括,把共享隊(duì)列拆分成per-CPU隊(duì)列;用他們自己設(shè)計(jì)的可伸縮計(jì)數(shù)器來(lái)替代引起性能瓶頸的共享計(jì)數(shù)器,也是類(lèi)似于per-CPU技術(shù),讓每個(gè)處理器都有一個(gè)計(jì)數(shù)器的副本;用lock-free技術(shù)替代鎖來(lái)保護(hù)一些共享數(shù)據(jù);把經(jīng)常修改的共享數(shù)據(jù)放在不同高速緩存行中來(lái)避免false sharing;精心設(shè)計(jì)部分代碼消除某些不必要的鎖。通過(guò)這些技術(shù),Boyd-Wickizer S等人消除了內(nèi)核中的這些瓶頸,使得測(cè)試程序的性能在多核環(huán)境下大幅提升。結(jié)果表明,沒(méi)有直接的伸縮性原因使得要放棄傳統(tǒng)的內(nèi)核設(shè)計(jì)。
Gough C等人[15]研究 Oracle Database 10g在運(yùn)行 Linux 2.8.18內(nèi)核的dual-core Intel Itanium處理器平臺(tái)上的伸縮性,發(fā)現(xiàn)問(wèn)題在于Linux內(nèi)核的運(yùn)行隊(duì)列、slab分配器和I/O處理。
Cui等人[16]提出了一個(gè)用于測(cè)試類(lèi)Unix系統(tǒng)多核伸縮性的微觀測(cè)試基準(zhǔn)程序OSMark,并且用運(yùn)行Linux 2.6.26.8的 AMD 32核平臺(tái)來(lái)進(jìn)行評(píng)測(cè)。微觀測(cè)試基準(zhǔn)程序分別測(cè)量Linux內(nèi)核不同子系統(tǒng)的伸縮性,包括:進(jìn)程創(chuàng)建與刪除、mmap文件創(chuàng)建與刪除、文件描述符操作、socket套接字創(chuàng)建與刪除和System V信號(hào)量操作。結(jié)果表明大部分被測(cè)試的Linux內(nèi)核的伸縮性都很差。他們分析后發(fā)現(xiàn),內(nèi)核中用來(lái)保護(hù)共享數(shù)據(jù)的同步原語(yǔ)是限制并行伸縮性的主要原因。Cui Yan等人[17]也評(píng)估和比較了 Linux、Solaris和 FreeBSD在AMD 32核平臺(tái)上的并行伸縮性。對(duì)于OSMark的測(cè)試,沒(méi)有一個(gè)操作系統(tǒng)表現(xiàn)得完全比另外兩個(gè)都好,對(duì)于并行應(yīng)用程序postmark,Linux和Solaris不相上下,都優(yōu)于FreeBSD。
在Cui Yan等人[18]的另一篇論文中,他們使用TPCC-UVa與Sysbench-OLTP測(cè)試基準(zhǔn)程序和PostgreSQL來(lái)研究OLTP(Online Transaction Processing)應(yīng)用程序在運(yùn)行Linux 2.6.25內(nèi)核的Intel 8-core平臺(tái)上的伸縮性。他們提出一個(gè)基于函數(shù)伸縮性值的方法來(lái)尋找伸縮性的瓶頸。一個(gè)函數(shù)的伸縮性值被定義為該函數(shù)在多核環(huán)境下的執(zhí)行時(shí)間減去對(duì)應(yīng)單核環(huán)境下的執(zhí)行時(shí)間,通過(guò)仔細(xì)研究在內(nèi)核、庫(kù)、應(yīng)用程序進(jìn)程中高伸縮性值的函數(shù),他們發(fā)現(xiàn)數(shù)據(jù)庫(kù)緩存池競(jìng)爭(zhēng)、內(nèi)核調(diào)度器和System V進(jìn)程間通信中的鎖競(jìng)爭(zhēng)是TPCC-UVa的主要瓶頸。對(duì)于Sysbench-OLTP,數(shù)據(jù)庫(kù)同步原語(yǔ)和內(nèi)核調(diào)度器限制了它的伸縮性。
Veal B和 Foong A[19]使用 SPECweb2005測(cè)試基準(zhǔn)程序來(lái)評(píng)估Apache在運(yùn)行Linux 2.6.20.3內(nèi)核的8核AMD Opteron系統(tǒng)上的伸縮性。他們確定Linux內(nèi)核伸縮性問(wèn)題在于進(jìn)程調(diào)度和目錄查找。Boyd-Wickizer S等人[14]也發(fā)現(xiàn)目錄查找時(shí)加在目錄項(xiàng)上的自旋鎖是一個(gè)伸縮性瓶頸,盡管Linux內(nèi)核中已經(jīng)用RCU來(lái)優(yōu)化目錄緩存的伸縮性[20],但是公共父目錄(比如/usr)的目錄項(xiàng)自旋鎖有時(shí)會(huì)成為瓶頸,即使路徑名最終指向不同的文件。
開(kāi)源項(xiàng)目 Linux Scalability Effort致力于解決大型系統(tǒng)上Linux內(nèi)核的伸縮性問(wèn)題[21],由幾個(gè)子項(xiàng)目組成,針對(duì)Linux不同方面的伸縮性問(wèn)題,包括:Linux-on-NUMA項(xiàng)目、多核進(jìn)程調(diào)度、鎖原語(yǔ)、異步I/O、epoll伸縮性、可伸縮的計(jì)數(shù)器。該項(xiàng)目中所討論的很多伸縮性技術(shù)已經(jīng)被現(xiàn)在的Linux內(nèi)核使用。
用戶在使用互聯(lián)網(wǎng)公司(百度、Google等)提供的搜索服務(wù)時(shí),一般通過(guò)瀏覽器向Web服務(wù)器發(fā)送HTML請(qǐng)求,互聯(lián)網(wǎng)公司架設(shè)的反向代理Web服務(wù)器接收用戶請(qǐng)求,把請(qǐng)求轉(zhuǎn)發(fā)給公司內(nèi)網(wǎng)的搜索引擎,搜索引擎根據(jù)請(qǐng)求產(chǎn)生結(jié)果后,再通過(guò)Web服務(wù)器把結(jié)果轉(zhuǎn)發(fā)給用戶。Web服務(wù)器在中間起代理和中介的作用,其效率和吞吐量直接影響搜索服務(wù)的響應(yīng)時(shí)間。
本文研究提供搜索服務(wù)的Linux多核平臺(tái)的伸縮性瓶頸及其改進(jìn)辦法。要發(fā)現(xiàn)Linux內(nèi)核的伸縮性瓶頸,需要測(cè)量它在不同CPU核數(shù)下的性能差異,分析具體的微觀數(shù)據(jù)。本節(jié)將先介紹實(shí)驗(yàn)平臺(tái),然后給出操作系統(tǒng)伸縮性實(shí)驗(yàn)設(shè)計(jì)和實(shí)驗(yàn)結(jié)果。
為了測(cè)試提供搜索服務(wù)的Linux的多核伸縮性瓶頸,需要有客戶端、搜索引擎端和中間反向代理服務(wù)器一起構(gòu)成實(shí)驗(yàn)環(huán)境。
如圖1所示,整個(gè)實(shí)驗(yàn)環(huán)境包括三臺(tái)客戶機(jī),負(fù)責(zé)發(fā)起請(qǐng)求;三臺(tái)服務(wù)器,實(shí)際處理用戶搜索請(qǐng)求的機(jī)器;中間一臺(tái)服務(wù)器充當(dāng)反向代理。
三個(gè)client和三個(gè)server:一顆四核i7-2600 CPU,4GB內(nèi)存,Intel 82579LM 千兆網(wǎng)卡,是一個(gè)UMA (Uniform Memory Access)系統(tǒng),四個(gè)處理器核分別擁有自己的32KB八路組相連L1高速數(shù)據(jù)緩存和指令緩存、256KB八路組相連L2高速緩存,并且共享16路組相連8MB L3高速緩存。
中間反向代理服務(wù)器:超微H8QGI64核服務(wù)器,四顆16核心的AMD Opteron(TM)Processor 6272CPU 芯片,128GB內(nèi) 存,Intel X520T2萬(wàn)兆網(wǎng)卡。一顆6262芯片由八個(gè)Bulldozer模塊組成。一個(gè)Bulldozer模塊有兩個(gè)處理器核心,每個(gè)核心有16KB四路組相連L1數(shù)據(jù)緩存,共享64KB二路組相連L1指令緩存,共享2MB 16路組相連L2高速緩存。四顆6272芯片組成一個(gè)八節(jié)點(diǎn)的NUMA系統(tǒng),節(jié)點(diǎn)之間用HT總線互聯(lián)。
Figure 1 Topology of experimental圖1 實(shí)驗(yàn)環(huán)境拓?fù)鋱D
在建立好實(shí)驗(yàn)環(huán)境后,在三個(gè)server上運(yùn)行g(shù)enData,模擬搜索引擎,提供搜索服務(wù)。本文使用Firebug[22]工具,選取20個(gè)常用搜索關(guān)鍵詞作測(cè)試點(diǎn),測(cè)得百度、搜狗、谷歌和有道的搜索結(jié)果頁(yè)的平均大小如表1所示。本文的研究主要針對(duì)百度公司的搜索引擎,所以設(shè)置genData在接收到用戶請(qǐng)求后返回18.8KB大小的隨機(jī)內(nèi)容。
Table 1 Result page size of 4search engines表1 四個(gè)搜索引擎的搜索結(jié)果頁(yè)大小
在三個(gè)client機(jī)器上運(yùn)行webbench,設(shè)置并發(fā)量為1 000,向中間Nginx服務(wù)器發(fā)送搜索請(qǐng)求。在中間反向代理服務(wù)器上運(yùn)行Nginx作反向代理服務(wù)。首先,設(shè)置Nginx服務(wù)器的Linux內(nèi)核的啟動(dòng)參數(shù)maxcpus=1,只啟動(dòng)一個(gè)CPU核,在該CPU核上啟動(dòng)三個(gè)Nginx worker進(jìn)程,進(jìn)行實(shí)驗(yàn),測(cè)試10分鐘內(nèi)整個(gè)系統(tǒng)共處理的請(qǐng)求數(shù)。然后逐次增加Nginx服務(wù)器啟用的CPU數(shù),利用Linux進(jìn)程的親和性設(shè)置,在每個(gè)啟用的CPU核上啟用并綁定三個(gè)Nginx worker進(jìn)程,測(cè)試10分鐘的系統(tǒng)處理請(qǐng)求數(shù),結(jié)果如圖2和圖3所示。
圖2橫坐標(biāo)表示本次實(shí)驗(yàn)中間Nginx服務(wù)器所啟用的CPU核數(shù),在每個(gè)使用的CPU核上啟動(dòng)三個(gè)Nginx進(jìn)程,并綁定到該CPU核上;縱坐標(biāo)表示相應(yīng)CPU核數(shù)實(shí)驗(yàn)的請(qǐng)求量。從圖2可以看出,中間Nginx服務(wù)器啟用的CPU核數(shù)超過(guò)八個(gè)后,再啟用更多的CPU核對(duì)整個(gè)系統(tǒng)的性能提升不大。
圖3中橫坐標(biāo)的意義與圖2的一樣,縱坐標(biāo)表示平均到每個(gè)使用的CPU核的每秒吞吐量,單位是requests/s/core。從圖3中曲線的快速下降可以看出,本Nginx服務(wù)器的CPU伸縮性不佳,系統(tǒng)的性能沒(méi)有隨啟用的CPU核數(shù)而線性上升,在理想伸縮性情形下,圖3中的線應(yīng)該是一條接近水平的線。
Figure 2 Throughput of the Nginx server with different number of CPUs圖2 在不同CPU核數(shù)下的Nginx服務(wù)器吞吐量
Figure 3 Average throughput on every CPU of the Nginx server with different number of CPUs圖3 在不同CPU核數(shù)下Nginx服務(wù)器平均到每個(gè)CPU核的吞吐量
最后經(jīng)過(guò)仔細(xì)研究發(fā)現(xiàn),系統(tǒng)瓶頸出現(xiàn)在Nginx服務(wù)器上操作系統(tǒng)的網(wǎng)卡中斷和軟中斷處理,即網(wǎng)卡硬件中斷處理程序和網(wǎng)絡(luò)協(xié)議棧程序。以Nginx服務(wù)器啟用64核的實(shí)驗(yàn)為例,所有發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包的網(wǎng)卡中斷和軟中斷處理均在Nginx worker進(jìn)程所在的CPU核上處理,如圖4所示,這樣的負(fù)載分擔(dān)很好地發(fā)揮了多核處理器的能力。
Figure 4 Packet and NIC interrupts and soft interrupt handling schematic啟用64核時(shí),數(shù)據(jù)包發(fā)送的網(wǎng)卡中斷和軟中斷處理均在上層應(yīng)用Nginx所在的CPU核上
Figure 5 Packet receive NIC interrupts and soft interrupt handling schematic圖5 啟用64核時(shí),數(shù)據(jù)包接收的網(wǎng)卡中斷和軟中斷處理均在Core 0上處理
然而所有接收網(wǎng)絡(luò)數(shù)據(jù)包的網(wǎng)卡中斷處理和軟中斷處理都是在Core 0上進(jìn)行,如圖5所示,造成Core 0上的負(fù)載過(guò)重,成為系統(tǒng)吞吐量提升的瓶頸。在啟用了64核的實(shí)驗(yàn)環(huán)境下,用Linux內(nèi)核的ftrace工具測(cè)得網(wǎng)絡(luò)數(shù)據(jù)包接收的網(wǎng)卡中斷處理和軟中斷處理的平均用時(shí)是62.7μs,通過(guò)讀?。痯roc/interrupts,測(cè) 得 10 分 鐘的 時(shí) 間 里,在Core 0上共發(fā)生9 077 448次數(shù)據(jù)包接收中斷??梢运愠鯟ore 0花在數(shù)據(jù)包接收處理上的時(shí)間百分比是:9077448*62.7μs/(10*60s)*100%=93.8%。以此,本文認(rèn)為數(shù)據(jù)包的接收處理集中在Core 0上,而且Core 0的處理能力有限,是本系統(tǒng)的一個(gè)主要伸縮性瓶頸。下面將給出一個(gè)解決本系統(tǒng)數(shù)據(jù)包接收處理負(fù)載的方案。
對(duì)于本系統(tǒng)中的64核Nginx服務(wù)器上的X520-T2網(wǎng)卡,需要選一個(gè)合適本系統(tǒng)的方案來(lái)消除網(wǎng)卡中斷處理的伸縮性瓶頸。
Linux內(nèi)核的RPS是Google工程師Tom Herbert提交的內(nèi)核補(bǔ)丁,在2.6.35進(jìn)入Linux內(nèi)核。這個(gè)patch采用軟件模擬的方式,實(shí)現(xiàn)了多隊(duì)列網(wǎng)卡所提供的功能,把數(shù)據(jù)包接收的軟中斷處理分散到各個(gè)CPU處理。
X520-T2的RSS是一種把網(wǎng)絡(luò)包分配到不同的硬件接收隊(duì)列上的機(jī)制,對(duì)數(shù)據(jù)包的包頭的五元組(源IP、目的IP、源端口、目的端口和協(xié)議)進(jìn)行哈希運(yùn)算,哈希值的低七位用來(lái)索引一個(gè)128表項(xiàng)的表,每個(gè)表項(xiàng)四個(gè)bit,用來(lái)標(biāo)識(shí)一個(gè)接收隊(duì)列。RSS將不同的流負(fù)載到不同的CPU上,同一流始終在同一CPU上,避免TCP順序性和CPU并行性的沖突。
Flow Direction是另一種多接收隊(duì)列機(jī)制,可以把不同數(shù)據(jù)包映射到64個(gè)硬件接收隊(duì)列。Flow Direction在一個(gè)哈希表中查找一個(gè)流對(duì)應(yīng)的硬件接收隊(duì)列。這個(gè)哈希表存儲(chǔ)在網(wǎng)卡中,并且通過(guò)一些指令內(nèi)核可以修改該表,每個(gè)表項(xiàng)映射一個(gè)流的哈希值到一個(gè)6bit的硬件接收隊(duì)列標(biāo)識(shí)符。
如果采用Linux內(nèi)核的RPS機(jī)制,則不能充分發(fā)揮X520-T2的多硬件接收隊(duì)列的優(yōu)勢(shì),若采用網(wǎng)卡的RSS機(jī)制,最多只能啟用16個(gè)硬件接收隊(duì)列,綁定到16個(gè)CPU核上,不能充分利用64個(gè)CPU核。所以,本文最終采用X520-T2提供的Flow Direction隊(duì)列功能。
啟用X520-T2的多硬件接收發(fā)送隊(duì)列、MSIX中斷、Flow Direction特性,在系統(tǒng)中會(huì)出現(xiàn)64個(gè)網(wǎng)卡接收發(fā)送中斷。通過(guò)設(shè)置中斷的CPU親和性,綁定該網(wǎng)卡64個(gè)接收發(fā)送中斷到Nginx服務(wù)器的64個(gè)CPU核上,關(guān)閉Linux內(nèi)核的RPS機(jī)制,讓數(shù)據(jù)包的網(wǎng)卡中斷和軟中斷在一個(gè)CPU核上處理。然后再進(jìn)行3.2節(jié)中的實(shí)驗(yàn),與3.2節(jié)的實(shí)驗(yàn)結(jié)果進(jìn)行對(duì)比,得到的結(jié)果如圖6和圖7所示。
Figure 6 Nginx server throughput comparison圖6 消除網(wǎng)卡中斷瓶頸后的Nginx服務(wù)器吞吐量對(duì)比
圖6 中,曲線multi-interrupts是配置啟用網(wǎng)卡多硬件接收隊(duì)列、多個(gè)MSI-X中斷后進(jìn)行的實(shí)驗(yàn),曲線normal-kernel是3.2節(jié)中的實(shí)驗(yàn),作為對(duì)比。橫坐標(biāo)表示本次實(shí)驗(yàn)Nginx服務(wù)器所啟用的CPU核數(shù),在每個(gè)使用的CPU核上啟動(dòng)三個(gè)Nginx進(jìn)程,并綁定到該CPU核上,縱坐標(biāo)表示相應(yīng)CPU核數(shù)實(shí)驗(yàn)完成的請(qǐng)求次數(shù)。從圖中可以看出,從1核到64核平均吞吐量上升了33%,啟用64核時(shí),改進(jìn)后的系統(tǒng)吞吐量上升了84%。
Figure 7 Nginx server throughput comparison on each CPU圖7 消除網(wǎng)卡中斷瓶頸后的Nginx服務(wù)器每CPU核吞吐量對(duì)比
圖7 中,橫坐標(biāo)的意義與圖6的相同,縱坐標(biāo)表示平均到每個(gè)啟用的CPU核的每秒吞吐量。平均到每CPU核的每秒吞吐量也有對(duì)應(yīng)的提升。
下面再?gòu)奈⒂^方面觀察,啟用Flow Direction機(jī)制后網(wǎng)卡硬件中斷處理和軟中斷處理是否實(shí)現(xiàn)負(fù)載均衡。
重點(diǎn)對(duì)比分析啟用64個(gè)CPU核時(shí)的實(shí)驗(yàn),未啟用網(wǎng)卡多接收發(fā)送隊(duì)列,網(wǎng)卡中斷的分布如圖8所示。圖8橫坐標(biāo)表示64個(gè)CPU核的每個(gè)核的id,縱坐標(biāo)表示整個(gè)實(shí)驗(yàn)過(guò)程中發(fā)生在該CPU核上的數(shù)據(jù)包接收網(wǎng)卡中斷次數(shù)。從圖8中可以看出,所有的網(wǎng)卡中斷和軟中斷處理都在Core 0上進(jìn)行,造成了瓶頸。
圖9則是啟用網(wǎng)卡Flow Direction多接收發(fā)送對(duì)列進(jìn)行64核實(shí)驗(yàn)時(shí)數(shù)據(jù)包接收網(wǎng)卡中斷分布。從圖9可以看出,網(wǎng)卡中斷比較均勻地分散在所啟用的64個(gè)CPU核上,由于未啟用Linux內(nèi)核RPS機(jī)制,數(shù)據(jù)包接收軟中斷也在對(duì)應(yīng)網(wǎng)卡中斷的CPU核上處理,故而也實(shí)現(xiàn)了均衡。
Figure 8 NIC interrupt distribution on 64CPUs without flow direction圖8 未開(kāi)啟Flow Direction啟用64核實(shí)驗(yàn)時(shí)網(wǎng)卡中斷分布
Figure 9 NIC interrupt distribution on 64CPUs with flow direction圖9 開(kāi)啟Flow Direction啟用64核實(shí)驗(yàn)時(shí)網(wǎng)卡中斷分布
在第3節(jié)的實(shí)驗(yàn)中,本文通過(guò)配置Nginx服務(wù)器上的X520-T2網(wǎng)卡的Flow Direction多接收隊(duì)列機(jī)制,并結(jié)合Linux中斷親和性配置,實(shí)現(xiàn)了數(shù)據(jù)包網(wǎng)卡硬中斷和軟中斷處理的負(fù)載分散,提升了整個(gè)搜索系統(tǒng)的吞吐量。
在解決了數(shù)據(jù)包處理的負(fù)載均衡之后,又出現(xiàn)了新的問(wèn)題,每個(gè)搜索請(qǐng)求和響應(yīng)是用一個(gè)短時(shí)間的TCP連接完成的,但現(xiàn)有的內(nèi)核機(jī)制下不能保證一個(gè)TCP連接的數(shù)據(jù)包的底層網(wǎng)卡硬件中斷、軟中斷處理與處理該TCP連接的上層應(yīng)用在同一個(gè)CPU核上運(yùn)行,這稱為非親和的TCP連接。相反,如果一個(gè)TCP連接數(shù)據(jù)包底層處理和它的上層應(yīng)用在一個(gè)CPU核上運(yùn)行,稱為親和的TCP連接。親和TCP連接相比非親和TCP有很大的性能優(yōu)勢(shì),會(huì)大幅減少CPU的高速緩存失效率和鎖競(jìng)爭(zhēng)等待的時(shí)間。
本節(jié)首先通過(guò)實(shí)驗(yàn)驗(yàn)證非親和性的TCP連接帶來(lái)的性能下降,然后就Linux內(nèi)核TCP/IP網(wǎng)絡(luò)協(xié)議棧的實(shí)現(xiàn)來(lái)說(shuō)明為什么會(huì)出現(xiàn)非親和性的TCP連接,最后給出本文的解決方案以及實(shí)驗(yàn)評(píng)測(cè)結(jié)果。
從3.1節(jié)可以知道,Nginx服務(wù)器上的Core 1和Core 3分別有自己的L1Cache、不共享L2 Cache、共享L3Cache。在啟用64核的Nginx服務(wù)器上,關(guān)閉X520-T2網(wǎng)卡的Flow Direction多接收隊(duì)列機(jī)制,并把唯一的一個(gè)網(wǎng)卡中斷綁定到CPU 核Core 1(從0開(kāi)始編號(hào))上,在 Core 3上啟用并綁定3個(gè)Nginx worker進(jìn)程,進(jìn)行發(fā)送搜索請(qǐng)求的實(shí)驗(yàn),這樣所有用于發(fā)送請(qǐng)求和返回結(jié)果的TCP連接都是非親和的。然后,把網(wǎng)卡中斷綁定到Core 3上,讓所有用于發(fā)送請(qǐng)求和返回結(jié)果的TCP連接都是親和的,進(jìn)行對(duì)比實(shí)驗(yàn),期間用oprofile來(lái)測(cè)量Linux內(nèi)核中網(wǎng)絡(luò)協(xié)議棧函數(shù)的運(yùn)行時(shí)間,以及運(yùn)行期間的L2Cache失效次數(shù),結(jié)果如表2所示。
Tabel 2 Performance comparison between non-affinity TCP connection and affinity TCP connection表2 非親和性TCP連接與親和性TCP連接性能對(duì)比
從表2中可以看出,對(duì)于親和性TCP連接,Linux內(nèi)核協(xié)議棧的函數(shù)執(zhí)行時(shí)間較非親和性TCP連接下降了32%,一個(gè)請(qǐng)求的響應(yīng)完成時(shí)間下降了13%。所以,解決TCP連接的親和性問(wèn)題可以給系統(tǒng)帶來(lái)可觀的性能提升。
Linux中,TCP三次握手建立連接的過(guò)程如圖10所示。當(dāng)收到客戶端發(fā)送的建立TCP連接請(qǐng)求的SYN包時(shí),Linux首先會(huì)創(chuàng)建一個(gè)請(qǐng)求套接字struct request_sock,把它放入偵聽(tīng)套接字的syn_table哈希表里,然后發(fā)送SYN/ACK包給客戶端,當(dāng)客戶端再發(fā)來(lái)ACK包,該請(qǐng)求套接字被放入偵聽(tīng)套接字的icsk_accept_queue的先進(jìn)先出接收隊(duì)列中。當(dāng)程序在該偵聽(tīng)套接字調(diào)用accept()系統(tǒng)調(diào)用時(shí),便從icsk_accept_queue中取走一個(gè)請(qǐng)求套接字,建立了一個(gè)新的連接。
若啟用X520-T2的RSS或Flow Direction機(jī)制,會(huì)產(chǎn)生大量的非親和性TCP連接。在圖11中,在CPU M 和CPU N 上分別有一個(gè) Nginx worker進(jìn)程處理來(lái)自用戶的TCP連接,TCP連接A的數(shù)據(jù)包都被網(wǎng)卡映射到CPU N上處理。當(dāng)內(nèi)核完成對(duì)連接A的三次握手時(shí),可能由Nginx M通過(guò)accept()系統(tǒng)調(diào)用獲得了連接A的處理權(quán)。在第3節(jié)啟用網(wǎng)卡Flow Direction機(jī)制的實(shí)驗(yàn)中,當(dāng)啟用了n個(gè)CPU核時(shí),因?yàn)橐粋€(gè)TCP連接被映射到n個(gè)CPU核上處理的機(jī)會(huì)是均等的,
Figure 10 Three-way handshakes of TCP connection on server end in Linux kernel圖10 Linux內(nèi)核中TCP連接三次握手服務(wù)器端實(shí)現(xiàn)
所有親和性TCP連接數(shù)量只占整個(gè)TCP連接的1/n。從以上分析可以看出,若能把所有的非親和性TCP連接轉(zhuǎn)變?yōu)橛H和性的,則對(duì)整個(gè)系統(tǒng)的性能提升有很大的幫助。
Figure 11 Non-affinity TCP connection generation process圖11 非親和性TCP連接產(chǎn)生過(guò)程
在圖11中,TCP連接A變成非親和性TCP連接的問(wèn)題在于,連接A的request_sock在TCP監(jiān)聽(tīng)套接字的全局icsk_accept_queue隊(duì)列上,所有CPU核上運(yùn)行的Nginx worker進(jìn)程都可能獲取到,如果能讓連接A的request_sock只能由CPU N上的Nginx worker進(jìn)程獲取,那么問(wèn)題可迎刃而解。本文采用的方案是,把TCP監(jiān)聽(tīng)套接字的全局icsk_accept_queue隊(duì)列拆成per-core隊(duì)列,即每個(gè)CPU核有專(zhuān)屬于自己的本地的accept queue隊(duì)列。
Figure 12 Per-core’s accept queue圖12 per-core的accept queue
如圖12所示,TCP連接A被網(wǎng)卡映射到CPU N上進(jìn)行底層數(shù)據(jù)包處理,它的request_sock被放進(jìn)TCP監(jiān)聽(tīng)套接字在CPU N的本地accept queue上,當(dāng)Nginx N 運(yùn)行accept()系統(tǒng)調(diào)用時(shí),直接從本地的accept queue上取得元素;同樣,Nginx M 也從 CPU M 的本地accept queue上獲取request_sock,這樣能夠保證只有CPU N 上的Nginx worker進(jìn)程才能夠獲得TCP連接A的上層處理權(quán),從而保證了TCP連接A的親和性。
為了不對(duì)其他網(wǎng)絡(luò)協(xié)議的內(nèi)核代碼造成影響,本文把TCP監(jiān)聽(tīng)套接字struct tcp_sock復(fù)制NR_CPUS-1份(NR_CPUs是當(dāng)前啟用的CPUs核數(shù)),從而icsk_accept_queue也復(fù)制了 NR_CPUS-1份,每個(gè)啟用的CPU核擁有其中的一份,通過(guò)這種方式實(shí)現(xiàn)icsk_accept_queue隊(duì)列的per-core拆分。
重新編譯修改過(guò)后增加了per-core accept queue改進(jìn)的 Linux 3.2.0內(nèi)核,并與4.1節(jié)的multi-interrupts機(jī)制做對(duì)比實(shí)驗(yàn)。
首先,設(shè)置Nginx服務(wù)器的Linux內(nèi)核的啟動(dòng)參數(shù)maxcpus=1,只啟動(dòng)一個(gè)CPU核,在該CPU核上啟動(dòng)三個(gè)Nginx worker進(jìn)程進(jìn)行實(shí)驗(yàn),測(cè)試10分鐘內(nèi)整個(gè)系統(tǒng)共處理的請(qǐng)求數(shù)。然后,逐次增加Nginx服務(wù)器啟用的CPU數(shù),利用Linux進(jìn)程的親和性設(shè)置,在每個(gè)啟用的CPU核上啟用并綁定三個(gè)Nginx worker進(jìn)程,測(cè)試10分鐘的系統(tǒng)處理請(qǐng)求數(shù),如圖13所示。
Figure 13 Throughput comparison between per-core accept queue and multi-interrupts圖13 啟用per-core accept queue機(jī)制與multi-interrupts的總吞吐量的對(duì)比
圖13 的橫坐標(biāo)表示啟用的CPU核的個(gè)數(shù),縱坐標(biāo)是對(duì)應(yīng)的吞吐量。啟用per-core accept queue機(jī)制后,總的吞吐量較 multi-interrupts上升了9%。
針對(duì)搜索服務(wù)的Linux內(nèi)核的伸縮性,提供搜索服務(wù)的反向代理服務(wù)器是本文研究的重點(diǎn)。首先通過(guò)伸縮性實(shí)驗(yàn),分析系統(tǒng)性能數(shù)據(jù),找出Linux系統(tǒng)在多核系統(tǒng)下的效率瓶頸,并通過(guò)修改Linux內(nèi)核進(jìn)行了改進(jìn),取得了一定的研究成果。
在TCP連接的親和性改進(jìn)中,應(yīng)用程序調(diào)用accept()系統(tǒng)調(diào)用只能從本地CPU核上的accept queue隊(duì)列上獲取request_sock,也就是說(shuō),如果網(wǎng)卡不能把TCP連接均勻地映射到所有CPU核上的話,會(huì)造成CPU核上的accept queue隊(duì)列長(zhǎng)度不均勻,某些CPU核處理的TCP連接少,另外一些CPU核處理的TCP連接多,導(dǎo)致負(fù)載不均衡。極端的情況下,有些CPU核負(fù)載過(guò)輕,有些CPU核的負(fù)載過(guò)重,成為瓶頸,最終造成系統(tǒng)整體性能下降。在未來(lái)的工作中,會(huì)實(shí)現(xiàn)TCP連接負(fù)載均衡的方案,讓負(fù)載輕的CPU核可以從負(fù)載重的CPU核上取走一些TCP連接來(lái)處理,減輕TCP連接極端不平衡時(shí)的危害。
[1] Kuskin J,Ofelt D,Heinrich M,et al.The stanford FLASH multiprocessor[C]∥Proc of the 21st ISCA,1994:302-313.
[2] Mellor-Crummey J M,Scott M L.Algorithms for scalable synchronization on shared-memory multiprocessors[J].ACM Transactions on Computer System,1991,9(1):21-65.
[3] Herlihy M.Wait-free synchronization[J].ACM Transactions on Programming Languages System,1991,13(1):124-149.
[4] Vaswani R,Zahorjan J.The implications of cache affinity on processor scheduling for multiprogrammed,shared memory multiprocessors[C]∥Proc of the 13th SOSP,1991:26-40.
[5] Brock B C,Carpenter G D,Chiprout E,et al.Experience with building a commodity Intel-based ccNUMA system[J].IBM Journal of Research and Development,2001:45(2):207-227.
[6] Bryant R,Barnes J,Hawkes J,et al.Scaling Linux to the extreme[C]∥Proc of the Linux Symposium,2004:133-148.
[7] Bershad B N,Anderson T E,Lazowska E D,et al.Lightweight remote procedure call[J].ACM Transactions on Computer System,1990,8(1):37-55.
[8] Kleen A.Linux multi-core scalability[C]∥Proc of Linux Kongress,2009:1.
[9] McKenney P E,Appavoo J,Kleen A,et al.Read-copy update[C]∥Proc of Ottawa Linux Symposium,2001:1.
[10] Aas J.Understanding the Linux 2.6.8.1CPU scheduler[EB/OL].[2005-02-15].http://josh.trancesoftware.com/linux/.
[11] Mauerer W.Professional Linux kernel architecture[M].First Edition.Beijing:Posts &Telecom Press,2010.
[12] Dobson M,Gaughen P,Hohnbaum M,et al.Linux support for NUMA hardware[C]∥Proc of Linux Symposium,2003:1.
[13] Luck T.Linux scalability in a NUMA world [EB/OL].[2012-05-16].http://www.linux-mag.com/id/6868/.
[14] Boyd-Wickizer S,Clements A T,Mao Y,et al.An analysis of Linux scalability to many cores[C]∥Proc of the 9th USENIX Symposium on Operating Systems Design and Implementation(OSDI’10),2010:1.
[15] Gough C,Siddha S,Chen K.Kernel scalability—expanding the horizon beyond fine grain locks[C]∥Proc of the Linux Symposium 2007,2007:153-165.
[16] Cui Yan,Chen Yu,Shi Yuan-chun.OSMark:A benchmark suite for understanding parallel scalability of operating systems on large scale multi-cores[C]∥Proc of the 2nd International Conference on Computer Science and Information Technology,2009:313-317.
[17] Cui Yan,Chen Yu,Shi Yuan-chun.Parallel scalability comparison of commodity operating systems on large scale multi-cores[C]∥Proc of the Workshop on the Interaction between Operating Systems and Computer Architecture,2009:1.
[18] Cui Yan,Chen Yu,Shi Yuan-chun.Scaling OLTP applications on commodity multi-core platforms[C]∥Proc of 2010 IEEE International Symposium on Performance Analysis of Systems &Software,2010:134-143.
[19] Veal B,F(xiàn)oong A.Performance scalability of a multi-core web server[C]∥Proc of the 3rd ACM/IEEE Symposium on Architecture for Networking and Communications Systems,2007:57-66.
[20] McKenney P E,Sarma D,Soni M.Scaling dcache with rcu[EB/OL].[2004-01-15].http://www.linuxjournal.com/article/7124.
[21] Linux scalability effort project[EB/OL].[2012-05-17].http://lse.sourceforge.net/.
[22] Firebug[EB/OL].[2012-05-17].http://getfirebug.com.