【開(kāi)發(fā)日志補(bǔ)全計(jì)劃】Gridlock小組、名字對(duì)象、以及單用戶CPU占用率
原作者:CCP Masterplan
原文:http://community.eveonline.com/devblog.asp?a=blog&nbid=2326?
?
我們的Gridlock小組一直致力于解決游戲中的延遲卡機(jī)問(wèn)題以及改進(jìn)游戲編碼。稍早前我們對(duì)正式服務(wù)器進(jìn)行了一些優(yōu)化,現(xiàn)在幾周過(guò)去了,我們也能夠開(kāi)始衡量?jī)?yōu)化帶來(lái)的結(jié)果了。在最近舉行的峰會(huì)上我們將這些結(jié)果與星際管理委員會(huì)(CSM)的成員們做了分享與交流,之后我們決定將其公之于眾。
首先,我們來(lái)看個(gè)圖表,大家都愛(ài)看圖表不是嗎?
?

圖表展示的是8周內(nèi)EVE世界服務(wù)器上單用戶CPU占用率的變化曲線。縱軸可以表示任意單位,但是它所代表的硬件指標(biāo)總是相同的。這條曲線表示的是特定時(shí)間內(nèi)每位在線用戶的CPU占用率,這個(gè)數(shù)字自然是越低越好。紅色的橫線代表的是我們對(duì)TQ改進(jìn)之前及之后CPU占用率的變化趨勢(shì),而藍(lán)色的線代表的是TQ改進(jìn)的日期。我們以在編碼中實(shí)際使用的標(biāo)簽名稱來(lái)命名這些更改(本篇開(kāi)發(fā)日志是基于我們的內(nèi)部報(bào)告寫成的,所以我盡可能使它簡(jiǎn)單易懂一點(diǎn))。
?
每一個(gè)標(biāo)簽代表著一次特定的優(yōu)化,這個(gè)過(guò)程并不需要給服務(wù)器打補(bǔ)丁。這一點(diǎn)很不錯(cuò),因?yàn)槲覀兛梢韵葘?biāo)簽轉(zhuǎn)化為一個(gè)節(jié)點(diǎn)子集并且密切監(jiān)控未知錯(cuò)誤的發(fā)生,之后再將其在整個(gè)服務(wù)器范圍內(nèi)實(shí)行。
?
標(biāo)簽(flag)
4月26日,我們添加了一個(gè)服務(wù)器標(biāo)簽'ballparkUsesInventorySelfLocal'。之后,整個(gè)服務(wù)器的單用戶CPU占用率下降了大概8%。這是個(gè)很不錯(cuò)的結(jié)果,因?yàn)閷?shí)際上我們只對(duì)代碼做出了很小的改變。(我們還要做許多工作來(lái)評(píng)估這種變化,之后會(huì)進(jìn)行一些測(cè)試和分析來(lái)驗(yàn)證。)
5月2日,我們添加了另一個(gè)服務(wù)器標(biāo)簽'crimewatchUsesInventorySelfLocal'。它并沒(méi)有實(shí)質(zhì)性地改變單用戶CPU占用率,不過(guò)這也是意料之中的。這個(gè)標(biāo)簽的初衷是只在艦隊(duì)會(huì)戰(zhàn)及低安地區(qū)等特定情況下發(fā)揮效用,所以并不會(huì)對(duì)整個(gè)服務(wù)器的平均表現(xiàn)產(chǎn)生很大影響。
如果你只是想知道我們?cè)诟倪M(jìn)延遲方面取得了什么進(jìn)展,那你就不必往下看了,上面的圖表已經(jīng)說(shuō)明了一切——8%的性能優(yōu)化。如果你還想知道我們具體改變了哪些,那么繼續(xù)往下讀。
?
名字對(duì)象(moniker)小科普
這兩項(xiàng)改變都遵循著同一個(gè)主題:以限定對(duì)象(bound-object)代替名字對(duì)象,并且與該對(duì)象建立直接引用。這是什么意思呢?那么,好好聽(tīng)著,我要解釋很多內(nèi)容,并且會(huì)告訴你為什么這樣處理是好事情。
服務(wù)器各部分之間的通信機(jī)制是通過(guò)名字對(duì)象和限定對(duì)象來(lái)實(shí)現(xiàn)的。名字對(duì)象作為限定對(duì)象的“開(kāi)關(guān)”,而限定對(duì)象作為一個(gè)部分的前端,負(fù)責(zé)追蹤有多少個(gè)名字對(duì)象指向它。這個(gè)基本上就是一種代理服務(wù)器的設(shè)計(jì)模式。
有趣的是,名字對(duì)象和限定對(duì)象可以處于不同的進(jìn)程中,甚至可以處于不同的服務(wù)器節(jié)點(diǎn)上(EVE世界服務(wù)器集群現(xiàn)有大概200個(gè)節(jié)點(diǎn))。事實(shí)上,它們甚至可以分處地球的兩端——你的EVE客戶端使用同樣的工具與我們服務(wù)器上的對(duì)象進(jìn)行互動(dòng)。程序員們把這種現(xiàn)象認(rèn)為是RPC機(jī)制的一種具體體現(xiàn)。這一點(diǎn)很不錯(cuò),它意味著我們可以將服務(wù)器的邏輯部分分布在不同的節(jié)點(diǎn)上,它們可以通過(guò)一個(gè)程序界面互相通信,就像它們都在同一個(gè)節(jié)點(diǎn)上一樣。名字對(duì)象會(huì)占用一定的系統(tǒng)資源——與常規(guī)的函數(shù)調(diào)用相比,每一個(gè)通過(guò)名字對(duì)象的連接都需要一些額外的檢查工作,這是因?yàn)槊謱?duì)象也能提供一些額外的特性,例如生命周期管理(如果任何一個(gè)連接終端被關(guān)閉了,或是某個(gè)對(duì)象被破壞掉了,會(huì)發(fā)生什么事情呢?)、調(diào)用同步(舉例來(lái)說(shuō),我可以保證對(duì)同一個(gè)限定對(duì)象來(lái)說(shuō),不會(huì)同時(shí)出現(xiàn)兩個(gè)調(diào)用)、以及每個(gè)函數(shù)的許可控制(任何用戶都可以調(diào)用函數(shù)A,但是只有GM才能調(diào)用函數(shù)B)。
?

上面的圖表呈現(xiàn)的是一個(gè)普通對(duì)象'ServiceObject'接受兩個(gè)玩家對(duì)象連接的情況。每個(gè)連接都是通過(guò)名字對(duì)象進(jìn)行,而所有的名字對(duì)象連接都通過(guò)一個(gè)限定對(duì)象界面處理。黑色的箭頭表示邏輯連接,可以是在同一個(gè)存儲(chǔ)空間內(nèi),也可以是在同一本地網(wǎng)絡(luò)的不同終端中,甚至還可以是通過(guò)互聯(lián)網(wǎng)連接。
?
付諸實(shí)施
在服務(wù)器上,每一個(gè)恒星系都由幾個(gè)相互關(guān)聯(lián)的部分負(fù)責(zé)處理,本文涉及到的三個(gè)部分分別稱為Ballpark、CrimewatchLocation以及InventoryLocation。具體到每個(gè)恒星系,我們可以舉具體實(shí)例說(shuō)明:Ballpark負(fù)責(zé)處理太空中的物品(例如Destiny物理引擎、向客戶端發(fā)送狀態(tài)更新、通過(guò)星門跳躍等等)。CrimewatchLocation負(fù)責(zé)追蹤罪犯標(biāo)記,戰(zhàn)斗規(guī)則、擊殺記錄以及統(tǒng)合部NPC生成。InventoryLocation負(fù)責(zé)追蹤物品的位置,并作為物品數(shù)據(jù)庫(kù)的前端而存在。
特定恒星系的InventoryLocation部分可以通過(guò)名字對(duì)象從任何其它節(jié)點(diǎn)獲得,你可以知道它的它所在的節(jié)點(diǎn)的ID,并且可以獲得對(duì)應(yīng)的權(quán)限。Ballpark以及CrimewatchLocation系統(tǒng)便是利用此功能,通過(guò)一個(gè)名字對(duì)象接入InventoryLocation的。
?

這個(gè)圖表展示了Ballpark、CrimewatchLocation和InventoryLocation三個(gè)對(duì)象在改變之前及之后的連接關(guān)系。之前,由于與其它對(duì)象的共同依賴性,名字對(duì)象連接被限定為相同的方式。
?
這很不錯(cuò)。我們最初的構(gòu)想是將每一個(gè)部分都移到它自己的節(jié)點(diǎn)中,然后試圖通過(guò)平行機(jī)制實(shí)現(xiàn)運(yùn)行。但是,在任何一個(gè)恒星系中,這三個(gè)部分總是處在一個(gè)節(jié)點(diǎn)中的。隨著時(shí)間的推移,他們通過(guò)其它非名字對(duì)象的部分生根發(fā)芽,彼此緊密聯(lián)系在了一起。那么,把他們分離開(kāi)來(lái)就成了主要問(wèn)題。實(shí)際上,Ballpark、CrimewatchLocation和InventoryLocation三個(gè)部分已經(jīng)像連體嬰兒一樣密不可分。因此,如果不做重大結(jié)構(gòu)性調(diào)整的話,它們之間聯(lián)絡(luò)的消耗,將抵消平行機(jī)制所能帶來(lái)的效率提升。
我們?nèi)匀幌胍貥?gòu)并消除這些連接,但是我們也可以用其他事半功倍的方法。
?
修正
因此,我們將這三個(gè)部分通過(guò)RPC機(jī)制聯(lián)系起來(lái),它們就會(huì)一直處于同一個(gè)地方了。這樣做的話,就需要注意名字對(duì)象所帶來(lái)的額外效應(yīng),我已經(jīng)開(kāi)始著手去除這個(gè)中間步驟,將名字對(duì)象變?yōu)橐粋€(gè)足夠簡(jiǎn)便的直接引用地址——它已經(jīng)可以實(shí)現(xiàn)這個(gè)功能。
我們做的改變?nèi)缦拢?/p>
之前:
初始化:
# Get the moniker to an inventory location:
inv = GetInventoryMoniker(solarsystemID)
運(yùn)行時(shí):
# Use the inventory to get stuff from the DB:
item = inv.SelectItem(itemID)
之后:
初始化:
# Get the direct reference to an inventory location:
inv = GetInventoryMoniker(solarsystemID).GetSelfLocal()
運(yùn)行時(shí):
# Use the inventory to get stuff from the DB:
item = inv.SelectItem(itemID)
就這些,我們多加了15個(gè)字符,禁用了一個(gè)功能,為服務(wù)器節(jié)省了8%的效能,不賴吧?
?
對(duì)“修正”的修正
不過(guò)等等!還記得我提到過(guò)的事嗎?名字對(duì)象除了提供RPC機(jī)制之外,還能做些其他有用的事情,比如引用計(jì)數(shù)的周期管理什么的。限定對(duì)象只是因?yàn)槊謱?duì)象對(duì)其的引用而存在,名字對(duì)象被移除了,那限定對(duì)象也不復(fù)存在了。這個(gè)是我們?cè)跍y(cè)試時(shí)發(fā)現(xiàn)的問(wèn)題。結(jié)果,InventoryLocation只是因?yàn)镃rimewatchLocation對(duì)它的名字對(duì)象而存在,這會(huì)導(dǎo)致一些奇怪的Bug。舉個(gè)例子,如果某些玩家?guī)в蟹缸飿?biāo)記的話,那這個(gè)恒星系可能沒(méi)什么問(wèn)題,但是如果沒(méi)有這樣的玩家,CrimewatchLocation也就沒(méi)什么事情做了,就會(huì)導(dǎo)致InventoryLocation關(guān)閉,盡管這個(gè)時(shí)候Ballpark可能還在通過(guò)直接引用在使用它。
我們發(fā)現(xiàn)并修正了這個(gè)問(wèn)題之后,還進(jìn)行了一系列的測(cè)試,并且將它應(yīng)用在了測(cè)試服務(wù)器上一段時(shí)間,以找出一些特殊情況。
?
結(jié)論
在結(jié)束了測(cè)試并對(duì)結(jié)果感到滿意之后,我們以一種謹(jǐn)慎的方式在正式服務(wù)器上面添加了這些修改。4月底的時(shí)候,我們不太忙(因?yàn)闆](méi)有熱修復(fù)以及更新發(fā)布),于是我們就開(kāi)始著手修改,首先在4月26日加入Ballpark,然后在5月2日加入CrimewatchLocation。我們選擇在這個(gè)時(shí)候進(jìn)行修改是因?yàn)闆](méi)有版本等其他方面的更新,我們可以單獨(dú)地觀察修改的效果以及可能存在的不良影響,而且也比較容易將其恢復(fù)原狀,只要禁用其中一個(gè)優(yōu)化標(biāo)簽就可以。
幾天之后,單用戶CPU占用率的數(shù)字看起來(lái)不錯(cuò),我們也沒(méi)有遇到負(fù)面問(wèn)題。幾周之后,我們有了足夠的數(shù)據(jù),并最終得出了8%服務(wù)器效能優(yōu)化的結(jié)論,只是多加了30個(gè)字符而已(沒(méi)有統(tǒng)計(jì)對(duì)一些對(duì)象周期性問(wèn)題的修正),這很不賴吧?
這個(gè)故事只是Gridlock小組的日常工作的一個(gè)例子。我們還做其他一些工作,諸如各種測(cè)試、借鑒其他團(tuán)隊(duì)的好點(diǎn)子以保證我們不會(huì)走下坡路、以及對(duì)未來(lái)的規(guī)劃,比如改進(jìn)實(shí)時(shí)的節(jié)點(diǎn)重分配(如果某星系中發(fā)生了艦隊(duì)會(huì)戰(zhàn)的話,我們就把它移到一個(gè)專用節(jié)點(diǎn)上去)和時(shí)間膨脹等。