當處理Lua資源時,我們也應該遵循提倡用于地球資源的3R原則——Reduce, Reuse and Recycle,即削減、重用和回收。
削減是最簡單的方式。有很多方法可以避免使用新的對象,例如,如果你的程序使用了太多的表,可以考慮改變數據的表述形式。一個最簡單的例子,假設你的程序需要操作折線,最自然的表述形式是:
另一個更經濟的做法是使用一個數組存儲所有x坐標,另一個存儲所有y坐標:
循環是尋找降低垃圾回收次數的機會的好地方。例如,如果在循環里創建一個不會改變的表,你可以把它挪到循環外面,甚至移到函數外作為上值。試對比:
對于多種字符串處理,我們可以通過使用現有字符串的索引來減少對創建新字符串的需要。例如,string.find函數返回它找到指定模式的位置索引,而不是匹配到的字符串。通過返回索引,它避免了在成功匹配時創建新的字符串。當有必要時,程序員可以通過調用string.sub來獲取匹配的子串[1]。
當我們無法避免使用新的對象時,我們依然可以通過重用來避免創建新的對象。對于字符串來說,重用沒什么必要,因為Lua已經為我們做了這樣的工作:它總是將所有用到的字符串內部化,并在所有可能的時候重用。然而對于表來說,重用可能就非常有效。舉一個普遍的例子,讓我們回到在循環里創建表的情況。這一次,表里的內容不再是不變的。通常我們可以在所有迭代中重用這個表,只需要簡單地改變它的內容??紤]如下的代碼段:
LPeg,Lua的一個新的模式匹配庫,就使用了一個有趣的緩存化處理。LPeg將每個模式字符串編譯為一個內部的用于匹配字符串的小程序,比起匹配本身而言,這個編譯過程開銷很大,因此LPeg將編譯結果緩存化以便重用。只需一個簡單的表,以模式字符串為鍵、編譯后的小程序為值進行記錄。
使用緩存化時常見的一個問題是,存儲計算結果所帶來的內存開銷大過重用帶來的性能提升。為了解決這個問題,我們可以在Lua里使用一個弱表來記錄計算結果,因此沒有使用到的結果最終將會被回收。
在Lua中,利用高階函數,我們可以定義一個通用的緩存化函數:
loadstring = memoize(loadstring)
新函數的使用方式與老的完全相同,但是如果在加載時有很多重復的字符串,性能會得到大幅提升。
如果你的程序創建和刪除太多的協程,循環利用將可能提高它的性能?,F有的協程API沒有直接提供重用協程的支持,但是我們可以設法繞過這一限制。對于如下協程:
Lua中的多數回收都是通過垃圾回收器自動完成的。Lua使用漸進式垃圾回收器,意味著垃圾回收工作會被分成很多小步,(漸進地)在程序的允許過程中執行。漸進的節奏與內存分配的速度成比例,每當分配一定量的內存,就會按比例地回收相應的內存;程序消耗內存越快,垃圾回收器嘗試回收內存也就越快。
如果我們在編寫程序時遵循削減和重用的原則,通常垃圾回收器不會有太多的事情要做。但是有時我們無法避免制造大量的垃圾,垃圾回收器的工作也會變得非常繁重。Lua中的垃圾回收器被調節為適合平均水平的程序,因此它在多數程序中工作良好。但是,在特定的時候我們可以通過調整垃圾回收器來獲取更好的性能。通過在Lua中調用函數collectgarbage,或者在C中調用lua_gc,來控制垃圾回收器。它們的功能相同,只不過有不同的接口。在本例中我將使用Lua接口,但是這種操作通常在C中進行更好。
collectgarbage函數提供若干種功能:它可以停止或者啟動垃圾回收器、強制進行一次完整的垃圾回收、獲取Lua占用的總內存,或者修改影響垃圾回收器工作節奏的兩個參數。它們在調整高內存消耗的程序時各有用途。
“永遠”停止垃圾回收器可能對于某些批處理程序很有用。這些程序創建若干數據結構,根據它們生產出一些輸出值,然后退出(例如編譯器)。對于這樣的程序,試圖回收垃圾將會是浪費時間,因為垃圾量很少,而且內存會在程序執行完畢后完整釋放。
對于非批處理程序,停止垃圾回收器則不是個好主意。但是,這些程序可以在某些對時間極度敏感的時期暫停垃圾回收器,以提高時間性能。如果有需要的話,這些程序可以獲取垃圾回收器的完全控制,使其始終處于停止狀態,僅在特定的時候顯式地進行一次強制的步進或者完整的垃圾回收。例如,很多事件驅動的平臺都提供一個選項,可以設置空閑函數,在沒有消息需要處理時調用。這正是調用垃圾回收的絕好時機(在Lua 5.1中,每當你在垃圾回收器停止的狀態下進行強制回收,它都會恢復運轉,因此,如果要保持垃圾回收器處于停止狀態,必須在強制回收后立刻調用collectgarbage("stop"))。
最后,你可能希望實施調整回收器的參數。垃圾回收器有兩個參數用于控制它的節奏:第一個,稱為暫停時間,控制回收器在完成一次回收之后和開始下次回收之前要等待多久;第二個參數,稱為步進系數,控制回收器每個步進回收多少內容。粗略地來說,暫停時間越小、步進系數越大,垃圾回收越快。這些參數對于程序的總體性能的影響難以預測,更快的垃圾回收器顯然會浪費更多的CPU周期,但是它會降低程序的內存消耗總量,并可能因此減少分頁。只有謹慎地測試才能給你最佳的參數值。
[1] 如果標準庫提供一個用于對比兩個子串的函數可能會是一個好主意,這樣我們無需將子串解出(會創建新的字符串)即可檢查字符串中的特定值。
[2] 緩存化,原文memoize
新聞熱點
疑難解答