最佳化編碼技巧 |
|
t[#t+1] = 0
比 table.insert(t, 0)
快。
x * (1/3)
和 x * 0.33333333333333333
一樣快,且比 x/3
在大多數 CPU 上執行得快(請參閱下方乘法註記)。1 + 2 + x
和 (1+2) + x
相同,執行速度應和 3 + x
或 x + (1 + 2)
一樣快,但比 x + 1 + 2
快,後者和 (x + 1) + 2
相同,但本質上並不等於前者。請注意,當發生溢位時,電腦上數字的加法通常不具結合性,而且編譯器甚至不知道 x
是數字或擁有非結合性 __add
元方法的其他類型。- LuaList:2006-03/msg00363.html 。據報導,Roberto 正在認真考慮將常數摺疊從 Lua 5.2 中移除,因為常數摺疊一直是 Lua 中的錯誤來源(儘管我們中有些人非常喜歡常數摺疊 -- DavidManura)。
x*0.5
比除法 x/2
快。
x*x
比 x^2
快。
x*y+x*z+y*z
--> x*(y+z) + y*z
。Lua 即使不能假設分配和其他常見代數性質會在數字溢位過程中保持不變,也不會為你執行這項動作。請注意,Roberto Ierusalimschy 的文章《Lua 效能訣竅》取自出色的 [Lua Programming Gems] 書籍,[可線上取得]。
以下資訊涉及 Lua 4 的最佳化,保留在此處以供歷史參考。
GameState
)需要從 C 存取而採用全域範圍,請建立一個次要變數,看起來像是「local GSLocal = GameState
」,並在模組內使用 GSLocal
。此技術也可以用於重複呼叫的函式(請參閱 OptimisingUsingLocalVariables)。lua_rawcall()
來呼叫其他函式。這樣就可以避免呼叫例外的 setjmp()
額外負擔,以及少數幾項其他事項。我不會建議在回呼函式外使用 lua_rawcall()
,以免執行期間出現錯誤。如果沒有 setjmp() 呼叫,則會呼叫終止應用程式的錯誤處理程式。lua_rawget()
和 lua_rawgeti()
存取表格,因為這樣可以避免標記方法檢查。請務必使用 lua_rawgeti()
進行索引式存取。它仍然是雜湊查找,但可能是根據索引取得資料最快速的方法。lua_ref()
。lua_ref()
的運作方式與局部變數在速度方面相似。lua_getglobal()
)的 C 字串會在輸入時轉換為 Lua 字串。如果要在遊戲的許多畫面中重複使用字串,也要對其執行 lua_ref()
作業。此資訊針對 v4.0 之前的 Lua 編寫 -- Nick Trout
assert(x <= x_max, "exceeded maximum ("..x_max..")")
function fast_assert(condition, ...) if not condition then if getn(arg) > 0 then assert(condition, call(format, arg)) else assert(condition) end end end
fast_assert(x <= x_max, "exceeded maximum (%d)", x_max)
這是產生的 VM 程式碼
assert(x <= x_max, "exceeded maximum ("..x_max..")") GETGLOBAL 0 ; assert GETGLOBAL 1 ; x GETGLOBAL 2 ; x_max JMPLE 1 ; to 6 PUSHNILJMP PUSHINT 1 PUSHSTRING 3 ; "exceeded maximum (" GETGLOBAL 2 ; x_max PUSHSTRING 4 ; ")" CONCAT 3 CALL 0 0 fast_assert(x <= x_max, "exceeded maximum (%d)", x_max) GETGLOBAL 5 ; fast_assert GETGLOBAL 1 ; x GETGLOBAL 2 ; x_max JMPLE 1 ; to 17 PUSHNILJMP PUSHINT 1 PUSHSTRING 6 ; "exceeded maximum (%d)" GETGLOBAL 2 ; x_max CALL 0 0
Edit: April 23, 2012 By Sirmabus The code above will not actually work with 5.1 Also added some enhancements like pointing back to the actual assert line number, and a fall through in case the assertion msg arguments are wrong (using a "pcall()").
function fast_assert(condition, ...) if not condition then if next({...}) then local s,r = pcall(function (...) return(string.format(...)) end, ...) if s then error("assertion failed!: " .. r, 2) end end error("assertion failed!", 2) end end
table = { "harold", "victoria", "margaret", "guthrie" }
對這個表格執行反覆運算的「適當」方式如下
for i=1, getn(table) do -- do something with table[i] end
然而,如果我們不關心元素順序,上面的反覆運算會很慢。第一個問題是,它會呼叫 getn(),根據以上假設「n」欄位尚未設定,並且假設其順序為 O(n)。第二個問題是,會執行位元組碼,並執行表格查找以存取每一個元素(也就是「table[i]」)。
一個解決方案是使用表格反覆運算器
for x, element in pairs(table) do -- do something with element end
已經移除 getn() 呼叫和表格查找。這個「x」是一個虛擬變數,因為這個範例中通常不會使用元素索引。
這個解決方案有一個注意事項。如果對表格使用函式庫函式 tinsert() 或 tremove(),它們會設定「n」欄位,該欄位會顯示在我們的反覆運算中。
另一個替代方案是使用 LuaPowerPatches 中列出的清單反覆運算修補程式。
(lhf) 表格是 Lua 中的核心資料結構。你不需要擔心表格效能,我們付出了大量的努力以使表格處理飛快。例如,我們有一個特別的指令碼用於 a.x
。觀察 a.x
和 a[x]
的不同 ... 但就像你說的,這裡不同之處主要在於額外的 GETGLOBAL
。
a,c = {},"x" CREATETABLE 0 PUSHSTRING 2 ; "x" SETGLOBAL 1 ; c SETGLOBAL 0 ; a b=a.x GETGLOBAL 0 ; a GETDOTTED 2 ; x SETGLOBAL 3 ; b b=a["x"] GETGLOBAL 0 ; a GETDOTTED 2 ; x SETGLOBAL 3 ; b b=a[c] GETGLOBAL 0 ; a GETGLOBAL 1 ; c GETTABLE SETGLOBAL 3 ; b END