Lua·001·性能優(yōu)化

Lua腳本是C語(yǔ)言實(shí)現(xiàn)的腳本署海,廣泛應(yīng)用于客戶端擴(kuò)展腳本采够,例如魔獸世界等網(wǎng)游肄方。但是Lua的性能一般,并且有許多不好的實(shí)現(xiàn)蹬癌,誤用會(huì)大大降低系統(tǒng)的性能权她。
網(wǎng)絡(luò)上有一些關(guān)于Lua腳本性能優(yōu)化的資料,但是都是針對(duì)Lua撰寫的逝薪,寫作年代較早隅要,一些優(yōu)化技巧不完全正確,而且沒有針對(duì)LuaJIT優(yōu)化過后的代碼進(jìn)行考慮董济。
本章對(duì)于Lua的一些語(yǔ)法步清,在Lua和LuaJIT中進(jìn)行比較測(cè)試,并給出相關(guān)優(yōu)化數(shù)據(jù)和結(jié)論虏肾。

由于LuaJIT的性能較Lua有很大的提高尼啡,在測(cè)試時(shí)使用的循環(huán)次數(shù)不同暂衡,以避免時(shí)間太短導(dǎo)致測(cè)量不準(zhǔn)確。
在結(jié)果中崖瞭,In LuaJIT (100x)表示LuaJIT中執(zhí)行性能但愿測(cè)試次數(shù)是Lua中的100倍狂巢。

相關(guān)參考資料:

  1. Roberto Ierusalimschy. Lua Performance Tips. http://www.lua.org/gems/sample.pdf
  2. Lua Performance: http://springrts.com/wiki/Lua_Performance

目錄

變量局部化

Lua變量區(qū)分為全局變量和局部變量。Lua為每一個(gè)函數(shù)分配了一套多達(dá)250個(gè)的寄存器书聚,并用這些寄存器存儲(chǔ)局部變量唧领,這使得Lua中局部變量的訪問速度很快。
相反的是雌续,對(duì)于全局變量斩个,Lua需要將全局變量讀出存入當(dāng)前函數(shù)的寄存器中,然后完成計(jì)算之后再存回全局變量表中驯杜。
這樣受啥,一個(gè)類似a = a + b這樣的簡(jiǎn)單計(jì)算,若ab為局部變量鸽心,則編譯之后只生成一條Lua指令滚局,而若為全局變量,則生成4條Lua指令顽频。Lua Performance Tips中提到藤肢,將全局變量變?yōu)榫植孔兞浚缓笤谑褂眠M(jìn)行訪問糯景,速度可以提升約30%嘁圈,編寫如下代碼進(jìn)行實(shí)驗(yàn):

代碼和結(jié)果

function nonlocal()
    local x = 0
    for i = 1, 100 do
        x = x + math.sin(i)
    end
    return x
end

local sin = math.sin
function localized()
    local x = 0
    for i = 1, 100 do
        x = x + sin(i)
    end
    return x
end

--[[------------------------
In Lua (1x)
ID  Case name   t1      t2      t3      t4      t5      avg.    %
1   Non-local   1.66    1.65    1.65    1.66    1.66    1.656   100%
2   Localized   1.25    1.25    1.25    1.25    1.25    1.25    75.48%
In LuaJIT (2x)
ID  Case name   t1      t2      t3      t4      t5      avg.    %
1   Non-local   1.3     1.31    1.3     1.3     1.3     1.302   100%
2   Localized   1.3     1.3     1.3     1.29    1.3     1.298   99.69%
--]]------------------------

結(jié)論
在普通Lua的解釋下,行為和//Lua Performance Tips//中所述大致一致蟀淮,調(diào)用全局變量中的函數(shù)大約會(huì)慢30%最住。
而在LuaJIT的解釋下,兩者差異不明顯怠惶。
不是一定需要將全局變量中的變量轉(zhuǎn)變?yōu)榫植孔兞俊?/p>

多重條件判斷

在Lua中温学,唯一的數(shù)據(jù)結(jié)構(gòu)table是哈希表,創(chuàng)建甚疟、銷毀和迭代都需要?jiǎng)?chuàng)建很多資源仗岖。
本例對(duì)比當(dāng)判斷較多條件時(shí),使用連續(xù)邏輯表達(dá)式和使用table的性能览妖。

代碼和結(jié)果

function use_or()
    local x = 0
    for _, v in pairs(states) do
        if "alabama" == v or
            "california" == v or
            "missouri" == v or
            "virginia" == v or
            "wisconsin" == v then
            x = x + 1
        end
    end
    return x
end

function use_table()
    local x, vals = 0, {"alabama", "california", "missouri", "virginia", "wisconsin"}
    for _, v in pairs(states) do
        for _, s in pairs(vals) do
            if s == v then x = x + 1 end
        end
    end
    return x
end

--[[------------------------
In Lua (1x)
ID  Case name   t1      t2      t3      t4      t5      avg.    %
1   Use OR      0.39    0.38    0.39    0.39    0.38    0.386   100%
2   Use table   1.85    1.84    1.84    1.85    1.84    1.844   477.72%
In LuaJIT (10x)
ID  Case name   t1      t2      t3      t4      t5      avg.    %
1   Use OR      0.6     0.6     0.59    0.59    0.6     0.596   100%
2   Use table   2.82    2.85    2.86    2.86    2.85    2.848   477.85%
--]]------------------------

結(jié)論
當(dāng)判斷較多邏輯條件時(shí)轧拄,應(yīng)當(dāng)使用簡(jiǎn)單的邏輯運(yùn)算,而table創(chuàng)建讽膏、銷毀和迭代的開銷較大檩电,應(yīng)避免使用。
盡管使用循環(huán)的方法,程序可能具有較好的可讀性俐末,但是性能會(huì)嚴(yán)重下降料按。
使用邏輯表達(dá)式判斷,采取較好的寫法也可以獲得可讀性卓箫。

函數(shù)調(diào)用

函數(shù)是我們對(duì)功能進(jìn)行封裝的基本方法载矿,但是函數(shù)的調(diào)用也是具有一定的開銷,本例反映了函數(shù)調(diào)用的時(shí)間開銷問題烹卒。
其中闷盔,直接計(jì)算部分在測(cè)試函數(shù)中內(nèi)嵌代碼進(jìn)行階乘計(jì)算,而函數(shù)調(diào)用部分則使用另外封裝的階乘計(jì)算函數(shù)旅急。

代碼和結(jié)果

function direct_compute()
    local x = 0
    for i = 1, 30 do
        local r = 1
        for j = 1, i do
            r = r * j
        end
        x = x + r
    end
    return x
end

function fact(x)
    local result = 1
    for i = 1, x do
        result = result * i
    end
    return result
end

function call_function()
    local x = 0
    for i = 1, 30 do
        x = x + fact(i)
    end
    return x
end

--[[------------------------
In Lua (1x)
ID  Case name         t1      t2      t3      t4      t5      avg.     %
1   Direct compute    1.17    1.17    1.17    1.17    1.18    1.172    100%
2   Call function     1.5     1.5     1.51    1.51    1.5     1.504    128.33%
In LuaJIT (10x)
ID  Case name         t1      t2      t3      t4      t5      avg.     %
1   Direct compute    1.08    1.08    1.08    1.08    1.07    1.078    100%
2   Call function     1.32    1.32    1.33    1.32    1.32    1.322    122.63%
--]]------------------------

結(jié)論
使用函數(shù)封裝之后逢勾,消耗的時(shí)間Lua中增加28%,LuaJIT中增加22%藐吮。如果是會(huì)反復(fù)調(diào)用的功能溺拱,如無必要,如代碼復(fù)用等問題谣辞,則應(yīng)當(dāng)盡可能避免多次調(diào)用函數(shù)迫摔。

尾遞歸

在一般的編程語(yǔ)言中,函數(shù)遞歸會(huì)占用椓氏校空間攒菠,層次很深的遞歸容易導(dǎo)致棧溢出迫皱。
在一些編程語(yǔ)言歉闰,尤其是函數(shù)式編程語(yǔ)言中,對(duì)一種特殊的遞歸方法——尾遞歸進(jìn)行了優(yōu)化卓起。
Lua中也是如此和敬,使得尾遞歸的函數(shù)在遞歸開始時(shí)會(huì)釋放當(dāng)前函數(shù)的調(diào)用棧空間戏阅,從而保證多級(jí)遞歸也不會(huì)額外占用椫绲埽空間。
本例中展示尾遞歸的性能奕筐,使用普通遞歸舱痘、普通循環(huán)和尾遞歸三種方法編寫計(jì)算階乘的程序,并以普通遞歸方法作的數(shù)據(jù)為對(duì)比的基準(zhǔn)离赫。

代碼和結(jié)果

-- 普通遞歸
function fact_recurse(x)
    if x <= 1 then return 1 end
    return x * fact_recurse(x - 1)
end

-- 普通循環(huán)
function fact_normal(x)
    local result = 1
    for i = 2, x do
        result = result * i
    end
    return result
end

-- 尾遞歸
function fact_tail(x, r)
    local result = r or 1
    if x <= 1 then return result end
    return fact_tail(x - 1, result * x)
end

--[[------------------------
In Lua (1x)
ID  Case name         t1      t2      t3      t4      t5      avg.     %
1   Normal recurse    1.88    1.88    1.89    1.88    1.88    1.882    100%
2   Normal loop       0.5     0.49    0.5     0.5     0.49    0.496    26.35%
3   Tail recurse      1.99    2       1.99    1.99    1.99    1.992    105.84%
In LuaJIT (20x)
ID  Case name         t1      t2      t3      t4      t5      avg.     %
1   Normal recurse    3.02    3       3       3       2.99    3.002    100%
2   Normal loop       0.98    0.97    0.97    0.97    0.98    0.974    32.45%
3   Tail recurse      1.06    1.07    1.07    1.07    1.07    1.068    35.58%
--]]------------------------

結(jié)論
雖然Lua中強(qiáng)調(diào)了對(duì)尾遞歸的優(yōu)化芭逝,但是實(shí)際執(zhí)行結(jié)果表明,Lua中的尾遞歸只是對(duì)椩ㄐ兀空間的分配進(jìn)行了優(yōu)化旬盯,速度并沒有比普通遞歸有提升。
而在LuaJIT中,使用尾遞歸編寫的函數(shù)具有與普通循環(huán)相接近的性能胖翰。
不過尾遞歸函數(shù)不太容易編寫接剩,在實(shí)際使用過程中可以使用。

Table

table追加

Lua的標(biāo)準(zhǔn)庫(kù)函數(shù)中萨咳,并不是所有函數(shù)都實(shí)現(xiàn)得很好懊缺,尤其是table數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)性能較差,table.insert函數(shù)就是一個(gè)性能較低的函數(shù)某弦。

代碼和結(jié)果

function table_insert()
    local t = {}
    for i = 1, 1000, 2 do
        table.insert(t, i)
    end
    return t
end

function table_insertL()
    local t, insert = {}, table.insert
    for i = 1, 1000, 2 do
        insert(t, i)
    end
    return t
end

function use_counter()
    local t, c = {}, 1
    for i = 1, 1000, 2 do
        t[c], c = i, c + 1
    end
    return t
end

function use_length()
    local t = {}
    for i = 1, 1000, 2 do
        t[#t + 1] = i
    end
end

--[[------------------------
In Lua (1x)
ID  Case name       t1      t2      t3      t4      t5      avg.    %
1   table.insert G  2.72    2.73    2.72    2.73    2.72    2.724   100%
2   table.insert L  2.24    2.24    2.24    2.24    2.24    2.24    82.23%
3   use counter     1.13    1.13    1.14    1.12    1.13    1.13    41.48%
4   use length      1.43    1.44    1.43    1.43    1.43    1.432   52.57%
In LuaJIT (5x)
ID  Case name       t1      t2      t3      t4      t5      avg.    %
1   table.insert G  3.69    3.69    3.68    3.68    3.69    3.686   100%
2   table.insert L  3.75    3.75    3.75    3.75    3.74    3.748   101.68%
3   use counter     0.86    0.85    0.85    0.86    0.85    0.854   23.17%
4   use length      3.71    3.71    3.71    3.71    3.71    3.71    100.65%
--]]------------------------

結(jié)論

當(dāng)需要生成一個(gè)數(shù)組桐汤,并往數(shù)組的尾部添加數(shù)據(jù)時(shí),應(yīng)當(dāng)盡可能的使用計(jì)數(shù)器靶壮,如果沒辦法使用計(jì)數(shù)器怔毛,也應(yīng)當(dāng)使用#運(yùn)算符先求出數(shù)組的長(zhǎng)度,然后使用計(jì)數(shù)器插入數(shù)組腾降。

轉(zhuǎn):https://github.com/flily/lua-performance/blob/master/Guide.zh.md

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拣度,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子螃壤,更是在濱河造成了極大的恐慌抗果,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奸晴,死亡現(xiàn)場(chǎng)離奇詭異冤馏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)寄啼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門逮光,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人墩划,你說我怎么就攤上這事涕刚。” “怎么了乙帮?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵杜漠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我察净,道長(zhǎng)驾茴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任氢卡,我火速辦了婚禮锈至,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘异吻。我一直安慰自己裹赴,他們只是感情好喜庞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棋返,像睡著了一般延都。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上睛竣,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天晰房,我揣著相機(jī)與錄音,去河邊找鬼射沟。 笑死殊者,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的验夯。 我是一名探鬼主播猖吴,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼挥转!你這毒婦竟也來了海蔽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤绑谣,失蹤者是張志新(化名)和其女友劉穎党窜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體借宵,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幌衣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壤玫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豁护。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垦细,靈堂內(nèi)的尸體忽然破棺而出择镇,到底是詐尸還是另有隱情挡逼,我是刑警寧澤括改,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站家坎,受9級(jí)特大地震影響嘱能,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜虱疏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一惹骂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧做瞪,春花似錦对粪、人聲如沸右冻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纱扭。三九已至,卻和暖如春儡遮,著一層夾襖步出監(jiān)牢的瞬間乳蛾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工鄙币, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肃叶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓十嘿,卻偏偏與公主長(zhǎng)得像因惭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绩衷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,783評(píng)論 0 38
  • 指令集 lua_capture_error_log lua_use_default_type lua_malloc...
    吃瓜的東閱讀 11,999評(píng)論 0 2
  • 第一篇 語(yǔ)言 第0章 序言 Lua僅讓你用少量的代碼解決關(guān)鍵問題筛欢。 Lua所提供的機(jī)制是C不擅長(zhǎng)的:高級(jí)語(yǔ)言,動(dòng)態(tài)...
    testfor閱讀 2,666評(píng)論 1 7
  • 函數(shù)有兩種用途: 完成指定任務(wù)唇聘,此時(shí)函數(shù)作為調(diào)用語(yǔ)句使用版姑。 計(jì)算并返回值,此時(shí)函數(shù)作為賦值語(yǔ)句的表達(dá)式使用迟郎。 調(diào)用...
    JunChow520閱讀 3,749評(píng)論 0 3
  • 夏天到了剥险,我們很多人都喜歡在車?yán)锩娣鸥鞣N各樣的空氣清新劑,我呢也從網(wǎng)上花了100多塊錢買了一盒境界1號(hào)的空氣清新劑...
    文致斌閱讀 91評(píng)論 0 0