• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Daly的游戲人生

            2012年11月30日 #

            服務器程序常見bug總結

               最近整理了過去一年發生過的bug,包含跟其他項目組程序朋友交流的例子, 都是大家發生過的真實營運事故。
               游戲服務器程序,很多bug的原因都是共通的。抽象出了以下10點啟示, 作為checklist, 寫下來以后寫程序review時自檢:

            1. 安全邊界問題
                 對于有界限的東西(數值,buffer空間,隊列或一切對象容器),一定要考慮越界判斷。
                 啟示:用snprint, strncpy等限制長度.  永遠都要考慮超過邊界的情況
                           數值加法和乘法:考慮上限溢出; 
                           減法:考慮負數; 除法,判斷分母

            2. 輸入參數非法
                case1: 扣錢邏輯,減去一個負數,變成了加錢。  
                case2: int型大負數相加,負溢出變成大正數
                啟示:test case要全覆蓋輸入參數范圍, 處理各種可能的情況

            3. 上下文改變錯誤
                 共享變量/全局變量被外部改變,這似乎很常見,而且有時很隱蔽。在異步回調的情況下更常見。

                 check A變量 
                 call func_B()
                 ....
                 A變量被func_B改變了, 但繼續信任A變量check的結果。

                啟示:白盒復查代碼時,注意檢查調用后的變化。
                           減少共享變量和全局變量的使用
                           外部接口調用后,注意共享變量的更新和恢復
                 啟示:在最接近執行的地方,檢查上下文變量。不信任調用者,如果效率不關鍵,多一遍冗余檢查沒有壞處

            4. 執行中斷
                 動態腳本拋異常,或者引擎層面的EINTR中斷信號,都有可能中斷代碼執行,需要考慮函數的重入性問題。
                 啟示:要檢查一致性,有些邏輯不允許多次被執行(比如發獎勵),需要有狀態變量確保只執行1次(避免出刷bug)
                 推廣到異步環境(多線程,多進程,各種回調),事務的中斷也有一個重入性問題,解決方法也只有一個:用一個唯一可辨認的狀態變量,保證某些邏輯不會被多次執行(比如購物應用中,用唯一訂單號來識別,狀態改變是一次性的,當邏輯運行多次,也不會重復加物品,或者重復扣錢了)

            5. 終止條件問題--死循環
                 case: 異步環境中,RPC遠程調用,調用成環,邏輯一直不結束。
                 啟示:while或遞歸的終止條件,邏輯全覆蓋檢查,避免死循環。較深層次的互相調用,要注意是否出現了遞歸,是否有可能死循環。

            6. 關聯數據操作的不一致
                 例子:Employee對象有company變量, Company中有employee變量,
                      如果操作改變其中一方,而另一方沒有改變,則造成數據不一致。
                  (數據庫表可以指定constrain, 關聯表刪除, 但代碼變量中需要程序員自己實現)
                   雙向引用的數據一致性問題,要特別注意。
                   為什么要雙向引用?為了查找效率,而避免遍歷其中一方.
                   這個問題本質是數據一致性問題,編程中遇到的很多bug也歸結到這個問題,比如野指針,就是因為數據結構相互引用的操作不一致造成的。
                   處理這個問題,個人經驗是,他們的attach,detach操作盡可能在同一個模塊,不要分散在多個地方隨意修改,所有修改都集中在同一級接口做。

                   同理適用于new, delete, malloc, free這些分配,釋放,都集中在同一層的接口/模塊文件中做,debug起來也容易;非常反感在一個地方new, 然后不知道哪個模塊去delete, 很容易泄漏或者野指針, 無論如何,想辦法傳遞這些指針,一直傳到分配他所在的模塊文件中釋放,而且new和delete的接口代碼要靠近,方便查找問題。

            7. 涉及多玩家,防止筆誤傳錯參數
                 經典錯誤: foreach(uid in team) some_func(usernum, xxx)   
                 經典錯誤:有usernum和target兩個對象,調用函數搞混了。review時要仔細檢查

            8. 特殊分支忘了return
                 異常判斷等if分支忘了return。導致邏輯繼續往下走。這屬于筆誤問題,測試期間未必能留意的到。

            9. 異步返回沒清變量
                對于異步操作,如果在返回時清變量,這時如果不能保證把變量清掉(比如期間玩家下線無法離線修改該變量),就會出刷。
                啟示:對于已獎勵標記,一定要保證各種情況下領獎后能正確記錄。

            10. 瞬爆容量上限
                 case1:  網絡待發送隊列,因為瞬間大量請求,塞滿拋異常,導致流程受影響。
                 case2:  大量連接請求,listen的accept沒有規定單次讀事件的accept,用了while(true), 導致爆機
                            在listen fd的讀事件回調中, 通常會accept所有新的連接請求,如果用while(true)而不設一個上限,就有可能被攻擊(想象一下客戶端也用一個死循環來做connect)。
                            一方面要限制單次接受的socket次數, 另外各個狀態要有超時機制,踢掉不尋常的連接,以防被攻擊占盡資源。

                 case3: 異步情況下,要限制操作者連續頻繁的操作。(比如在請求入口處增加最少時間間隔限制,避免玩家狂點,形成雪崩效應)
                            (同時要考慮用戶體驗,不要讓玩家死等,可以做一個提示跳轉,或者等候的動畫)

            參考資料:
            附上最近看的一篇文章
            <Writing-reliable-online-game-services> 作者曾是魔獸爭霸和星際爭霸,battle.net的開發者,
            里面講的point也是游戲里經常遇到的可靠性問題。
            http://www.codeofhonor.com/blog/wp-content/uploads/2012/04/Patrick-Wyatt-Writing-reliable-online-game-services.pdf



            posted @ 2012-11-30 14:14 Daly 閱讀(2352) | 評論 (5)編輯 收藏

            2012年8月5日 #

            網游服務器多進程架構的思考

                by  Daly
                網游服務器程序優化要解決的最主要矛盾無非就是在保證流暢游戲體驗(響應時間在可接受范圍)的前提下,容納更多的玩家,當然還要保證開發的便捷性。一個靠譜的MMOG游戲服務器基本上都是多線程或多進程的架構, 利用多個CPU核把串行處理變成并行處理,以容納更大的并發玩家規模。
                然而并行處理程序會使開發的復雜度增加,一不小心很容易出一些詭異bug。為什么這樣說呢?實際環境的大部分程序,函數的執行結果與狀態數據相關(外部狀態,全局數據),并且函數執行可能會改變這些狀態。如果把處理模塊拆成多進程,進程間的這些狀態數據的一致性和處理時序,會影響到結果的正確性。多進程狀態數據的管理,讀寫和同步更新機制,便是本文要探討的主要問題。 
                   如果函數能變成無狀態的(結果只與輸入參數相關),則分拆成多進程毫無壓力。于是業界開始探討erlang這種函數式編程語言,并有已有實際游戲項目(參看:http://www.qingliangcn.com/) 。不過筆者覺得,erlang的無狀態,本質上是把狀態數據通過函數參數傳遞,這樣意味著頻繁而大量的數據復制和傳遞,是否更適合于MMORPG開發很難說,本文不予討論,可見文章末尾參考資料。下面探討一下狀態數據在多進程之間的問題。

                 為了容易描述,整個架構如下圖
                                  G
                client <--->║ <------> A
                                 ║ <------> B

                 其中G表示接入網關,負責把client協議分發到內網對應處理進程,A,B是負責不同功能的處理進程,client表示客戶端,玩家狀態數據只有個v和w兩個。用reqA,reqB分別表示client對A, B的處理請求,respA, respB表示A,B返回給client的處理結果。
                 游戲邏輯大部分情況下需要保證狀態數據的強一致性,基于過期的數據進行處理會得到錯誤的結果(分布式數據一致性的工程問題見文末的參考資料)。舉個有點蹩腳的例子,假設client先后發出reqA, reqB兩個請求,reqA是換武器,reqB是發起攻擊,變量v是攻擊輸出量(dps)。reqB在reqA之后發出,攻擊理應是按穿上武器后的dps數值來計算的。但多進程情況下,卻有可能reqB先于reqA處理(比如A進程很忙),這時reqB的邏輯會基于還沒穿上裝備時的變量v來計算結果。下面分別討論幾種解決數據一致性問題的方案。
            模式一:共享內存
                 適合于單機多進程或多線程的模式。
                 優點:數據只有一份,可以保證強一致性。
                 缺點:進程無法擴展到多臺服務器;
                      需要加鎖,加鎖相當于把處理串行化,還是有可能被某一個較忙的進程卡住。如果精心設計和劃分數據,減少鎖的粒度可以提高性能,但細粒度的鎖(設計成類似MySQL的行級鎖),在涉及多個玩家數據的交互邏輯時,稍有不慎又容易導致死鎖。隨手寫一個:
                    假設進程A和B同樣執行以下類似的邏輯
                     foreach( user in mapA) {
                          lock(user);
                          lock(user‘s friend);
                          do_something();
                          unlock(user's friend);
                          unlock(user_id);
                     }
                     由于遍歷的是map, 進程A和B中的user順序有可能交叉, 假設交叉的兩個user互為friend,就可能死鎖了。
                     參考資料[4]采用了這種模式的方案。
            模式二:狀態數據只由一個進程管理
                  把狀態數據根據游戲邏輯進行劃分,比如變量v只由A讀寫, 變量w只由B讀寫。假如A邏輯需要用到w,則通過異步請求B獲取w。
                  優點:保證強一致性;數據只有一份,無需進程間復制更新。
                  缺點:異步請求增加了響應時間(嗯,又從并行變成了串行); 異步寫起來的代碼有點ugly,到處是callback, 回來要檢查上下文,不然又是詭異bug.
                  適用范圍:如果狀態數據能比較好的劃分(即絕大多數情況下,某個數據只會在某個進程的邏輯中用到),用這種方案比較適合,因為簡單。比如玩家位置只由AOI進程管理,玩家好友由聊天進程管理。
            模式三:多個writer, 類似MVCC方案
                  這是完全的分布式設計。每個進程有自己版本的狀態數據,進程間可互相同步更新, 狀態數據v分別在A,B都有一份。互相update時,根據版本信息進行merge。 
                  這種方案不能保證強一致性,而且merge時會有可能發生沖突,需要邏輯開發者仲裁這種沖突(比如按時間先后)。不同于互聯網應用,游戲需要較強的數據一致性和實時性,這種方案比較復雜且不太可控。
            模式四:Master-Slave模式
                  這個是對模式二的一個擴展,某個狀態數據還是只由一個進程進行寫操作,但其他進程會維持一份cache進行讀操作,比如變量v由進程A管理,v的更新會同步到進程B,進程B邏輯如果要用到v,直接讀自己的cache就可以了。對于變量v
                 特點:這種方式也是不能保證強一致性,只能保證最終一致性。作為模式二的補充,有些數據不需要保證更新時序,根據過期數據進行處理也可以接受(這個是代價,需要權衡玩家體驗),可以采取這種方式。而對于不能接受的,走模式二。某些需求reqA,reqB雖然先后發出,如果respA還沒反饋回來的話,即使邏輯上reqB先于reqA處理,在玩家體驗上也是可以接受的。比如reqA穿裝備, 然后reqB攻擊,但是respA還沒返回,客戶端還是看作是沒穿上裝備,這時候按照老的屬性計算攻擊值是可接受的。廣域網幾百毫秒的延遲,reqB要晚于reqA + respA這種概率很小了,如果真的發生,服務器已經很卡了。
                又比如聊天進程,reqA離開場景,然后reqB發聊天消息往當前場景頻道,需要知道當前場景的玩家列表(假設場景玩家列表在AOI進程管理),如果reqB先到達聊天進程,拿到舊的場景玩家列表, 那么這個廣播就不準確了。這種不一致性的代價可以忍受的話就沒問題(在這個聊天欄例子,在跳場景的瞬間發錯人了也可以忍),實際情況,進程間通信幾個毫秒,發生這種處理時序反轉的幾率其實非常小了。
            綜上,如果要設計多進程結構,個人比較推崇模式四。這時又引申出幾個問題:狀態數據如何合理劃分?何時更新?同步給誰?
            如何劃分?
                 有些功能很好劃分。比如聊天進程,狀態數據只與好友列表有關,這個需求可以忍受過期數據,好友關系由主進程修改,同步到聊天進程。玩家position, 由AOI進程管理,修改同步到主進程,主進程幾乎沒有需要用到position的邏輯。
                但有些數據就可能很糾結,比如背包數據。玩家交易,在線獎勵,戰斗都需要修改背包物品數據,而且必須保證強一致性,否則就可能出現丟失或物品復制,該由誰做這個數據的管理者呢?如果AOI進程管理,物品使用效果可以馬上生效,但是交易和在線獎勵也需要驗證背包物品,這些邏輯也放到AOI進程么,如果放,則又牽扯出更多的變量,如果不放,則需要退化成模式2的異步請求。如果放主進程,則使用物品后產生的效果不能立刻同步到AOI進程。可以經過仔細對比,AOI與背包數據交互的頻率遠高于主進程,于是背包數據可由AOI進程管理。
            何時更新?
                 兩種選擇:一有修改立馬發送更新給其他進程;隊列buffer住所有更新,定時送出去(比如每2秒同步一次);既然是無法保證強一致性,后者性能容易優化些。比如AOI進程中的位置信息變化很頻繁,但主進程對位置實時性不敏感(比如只用于持久化,掉線重上后的位置恢復),則更新間隔可以長一些,否則會有頻繁而大量的位置數據更新;定時更新也利于同步間隔內數據修改的合并,減少同步量。
            同步給誰?
                 某類數據有修改時,需要通知哪些進程,意味著要維持一個映射表。可以在編碼階段,在數據定義時靜態寫死某類數據要通知哪一類功能進程; 也可以在運行期設計成pub-sub模式(或者叫observer模式), 動態增刪訂閱者。筆者覺得前者可控一點,因為進程要用到哪些數據,在編碼階段是可以清楚規劃的,根據這個原則把數據劃分成一個個模塊,比如玩家數據分為基本角色屬性,avatar, 位置/朝向, 好友數據....  然后決定歸屬。
                多進程可以提升系統并發規模,但同時有各種異步調用和數據一致性問題,帶來的代價就是bug的風險增加(尤其團隊水平不能保證個個都很高的情況下,一個菜鳥程序員就夠受了,還很難跟蹤),開發難度增大。這個需要仔細profile和實驗確定瓶頸在哪,真的跑滿CPU或者卡IO才有必要分出去,想當然的把模塊拆分很多進程,設計看上去很優雅也很牛逼,往往是麻煩的開始 ——> 開發效率降低,出bug意味著啥?加班,加班,深夜運維的奪命追魂call... ...
            參考資料
            [1] 當webgame邂逅erlang.  明朝網絡的慶亮。 http://www.slideshare.net/qingliangcn/webgameerlang-8241397#btnNext
            [2] 陳杰談網游服務器后端技術.  西山居陳杰的ppt, 講多進程架構下的尋路算法 http://timyang.net/architecture/game-backend/
            [3] nosql ecosystem. 13節講述分布式系統的數據一致性問題
            [4] 結構化數據的共享內存, 云風 http://blog.codingnow.com/2011/12/dev_note_6.html

            posted @ 2012-08-05 17:01 Daly 閱讀(4267) | 評論 (3)編輯 收藏

            2012年7月17日 #

            網絡游戲不同類型的技術分類

                不同的游戲類型需要有不同的技術設計,尤其服務器端,沒有一個通用游戲引擎可以適應所有類型。所以大部分游戲的服務器端引擎都是根據產品需求手工打造,商業引擎通常也得經過別扭的折騰改造才能用得比較好(比如Bigworld ^_^ )。 本文對常見的幾種網絡游戲的服務端技術做一個技術特點的分類。
                約束性能和容量規模的因素歸納起來是:數據共享域,消息廣播域,運算共享域。這幾個因素涉及的對象數量會直接影響了架構涉及,下面分別來說。
             
                 分類特征:
                 玩家數據總量,同時在線數
                 這里是指,玩家登錄后,需要從多大規模的數據中讀取自己的數據。social game全局共享的角色數據(統一世界),隨著玩家數量增多線性增長,往往單機儲存不能滿足需求,需要分布式儲存技術。而對于傳統MMORPG,由于是分服(服務器之間是平衡世界,角色數據相互獨立),相當于天然地用服務器id做了數據分區(而不需要像分布式儲存那樣考慮分區算法的擴展性問題),除了登錄信息,沒有全局數據,單機儲存即可解決問題。
                 AOI范圍與頻率
                 這里指的是游戲過程中,即時廣播涉及的對象數量以及消息密度,比如同屏玩家數。競技和休閑類游戲通常比較小,而傳統MMORPG通常較大。
                 范圍和頻率還影響client間傳輸消息的方式,通常有兩種:client間直傳(P2P); 中心服轉發。從延時來看,廣域網游戲這兩者區別到不大(前提是中心服不卡,其實可以看做一個路由器)。如果廣播范圍 x 頻率較大,中心服的帶寬成本很高(嗯嗯,帶寬是很貴的)。如果采用純P2P,則需要考慮客戶端作弊問題。當然還可以用混合的方法,即中心服監督下的P2P,比如有些競技游戲在服務端也做一層校驗,查出外掛的可能,當然這個不容易實現得好。 大部分情況下,服務器監督一切,最省心。

                 AOI跳轉方式
                 是指在不同運算共享域和廣播域直接切換的方式,分為無縫和跳轉點。舉個例子,休閑游戲和競技游戲通常是玩家主動點擊進入或退出房間/頻道,不同房間/頻道分隔了廣播域。而即時制MMORPG(比如WOW)區域之間是無邊界的。這兩者區別影響運算性能的擴展性,即實現多進程處理的技術難度。對于前者可以很容易實現多進程分擔處理任務。后者的無縫AOI,要實現多進程的話,在邊界處需要較復雜的進程數據同步技術。
                 實時性/同步要求
                 網絡延時的前提下,同步方案主要是用戶體驗和數據正確性之間的權衡。競技和動作游戲強調打擊感和位置準確性,需要很高的同步要求。很多游戲采用幀同步方案,即一旦對應幀數據未到,卡住整個客戶端(dota的等待連線)。也有采用運動補償的方式(也稱追影),即客戶端預判,當和服務端位置不一致時,通過加速等方式平滑追上。為了減少延時帶來的影響,一部分計算放在客戶端,關鍵計算等待服務器返回。在等待服務器返回結果的過程中,通常結合美術和技術手段"欺騙"玩家的視覺(比如起手動作),達到較好的體驗。

            Social game
                 玩法:策略經營類,好友互動
                 玩家數量大,冷數據總量大 (海量玩家同一交互域),同時在線高;
                 AOI范圍中。頻率低。消息在好友之間分發(好友數量一般在一百以內)
                 實時性/同步要求:低,不需要實時。運算較簡單(看成是海量數據的CURD應用)
                 技術特點:跟微博,QQ群等傳統互聯網應用比較接近:數據量大,AOI范圍中,實時要求低。主要難點在于讀寫規模大,總數據量大,cache熱度不明顯(無明顯熱數據)。
                 性能擴展:依賴于分布式儲存和讀寫技術,與一般社交網絡技術類似。由于需要預先加好友,設定好友數量上限可以限制數據廣播的規模。

            休閑棋牌類游戲
                 玩家數量大;冷數據總量大;但登錄后通常進入房間。
                 房間之間分隔了廣播域,游戲局之間玩家互相獨立(除了聊天頻道)。意味著較容易根據房間和游戲分服/進程,性能擴展容易。
                 AOI范圍低(一局游戲的幾個人),實時性一般。運算一般(棋牌算法計算)。
                 架構上通常分前端(登錄和房間邏輯)和后端(具體一局游戲),分服設計較容易。通常一個前端要對應很多種不同類型的后端邏輯(各種類型游戲),需要制定一個容易開發和接入的框架。
                 容量擴展:由于游戲局的獨立性,分進程/分服做運算擴展比較簡單。 通過分區分房間限制了數據廣播規模。

            競技類游戲
                 玩法:dota, FPS, 格斗動作類
                 AOI范圍低(10人以下),交互頻率高
                 實時交互和同步要求極高(技術難點)
                 容量擴展:通常與休閑類一樣,先進入房間(有些叫頻道)限制數據廣播規模。不同房間互相獨立,因此也較容易通過增加進程/服務器分散運算規模。

            即時制MMORPG
                 通常技能有cooldown, 玩家之間可以穿插(沒有動態碰撞檢測),同步要求低于動作類和dota網游。單服同時在線人數有限(1w人左右),邏輯復雜, IO通常單機就可搞定。AOI通常是運算瓶頸,要提高容量就要分進程或分線程。對于區域間無縫世界,在邊界處的對象,由于互相可見且可戰斗,分管兩個區域的進程間需要較復雜的同步機制。比如bigworld用的是對象代理技術,即在原區域是real對象,對端區域建立一個ghost(代理對象), 對real對象的所有狀態改變即時同步到對端進程,反之對ghost操作也同步到real。也就是說玩家A在兩個進程都有自己的副本,且都可寫,需要借鑒分布式技術中,多Writer的數據一致性設計。
                 對MMORPG來說,單服人數越高,游戲的社區性和人氣感就越強, 但人數越多,就越容易卡住服務器。現在都是多核的世界,基本上都是多線程/進程的架構了,多writer/reader, 數據同步,鎖這些是常見技術考量點。一般功能模塊交互性不強,分進程/線程難度不大,但AOI這塊分進程要比較折騰。

                 題外話
                 這兩年公司的校園招聘,程序員的title是虛擬世界架構師(汗 -_-!)。真正的虛擬世界應該是:數據規模大,AOI范圍大,實時交互。以上還沒有一種游戲同時符合幾個特征,都不同程度通過分區/分服/分房間/分場景/分頻道分隔了單個進程的處理規模,單臺服務器的數據規模和帶寬規模。當然,即使技術上可行,玩家腦子同時能處理的對象數比電腦要差多了(呃,試想數萬人同處一個場景,然后走來走去,這個有游戲性可言?),這時候瓶頸不在服務器,超密集的角色,客戶端的渲染效率變成瓶頸。

            posted @ 2012-07-17 09:45 Daly 閱讀(2850) | 評論 (5)編輯 收藏

            2012年7月2日 #

            替代系統malloc/new--選擇合適的內存跟蹤方案

             
            替代系統自帶的malloc/new原因無非兩個: 
            reason 1. 做內存profile或查找問題   
            reason 2. 自定義的分配方案提高性能

            不過文章[1]中說明了,替代全局new不是一個好做法. 其實要達到以上兩點目的,筆者認為用valgrind工具鏈就可以了。

            解決方案:
            1. 用valgrind和massif
                 valgrind的memcheck做內存泄露和bug的查找, 里面的massif工具包做內存性能profile, 足矣。比自己山寨的一個profiler要好。
                 注意:tcmalloc目前還不能很好支持valgrind,  實測中jemalloc可以

            2.  linux下C的程序可以用wrap的方式(相當于python的decorator)
                 編譯加上選項:gcc -Wl,-wrap,malloc
                 可以做到對malloc這個函數,linker會調用__wrap_malloc代替之, 若要調用原來的malloc函數__real_malloc
                 缺點:依賴于編譯器支持; 對c++的new不起作用 --> 不實用
                 啟示:這個方法作為function裝飾器,對于調試別的問題倒有幫助。(例如不改變函數的情況下,wrap一層,輸出些調試信息)
            3. 用__malloc_hook
                參考: http://linux.die.net/man/3/__malloc_hook
                 #include <malloc.h>
                 void *(*__malloc_hook)(size_t size, const void *caller);
                 缺點:依賴GNU編譯工具鏈;  容易死循環(想利用原有malloc,要參考例子中,把原__malloc_hook變量保存起來使用,并恢復現場)
            

            4. LD_PRELOAD注入.so ,替代原
                 環境變量LD_PRELOAD指定程序運行時優先加載的動態連接庫,這個動態鏈接庫中的符號優先級是最高的。標準C的各種函數都是存放在libc.so.6的文件中,在程序運行時自動鏈接。使用LD_PRELOAD后,自己編寫的malloc的加載順序高于glibc中的malloc,這樣就實現了替換。用法 LD_PRELOAD=" ./mymalloc.so"
                  缺點:在生產環境不現實。因為LD_PRELOAD相當于庫注入,有安全性問題,是必須禁止的。(生產環境很多時候用-static連接)
            5. 用宏或另外的函數替代new/malloc
               比如定義一個宏或者指定的函數,規定所有的分配釋放都調用他。這樣相當于給項目引入了額外的代碼規則(而且是一立項就要遵循這個規則,否則該方法無效),不能很自然的new/delete, 如果分配和釋放調用得不一致,會產生問題的。某產品組就是用宏,然后加上__FILE__, __LINE__之類的信息。

             有時候valgrind的效率是個問題(尤其生產環境),這種方案有其價值所在, 就是代碼看上去比較ugly罷了

               用宏的例子:
               #define _New(Type, Catergory)                    (Type*)MyMemController::New((new Type), #Type, 1, sizeof(Type),   Catergory, __FILE__, __LINE__, false)
               #define _NewArray(Type, N, Catergory)          (Type*)MyMemController::New((new Type[N]), #Type, N, sizeof(Type)*(N), Catergory, __FILE__, __LINE__, true)

               
            MALLOC的替代品:
                 自己寫一個malloc其實很復雜,要考慮線程安全等各種問題,性能到頭來可能更差。google 的tcmalloc,  facebook使用的jemalloc.   多線程下性能較好,可以考慮使用。
                 缺點:筆者嘗試過。tcmalloc不能正確用valgrind,只能用自帶gperftools(運行中會core)
                             jemalloc可以使用valgrind,不過還沒完全驗證是否都準確。
            tcmalloc相關:
                在64位系統上要裝libunwind, 對x86-64架構使用還有些問題

            源碼包的INSTALL文檔里面也提到了這個問題。
             CAUTION: if you install libunwind from the url above, be aware that
               you may have trouble if you try to statically link your binary with
               perftools: that is, if you link with 'gcc -static -lgcc_eh ...'.
               This is because both libunwind and libgcc implement the same C++
               exception handling APIs, but they implement them differently on
               some platforms.  This is not likely to be a problem on ia64, but
               may be on x86-64.

            主要是64位機frame-pointer的影響, 他的profile工具里的backtrace用libunwind這個庫,這個庫又有版本問題,各種囧啊....
            筆者試過系統x86-64, freebsd,用靜態鏈接。實際用了一下,問題很多很折騰,等他fix了再說吧.

            windows下可以參考:

            jemalloc暫時未發現有什么兼容性問題,運行得挺好的。
             
            Reference
            [1] <不要重載全局operator new>

            [2] effective c++條款50:了解new和delete的合理替換時機

            [3] 游戲引擎中的內存分配策略
            [4] 更好的內存管理jemalloc
            [5] tcmalloc官網(gperftools)

            posted @ 2012-07-02 13:01 Daly 閱讀(7462) | 評論 (0)編輯 收藏

            2012年7月1日 #

            基于binlog的游戲數據儲存引擎

                最近組內發表一篇小論文,是關于改進游戲儲存系統的IO性能思路。老大原來早有相同的想法,并且已經實現了大部分模塊,后來和老大一同努力,新的儲存引擎終于逐步完善。在外服環境跑了兩個多月,性能和可靠性得到了明顯的提升。具體的細節就不方便發表了,實踐證明,用binlog來做MMORPG的數據儲存是行得通的。

            幾個事實:
               1. 磁盤IO的瓶頸在尋道,順序寫性能比隨機寫性能高一個數量級。

            目前典型硬盤的順序寫入速度大約是60MB/s , 而尋道時間在5~8ms (200/)。可以看到硬盤IO的主要瓶頸在于磁頭尋道,也就是隨機寫。在linux開發服(非虛擬機,Xeon 3.0G 4/16G內存)上做了一個benchmark

            順序寫50MB: 700ms

            5000個文件,每個10KB(50MB): 12

                    10000次隨機寫,每次1KB(10MB): 21
               2. 游戲數據都是K-V數據,關系查詢需求極少;k-v數據的update很頻繁(實測是每玩家每5秒一次修改)
               3. MMORPG單服的玩家同時在線數量是10K級別, 這個數量級可以有效估算binlog的規模,使得方案可行。

                 一般MMORPG系統的存盤策略: 定時存盤。就是過一段時間(比如5分鐘)把在線有修改過的玩家數據,整個snapshot存下去(mysql也好,文件系統也好)。這樣有兩個主要問題:一到保存點,IO隨機寫暴增,玩家卡機;如果系統down機, 數據就會有幾分鐘的回檔。而性能和數據可靠性兩則是矛盾的,存盤間隔過小,玩家卡機,過大,故障后數據回檔時間長。需知現在的MMORPG,貴價武器價值都成千上萬RMB,數據可靠性對游戲營運影響還是很大的。
                so,   可以用定制的binlog來記錄玩家數據,也就是說,不記錄整個snapshot,而是每個k-v變化時記錄opcode馬上寫入binlog文件, binlog的格式根據游戲情況可以高度定制,盡量減少空間。由于是順序寫,性能可以非常高。如果down機,可以根據binlog來恢復,基本上沒有回檔。不過要解決一個問題:binlog增長過大 --> 崩潰恢復時間過程 & binlog文件本身損壞的風險增大 & 磁盤空間用光。因此binlog需要有rotate機制, rotate的時候需要存一次在線玩家數據的snapshot, 這樣舊的binlog就可以存到遠處或者丟棄。rotate的過程中需要考慮恢復時玩家數據一致性和完備性等等一系列細節問題,后來一一解決了。
                這是最近做的成就感的事。幾年沒寫blog了,筆記都記在evernote里,最近又想在公開的地方寫點東西,發個文紀念一下。


            posted @ 2012-07-01 18:05 Daly 閱讀(2220) | 評論 (6)編輯 收藏

            2010年6月18日 #

            Nginx源碼學習(2) ---- 模塊化及配置

                 摘要:   閱讀全文

            posted @ 2010-06-18 16:15 Daly 閱讀(2492) | 評論 (0)編輯 收藏

            山寨版小游戲~~~

                 摘要: 實習做的兩個小游戲,山寨版的雷電和網絡對戰的休閑游戲。  閱讀全文

            posted @ 2010-06-18 13:38 Daly 閱讀(803) | 評論 (1)編輯 收藏

            Nginx源碼學習(1) ---- 基礎數據結構

                 摘要: nginx的基礎數據額結構分析  閱讀全文

            posted @ 2010-06-18 12:27 Daly 閱讀(3774) | 評論 (1)編輯 收藏

            2010年5月26日 #

            高效的Timer實現

                 摘要: 很多程序都需要處理一系列定時事件, 本文就見過的程序中,幾種實現Timer的方法。用到的數據結構一般有鏈表, 堆, RB樹,hash table等,還有一些比較優化的方法。  閱讀全文

            posted @ 2010-05-26 20:44 Daly 閱讀(4331) | 評論 (2)編輯 收藏

            2010年5月2日 #

            資源和內存管理學習總結

                 摘要: 總結了幾種資源和內存管理的實現思路。包括buddy算法,STL中的allocator實現思路,游戲中的資源管理  閱讀全文

            posted @ 2010-05-02 00:02 Daly 閱讀(2254) | 評論 (1)編輯 收藏

            僅列出標題  下一頁
            久久夜色精品国产www| 国产精品熟女福利久久AV| 婷婷久久综合九色综合绿巨人| 91精品无码久久久久久五月天| 久久久久久综合一区中文字幕| 国产69精品久久久久99| 性做久久久久久久久浪潮| 日韩精品无码久久久久久| 欧美日韩中文字幕久久伊人| 日韩欧美亚洲综合久久| 99久久精品日本一区二区免费| 久久99精品久久久久久不卡| 少妇熟女久久综合网色欲| 青青青伊人色综合久久| 久久综合亚洲色一区二区三区| 久久久久久狠狠丁香| 久久婷婷色综合一区二区| 青青国产成人久久91网| 亚洲熟妇无码另类久久久| 精品久久久久久久久久中文字幕| 久久99久久99精品免视看动漫| 久久精品国产亚洲麻豆| 色8久久人人97超碰香蕉987| 青青草原综合久久大伊人导航| 亚洲国产成人久久综合碰碰动漫3d| 国产成人久久精品一区二区三区| 久久精品二区| 久久免费国产精品一区二区| 国产人久久人人人人爽| 亚洲AV无码久久精品色欲| 综合久久一区二区三区 | 久久AV高清无码| 久久综合久久伊人| 波多野结衣中文字幕久久| 亚洲∧v久久久无码精品| 久久久这里只有精品加勒比| 久久午夜综合久久| 91精品国产91久久久久久青草| 国产精品一久久香蕉国产线看| 久久午夜羞羞影院免费观看| 亚洲中文字幕无码久久2020|