淺談遊戲程序開發的核心——復雜度

TuesdayAugust1320131376387621.jpg

文/安柏霖

在《The art of unix programming》中,復雜度的控制被看的非常的重,裡面一句話提到編程項目的核心就是對於復雜度的控制,以及simple原則其實也在講這個事情。

我自己在08年也寫瞭關於這個的話題:復雜度與習慣。7年過去瞭,也經歷瞭《天涯明月刀》這樣的重型項目的磨練,也有瞭更多的認識。

復雜度的要點

復雜度的要點所在就是程序給大腦帶來的負擔,它等同於程序員提升和開發程序的難易程度,這個負擔隨著模塊的復雜度大約是平方級數增長。



如果負擔很低,那麼一段程序的就容易控制,程序員就容易提升程序的質量(包括開發效率,運行穩定性和運行效率)。

所以我們也不需要在任何時候任何情況去做復雜度的最小化,如果一個模塊本身規模很小,那麼就不需要花很多精力去做進一步簡化(當然處於自我提升和精益求精的本能,在時間允許的情況下,做這個當然好的瞭)

同時低復雜度度也不等同於最少行數的代碼,而是給大腦帶來最少負擔的代碼,比如後文舉得代碼例子,雖然另外一種寫法代碼行數更多,但是由於它符合一個更穩定的模式,所以在大腦負擔和心理負擔都更輕,它可以認為是更低復雜度的代碼。

復雜度控制的實際意義

實際價值

先從實用的角度來看:關乎運行效率和開發效率(當然其他的擴展性等等也會包括,但是實際在項目裡的感受是這兩個尤其的明顯)。

其實7年前我也是毫無疑問的這麼認為的,但是實踐起來並不是一碼事情,大約幾年前,才真正的形成開發的原則。

開發效率

這個最深刻的認識原則當初開發地形系統,包括從編輯器的底層部分(UI部分是另外一個同事做的)以及runtime部分,從材質到高度圖,系統龐大而且復雜。

開發過程中,也不可避免的遭遇到需求變動(包括材質系統的能力,地圖大小這種非常顛覆性的)。

時間緊任務重,一直想盡量快點把東西做好,開發過程中,代碼整理和系統整體控制沒有做太多,然後其他組可以同步進行,然後再進行代碼整理。

但是對於一個龐大的系統,這種策略就不好。

寫程序的時候,質量和效率最好的情況就是始終對於整個系統,在代碼級別保持一個非常清晰的狀態,你心裡知道要寫成什麼樣,寫的過程,整體的代碼也清晰合理,與你心裡的樣子相印證,然後可以心如止水的一直非常快的寫,整個過程非常的享受。

而如果實現過程中,缺乏對於系統良好的認識和整理,希望“隨便搞搞,搞出來再整理“,這種在小型情況下是ok的,但是大型系統下,即便思維保持清晰,但是龐大的系統缺乏整理,而造成非常的復雜,很多東西由於前後設計的不一致,導致是處於一個不合理的復雜情況–需要你去死記。

這樣造成的結果就是,即便你對於整體系統的設計非常的清晰,但是在編程過程中,由於系統的一定的混亂,讓你沒法整個過程非常清晰的,心如止水的進行,整個的過程,磕磕絆絆,讓人疲憊不堪。

所以在後半段,就停下來改變瞭策略,先做充分的整理,把不需要的部分去除,然後把代碼整理到完全準備好來做新代碼的實現,才去做新的實現,這樣反而是最快的,寫起來也愉快迅捷。

運行效率

處理效率,常規的基本做法是profile熱點,以及根據遊戲的情況進行feature的關閉。

但是這個能做的事情是非常有限的,如果想做進一步提升性能,接近性能的極限,必須要做的就是:

– 對於每一個模塊有充分的理解

– 可以做到快速的反復嘗試迭代

處理性能熱點,在優化早期是一個非常高效的做法,準確來講,熱點處理是”在有水分的情況下,高效提升性能“的方法。

但是在追求極限性能方面,熱點優化還是不夠,某一個模塊的性能消耗是不是超過瞭它應該有的,以及一個排名10名開外的模塊其實是不需要高頻運行的等等,這些都是熱點處理不能解決的。

在對於程序有充分瞭解,就可以進行更徹底的調整,把大量的運行做並行,低頻執行或者直接優化掉。

實踐中看下來,這樣的處理會把程序的性能帶到一個新的臺階。

這個道理可以說是知易行難,難就難在,對一個超大系統(比如對於《天涯明月刀》來說,就是整個客戶端,覆蓋幾十萬行的代碼),如何做到充分理解,如何做到容易的徹底的修改優化。

所以關鍵點又回到復雜度,隻有程序的復雜度得到最好的控制,才能較好的做這個工作。

這個後來在實踐中,優化過程中,大約一半時間是在做代碼的調整和重構,代碼合理就會讓優化更加的可行和高效。

復雜度控制的方法與實踐

實踐下來,復雜度控制的能力在我看來可以從三個方面來拆解:渴望,目標與時間積累。

渴望:

首先最有效的方式就是去承擔實際的,要覆蓋非常大范疇的開發任務,這種情況下,你就會對於復雜度有切膚之痛,你就會非常真切的瞭解到復雜度是什麼,什麼是重要的,讓你抓狂的,什麼隻是虛張聲勢,無足輕重的,有瞭非常充分的渴望,那麼後面的積累和實踐就容易多瞭。

目標:

方法和實踐會是非常的多,但是目標卻簡單很多,就是能夠始終保持對於整個系統,在代碼級別非常的清晰。在開發設計和做決定的時候,能有心如止水般的順暢即可。所以一定程度上,可以說復雜度控制還是比較主觀的,也很看火候的。比如有時候項目本來就比較小,即便復雜度控制不是很好,但是也非常的清晰,hold住,那就可以把更多的精力放在其他方面。

方法:

個人實踐中,這幾個方面可以註意下:

– 任務切分+代碼整理:在較小型的任務結束的時候,就開始做小規模的代碼整理,始終保持代碼是幹凈的

– 模式+自然:積累更多的模式,比如一大片的代碼,其實就是做瞭pool的事情,那麼這一大片的復雜度就是一個詞:pool。讓所有的東西都更加自然,符合編程的優秀實踐,這樣需要你記和註意的東西就很少,那麼它就是一個很低的復雜度。

比如下面這個代碼:

12345 int a[5];for(int i=0; i<5; i++){    printf("%d",a[5]);}

這個在實際程序中就不是一個好的實踐,在看到這片代碼的時候,應該本能的註意到a[5]如果它的大小變化瞭怎麼辦,就會出現for的訪問越界的可能。

123456 #define ARRAY_NUM(a) (sizeof(a)/sizeof(a[0]))int a[5];for(int i=0; i < ARRAY_NUM(a); i++){     printf("%d",a[i]);}

那麼再次看到這樣的代碼的時候,就會比較放心,一路就過去瞭,那麼這個就可以認為是復雜度比較低的(需要註意的或者刻意要記的東西少)。

所以保持一個總結積累就變得非常重要,對於編程模式或者算法越來越多的積累,那麼在開發和思考的時候,就可以以更高的維度去做,那麼對於壓縮復雜度,提升思維速度和質量就非常的重要瞭。

並且,在這個層面上看,盡量返璞歸真的編程風格是一個更加有力的編程風格。

復雜度控制的“敵人”

沒有意識到“復雜度”的重要性

遇到不少程序員(甚至是大部分)對於復雜度無感,把一些算法和效率因素重要性遠遠放在復雜度之上,甚至是以寫出很復雜的程序為榮。這一塊不是很容易溝通,隻有實際去承擔大量的程序實現,對復雜度有切膚之痛的情況,才能有一個真實的認識。

還有就是沒有及時和項目組溝通,爭取足夠的時間來處理復雜度問題以及清理代碼,相當多的程序員都不會對復雜度有充分的認識,那麼要求項目經理有足夠的認識在我看來不太合理。基本上較有可行性的方法是程序員給予足夠的溝通,以及在實現估時上留有充分的餘量,而如果出現沒有意識到,沒有溝通充分,甚至是為瞭取悅manager而無視復雜度,瘋狂追求實現時間的情況,這都太糟糕瞭。

進度問題

時間緊任務重的情況,這個前面已經提過瞭,但是實際項目中還是會反復出現,這塊其實是可以是一個大的話題。

首先每個程序員需要建立一個代碼實現的profile機制–我個人一直使用worklog,然後對於自己的開發效率有一個跟蹤,這樣才能知道哪種方法是正確的更快的。磨刀什麼情況下才不誤砍材工,profile瞭才知道。

根據具體情況采取具體的策略,個人經驗下,相當的情況都是一邊實現一邊整理是更快的。

編程基本功,就是快速穩定的實現瞭,這個需要長期的有意識的積累。

good for the programmer’s soul

“Low-level programming is good for the programmer’s soul.” – John Carmack

對於卡神的這句話,無比的贊同,做底層代碼實現,對硬件和系統有透徹的理解,對於程序員去清晰的理解整個程序如何運行的至關重要,你就會更好的以底層的思維去思考。

同樣的道理,也可以用於高層的復雜度控制上面,更多的優秀的編程實踐,更好的理解要做的事情,理解系統本身,最後達到一個最簡潔的實現,整個設計和實現的過程,可以讓人進入心如止水的狀態,同樣的”good for the programmer’s soul“。

Comments are closed.