RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Lua5.4源碼剖析—性能優(yōu)化與原理分析

冬至子 ? 來源:碼農(nóng)成長(zhǎng)寶典 ? 作者:碼農(nóng)小濤哥 ? 2023-06-01 15:09 ? 次閱讀

測(cè)試設(shè)備

1)本文中的數(shù)據(jù)是基于我的個(gè)人電腦MacBookPro:4核2.2GHz的i7處理器;

2)在時(shí)間的測(cè)量上,為了能精確到毫秒級(jí)別,我使用了Lua自帶的os.clock()函數(shù),它返回的是一個(gè)浮點(diǎn)數(shù),單位是秒數(shù),乘1000就是對(duì)應(yīng)的毫秒數(shù),本文測(cè)試數(shù)據(jù)均以毫秒為單位。另外本文為了放大不同寫法數(shù)據(jù)的性能差異,在樣例比較中,通常會(huì)循環(huán)執(zhí)行較多的次數(shù)來累計(jì)總的消耗進(jìn)行比較。

3星優(yōu)化

推薦程度:極力推薦,使用簡(jiǎn)單,且優(yōu)化收益明顯,每個(gè)Lua使用者都應(yīng)該遵循。

1)全類型通用CPU優(yōu)化——高頻訪問的對(duì)象,應(yīng)先賦值給一個(gè)local變量再進(jìn)行訪問

測(cè)試樣例 :用循環(huán)模擬高頻訪問,每輪循環(huán)中訪問調(diào)用math.random函數(shù),用它來創(chuàng)建一個(gè)隨機(jī)數(shù)。

備注 :這里的對(duì)象,是Lua的所有類型,包括Booeal、Number、Table、Function、Thread等等。

錯(cuò)誤寫法

for i = 1, 1000000 do local randomVal = math.random(0, 10000)end

正確寫法(把函數(shù)math.random賦值給local變量)

local random = math.randomfor i = 1, 1000000 do local randomVal = random(0, 10000)end

測(cè)試樣例及性能差異(循環(huán)100 0000次消耗):

圖片

結(jié)果(耗時(shí)減少14毫秒):

圖片

原理分析

非local局部變量,即UpValue(全局變量其實(shí)也是記錄在名為_ENV的第一個(gè)Table類型的UpValue中)的訪問通常需要更多的指令操作,如果對(duì)應(yīng)的UpValue是個(gè)Table的話,還需要進(jìn)入Table的復(fù)雜的數(shù)據(jù)查詢流程。而local變量的訪問,只需要一條簡(jiǎn)單寄存器讀取指令,所以能節(jié)省出數(shù)據(jù)查詢帶來的性能開銷。

OpCode對(duì)比

錯(cuò)誤寫法:每次循環(huán)需要執(zhí)行下面5條指令:

圖片

正確寫法:每次循環(huán)只需要執(zhí)行下面4條指令操作,原本復(fù)雜的函數(shù)從Table中查詢定位的操作,替換成了簡(jiǎn)單的OP_MOVE寄存器賦值操作:圖片

2)String類型CPU與內(nèi)存優(yōu)化——要拼接的字符串如果比較多,應(yīng)使用table.conat函數(shù)

測(cè)試樣例 :多次循環(huán),每次拼接一個(gè)0到10000范圍的隨機(jī)數(shù)到目標(biāo)字符串上。

錯(cuò)誤寫法

local random = math.randomlocal sumStr = ""for i = 1, 10000 do  sumStr = sumStr .. tostring(random(0, 10000))end

正確寫法

local random = math.randomlocal sumStr = ""local concat_list = {}for i = 1, 10000 do  table.insert(concat_list, tostring(random(0, 10000)))end

測(cè)試樣例及性能差異(循環(huán)1 0000次消耗):

由于本樣例會(huì)產(chǎn)生臨時(shí)內(nèi)存數(shù)據(jù),我們統(tǒng)計(jì)時(shí)間消耗的時(shí)候要把GC的時(shí)間也算上,所以都先調(diào)用了collectgarbage("collect")進(jìn)行強(qiáng)制的GC。

圖片

結(jié)果(循環(huán)中允許自動(dòng)GC,節(jié)省18毫秒)

圖片

特殊情況

若在兩種寫法的開頭都加上collectgarbage("stop"),即把上述代碼注釋部分取消,在循環(huán)中就不會(huì)自動(dòng)觸發(fā)GC,有些開發(fā)者會(huì)控制GC時(shí)機(jī),例如只在Loading過場(chǎng)的時(shí)候調(diào)用GC,所以實(shí)際中會(huì)有這種情況出現(xiàn)。

我們之前學(xué)習(xí)Lua字符串源碼的時(shí)候知道,字符串的使用是有自動(dòng)的緩存機(jī)制的,當(dāng)前已經(jīng)緩存住的未被GC的字符串越多,后續(xù)字符串的查詢效率越低下,此時(shí)運(yùn)算結(jié)果如下(循環(huán)中不自動(dòng)GC,錯(cuò)誤寫法時(shí)間消耗大增,正確寫法節(jié)省100毫秒):圖片

另外,在循環(huán)中可自動(dòng)GC的情況下,把測(cè)試次數(shù)由10000乘以10改成100000次時(shí),正確寫法時(shí)間消耗幾乎成比例增長(zhǎng)10倍,而錯(cuò)誤寫法則會(huì)隨著拼接后的字符串越來越長(zhǎng),時(shí)間消耗指數(shù)性急劇上升,增長(zhǎng)了50倍:圖片

源碼分析

每次有兩個(gè)字符串使用".."連接符進(jìn)行拼接的時(shí)候,會(huì)生成一個(gè)新的中間字符串,隨著拼接的字符串長(zhǎng)度越長(zhǎng),內(nèi)存占用與CPU消耗越大。在上述測(cè)試樣例中,我們只需要最終所有字符串的拼接結(jié)果,這些中間產(chǎn)物其實(shí)對(duì)我們是沒有意義的;而table.concat函數(shù)則是可以一次性拼接table中所有的字符串,無中間產(chǎn)物,所以會(huì)對(duì)內(nèi)存與CPU有較大的優(yōu)化。

table.concat函數(shù)的實(shí)現(xiàn)如下,會(huì)先創(chuàng)建一個(gè)緩存區(qū),把所有字符串都放到緩沖區(qū)中,放入緩沖區(qū)的時(shí)候雖然也可能觸發(fā)緩沖區(qū)擴(kuò)容,但比起".."連接符每次創(chuàng)建一個(gè)新的字符串消耗要小很多,然后拼接完成后返回僅僅一個(gè)新的字符串:

圖片

不適用的場(chǎng)景:

要拼接的字符串?dāng)?shù)量較少、長(zhǎng)度較短,此時(shí)可能創(chuàng)建并GC回收一個(gè)Table的消耗會(huì)比直接用".."更大。

3)String類型CPU與內(nèi)存優(yōu)化——使用".."連接符拼接字符串應(yīng)盡可能一次性把更多的待拼接的子字符串都寫上

測(cè)試樣例 :把數(shù)字0到5拼接成"012345"。

錯(cuò)誤寫法1

**

local sumStr = "0"sumStr = sumStr .. "1"sumStr = sumStr .. "2"sumStr = sumStr .. "3"sumStr = sumStr .. "4"sumStr = sumStr .. "5"

**

錯(cuò)誤寫法2(加括號(hào)其實(shí)跟獨(dú)立一行運(yùn)算效果差不多,只是節(jié)省了一個(gè)賦值操作的OpCode)

local sumStr = (((("0" .. "1") .. "2") .. "3") .. "4") .. "5"

正確寫法

local sumStr = "0" .. "1" .. "2" .. "3" .. "4" .. "5"

測(cè)試樣例及性能差異(循環(huán)100 0000次消耗):

圖片

結(jié)果(耗時(shí)減少了100多毫秒,同時(shí)運(yùn)算中內(nèi)存占用也會(huì)大幅減少):

圖片

原理分析: ".."連接符號(hào)對(duì)應(yīng)的OpCode為OP_CONCAT,它的功能不僅僅是連接兩個(gè)字符串,而是會(huì)把以".."連接符號(hào)相鄰的所有字符串結(jié)合為一個(gè)操作批次,把它們一次性連接起來,同時(shí)也會(huì)避免產(chǎn)生中間連接臨時(shí)字串符。

由于字符串緩存機(jī)制的存在,在上述錯(cuò)誤寫法1、2中,會(huì)產(chǎn)生無用中間字符串產(chǎn)物:"01","012","0123","0124";它們會(huì)導(dǎo)致內(nèi)存的上升和加重后續(xù)GC的工作量。而正確寫法則只會(huì)產(chǎn)生最終"012345"這僅僅一個(gè)字符串。

OP_CONCAT實(shí)現(xiàn)核心邏輯如下,會(huì)先計(jì)算出結(jié)果字符串長(zhǎng)度,然后一次性創(chuàng)建出對(duì)應(yīng)大小的字符串緩沖區(qū),把所有子串放進(jìn)去,然后返回僅僅一個(gè)新的字符串:

圖片

OpCode對(duì)比:

錯(cuò)誤寫法2:

圖片

正確寫法(一條OP_CONCAT就可以完成所有拼接操作):

圖片

4)Table類型CPU優(yōu)化——盡量在table構(gòu)造時(shí)完成數(shù)據(jù)初始化

測(cè)試樣例 :創(chuàng)建一個(gè)初始值為1, 2, 3的Table;

錯(cuò)誤寫法:

local t = {}t[1] = 1t[2] = 2t[3] = 3

正確寫法:

local t = {1, 2, 3}

測(cè)試樣例及性能差異(循環(huán)10 0000次消耗):

圖片

結(jié)果(節(jié)省35毫秒):

圖片

原理分析:

Table在使用"{}"列表形式進(jìn)行賦值的時(shí)候,會(huì)把其中數(shù)組部分的所有數(shù)據(jù)合并成一條OP_SETLIST指令(哈希部分無法通過此方式優(yōu)化),在里面批量一次性完成所有數(shù)組元素的賦值。而使用t[i]=value的形式進(jìn)行賦值,則每次都會(huì)調(diào)用一條OpCode,會(huì)生成更多的OpCode指令。

OP_SETLIST實(shí)現(xiàn)如下:

圖片

OpCode對(duì)比:

錯(cuò)誤寫法:

圖片

正確寫法(生成的OpCode的數(shù)量雖然更多了,但OP_LOADI的消耗遠(yuǎn)比上面的OP_SETI要小):圖片

5)Table類型內(nèi)存優(yōu)化——Table關(guān)聯(lián)到類似excel的只讀數(shù)據(jù)表時(shí),頻繁出現(xiàn)的復(fù)雜類型數(shù)據(jù)可以單獨(dú)定義為一個(gè)local變量進(jìn)行復(fù)用

測(cè)試樣例 :4條射線,射線用Table進(jìn)行表示,它有一個(gè)起點(diǎn)坐標(biāo)和一個(gè)方向;多數(shù)時(shí)候起點(diǎn)為(0, 0, 0)坐標(biāo)。

錯(cuò)誤寫法:

local Ray1 = {  origin = {x = 0, y = 0, z = 0},  direction = {x = 1, y = 0, z = 0},}
local Ray2 = { origin = {x = 0, y = 0, z = 0}, direction = {x = -1, y = 0, z = 0},}
local Ray3 = { origin = {x = 1, y = 0, z = 0}, direction = {x = 1, y = 0, z = 0},}
local Ray4 = { origin = {x = 1, y = 0, z = 0}, direction = {x = -1, y = 0, z = 0},}

正確寫法:

local x0_y0_z0 = {x = 0, y = 0, z = 0}local x1_y0_z0 = {x = 1, y = 0, z = 0}local xn1_y0_z0 = {x = -1, y = 0, z = 0}
local Ray1 = { origin = x0_y0_z0, direction = x1_y0_z0,}
local Ray2 = { origin = x0_y0_z0, direction = xn1_y0_z0,}
local Ray3 = { origin = x1_y0_z0, direction = x1_y0_z0,}
local Ray4 = { origin = x1_y0_z0, direction = xn1_y0_z0,}

原理分析:

正確寫法在數(shù)據(jù)賦值的時(shí)候效率也會(huì)更高,不過該優(yōu)化更多的是針對(duì)內(nèi)存。被復(fù)用的復(fù)雜結(jié)構(gòu)對(duì)象單獨(dú)定義,然后用到的每個(gè)對(duì)象實(shí)例只存儲(chǔ)它的一個(gè)引用,避免了重復(fù)的數(shù)據(jù)定義,能極大降低內(nèi)存占用。

6)Table類型內(nèi)存優(yōu)化——Table關(guān)聯(lián)到類似excel的只讀數(shù)據(jù)表時(shí),默認(rèn)值的查詢可以使用元表和__index元方法

測(cè)試樣例 :班級(jí)內(nèi)部有一個(gè)學(xué)生信息表,學(xué)生有姓名,年齡,年級(jí)。該界學(xué)生默認(rèn)且最多都是12歲,上6年級(jí)。本例班級(jí)中有3名學(xué)生,其中2名學(xué)生信息都與默認(rèn)值一致,另外一名年紀(jì)與默認(rèn)值不一致。

錯(cuò)誤寫法:

local Students1 = {  name = "小明",  age = 12,  grade = 6}
local Students2 = { name = "小紅", age = 12, grade = 6}
local Students3 = { name = "小剛", -- 小剛年紀(jì)比同一屆的其它同學(xué)大一點(diǎn)點(diǎn) age = 13, grade = 6}

正確寫法:

local StudentsDefault = {__index = {  age = 12,  grade = 6}}
local Students1 = { name = "小明",}setmetatable(Students1, StudentsDefault)
local Students2 = { name = "小紅",}setmetatable(Students2, StudentsDefault)
local Students3 = { name = "小剛", -- 小剛年紀(jì)比同一屆的其它同學(xué)大一點(diǎn)點(diǎn) age = 13, }setmetatable(Students3, StudentsDefault)

原理分析:

把所有對(duì)象的字段默認(rèn)值獨(dú)立出來進(jìn)行定義,不用每個(gè)對(duì)象都定義一堆相同的字段,當(dāng)字段與默認(rèn)值不一致時(shí)才需要重新定義,減少了重復(fù)數(shù)據(jù)的內(nèi)存占用。當(dāng)對(duì)一個(gè)對(duì)象實(shí)例查詢其字段數(shù)據(jù)的時(shí)候,若字段未定義,則代表該字段沒有或者采用了默認(rèn)值,此時(shí)會(huì)通過元方法__index在默認(rèn)值元表對(duì)象中進(jìn)行查詢,以多一層的數(shù)據(jù)查詢性能開銷換來內(nèi)存的大幅減少。

7)Function類型 內(nèi)存、堆棧優(yōu)化—— 函數(shù)遞歸調(diào)用時(shí)盡量使用尾調(diào)用的方式

測(cè)試樣例 :以函數(shù)遞歸的方式求1,2,...到n的和。

錯(cuò)誤寫法:

local function BadRecursionFunc(n)  if n > 1 then    return n + BadRecursionFunc(n - 1)  end  return 0end

正確寫法:

local function GoodRecursionFunc(sum, n)  if n > 1 then    return GoodRecursionFunc(sum + n, n - 1)  end  return sum, 0end

當(dāng)n為10 0000:

結(jié)果如下(節(jié)省14毫秒):

圖片

當(dāng)n為100 0000:

錯(cuò)誤寫法會(huì)直接報(bào)錯(cuò),Lua運(yùn)行堆棧溢出了:

圖片

而正確寫法能正常運(yùn)算出結(jié)果,消耗為26毫秒:

圖片

原理分析:

Lua的函數(shù)調(diào)用在return 的時(shí)候,若直接單純調(diào)用另外一個(gè)函數(shù),則會(huì)使用尾調(diào)用的方式,在尾調(diào)用模式下,會(huì)復(fù)用之前的CallInfo與函數(shù)堆棧。所以無論樣例中的n多大、遞歸層次多深,都不會(huì)造成堆棧溢出。

OpCode實(shí)現(xiàn)對(duì)比,OP_CALL為普通函數(shù)調(diào)用,會(huì)創(chuàng)建CallInfo;OP_TAILCALL為尾調(diào)用,直接復(fù)用CallInfo:

圖片

8)Thread類型CPU、內(nèi)存優(yōu)化——不需要多個(gè)協(xié)程并行運(yùn)行時(shí),盡量復(fù)用同一個(gè)協(xié)程,協(xié)程的創(chuàng)建與銷毀開銷較大

Lua中Thread類型其實(shí)就是協(xié)程(Coroutine),每一個(gè)協(xié)程的創(chuàng)建都會(huì)創(chuàng)建一個(gè)lua_State對(duì)象,該對(duì)象字段繁多,初始化或者銷毀邏輯復(fù)雜,定義如下圖:

圖片

測(cè)試樣例 :用協(xié)程執(zhí)行3個(gè)不同的函數(shù),但不要求它們同時(shí)并行執(zhí)行。

錯(cuò)誤寫法:

local function func1()end
local function func2()end
local function func3()end
local ff = coroutine.wrap(func1)f()f = coroutine.wrap(func2)f()f = coroutine.wrap(func3)f()

正確寫法(復(fù)用同一個(gè)協(xié)程,下面的co變量):

local function func1()end
local function func2()end
local function func3()end
local co = coroutine.create(function(f) while(f) do f = coroutine.yield(f()) endend)
coroutine.resume(co, func1)coroutine.resume(co, func2)coroutine.resume(co, func3)

測(cè)試樣例及性能差異(循環(huán)1 0000次消耗):

圖片

結(jié)果(節(jié)省18毫秒):

圖片

原理分析:

每一個(gè)協(xié)程的創(chuàng)建都會(huì)創(chuàng)建出一個(gè)lua_State對(duì)象,如上面的定義,它字段繁多,而且邏輯復(fù)雜,創(chuàng)建與銷毀的開銷極大。協(xié)程創(chuàng)建的時(shí)候最終會(huì)創(chuàng)建Thread類型對(duì)象,源碼實(shí)現(xiàn)如下,會(huì)創(chuàng)建一個(gè)lua_State:

圖片

所以通過特定的寫法重復(fù)使用同一個(gè)協(xié)程能極大優(yōu)化性能與內(nèi)存。

2星優(yōu)化

推薦程度:較為推薦,使用簡(jiǎn)單,但優(yōu)化收益一般。

1)Table類型CPU優(yōu)化——數(shù)據(jù)插入盡量使用t[key]=value的方式而不使用table.insert函數(shù)

測(cè)試樣例 :把1到1000000之間的數(shù)字插入到table中。

錯(cuò)誤寫法:

local t = {}
local table_insert_func = table.insert
for i = 1, 1000000 do  
  table_insert_func(t, i)
end

正確寫法:

local t = {}for i = 1, 1000000 do  t[i] = iend

測(cè)試樣例及性能差異(循環(huán)100 0000次消耗):

圖片

結(jié)果(節(jié)省40毫秒):

圖片

原理分析:

通過t[integer_key]=value或者t[string_key]=value的方式插入數(shù)據(jù),只會(huì)生成OP_SETI或者OP_SETFIELD一條OpCode,會(huì)直接往Table的數(shù)組或哈希表部分插入數(shù)據(jù);而通過table.insert函數(shù)插入數(shù)據(jù),會(huì)多出以下這堆邏輯:1)table.insert這一個(gè)函數(shù)的查詢定位(上述樣例中使用local緩存優(yōu)化了);

2)暫不考慮table.insert函數(shù)的邏輯,單純函數(shù)的調(diào)用就會(huì)有創(chuàng)建CallInfo,參數(shù)準(zhǔn)備,返回處理等固定開銷;3)table.insert函數(shù)對(duì)參數(shù)的判斷與table類型對(duì)象的獲取與轉(zhuǎn)化;4)為了在table的最尾端插入數(shù)據(jù),需要計(jì)算table當(dāng)前的數(shù)組部分長(zhǎng)度;

OpCode對(duì)比:

錯(cuò)誤寫法(table.insert):

圖片

正確寫法(t[i]=value):

圖片

明顯使用t[key]=value方式插入數(shù)據(jù)能減少較多的指令或邏輯,對(duì)CPU優(yōu)化有一定效果。

1星優(yōu)化

推薦程度:一般推薦,某種程度上來說屬于吹毛求疵,有一定優(yōu)化收益,但可能影響可讀性、或者需要特定場(chǎng)景配合實(shí)現(xiàn)。

1)全類型通用CPU優(yōu)化——變量盡量在定義的同時(shí)完成賦值

測(cè)試樣例: 把變量a初始化為一個(gè)整數(shù)。

錯(cuò)誤寫法:

local aa = 1

正確寫法:

local a = 1

測(cè)試樣例及性能差異(循環(huán)100 0000次消耗):

圖片

結(jié)果(耗時(shí)減少了2毫秒):

圖片

原理分析:

錯(cuò)誤寫法中第一行的local a其實(shí)與local a = nil是等價(jià)的,底層會(huì)生成OP_LOADNIL這條OpCode,來創(chuàng)建出一個(gè)nil對(duì)象,第二行則又調(diào)用了另一條OpCode完成真正賦值;而正確寫法中則會(huì)省略掉第一條的OP_LOADNIL。

OpCode對(duì)比:

錯(cuò)誤寫法(2條OpCode,分別對(duì)應(yīng)nil類型的創(chuàng)建與整數(shù)的加載賦值):

圖片

正確寫法(只生成一條整數(shù)加載的OpCode):

圖片

2)Nil類型CPU優(yōu)化——nil對(duì)象在定義的時(shí)候盡量相鄰,中間不要穿插其它對(duì)象

測(cè)試樣例 :定義6個(gè)變量,其中3個(gè)為nil,3個(gè)為整數(shù)100。

錯(cuò)誤寫法:

  local a = nil  local b = 100  local c = nil  local d = 100  local e = nil  local f = 100

正確寫法(把nil的賦值排在相鄰的位置):

  local a = nil  local c = nil  local e = nil  local b = 100  local d = 100  local f = 100

測(cè)試樣例及性能差異(循環(huán)100 0000次消耗):

圖片

結(jié)果(耗時(shí)減少了4毫秒):

圖片

原理分析:

賦值或者加載一個(gè)nil對(duì)象的OpCode為OP_LOADNIL,它支持把棧上面連續(xù)的一定數(shù)量的對(duì)象都設(shè)置為nil。若中間被隔開了,則后面的調(diào)用需要重新生成OP_LOADNIL。

OP_LOADNIL實(shí)現(xiàn):圖片

OpCode對(duì)比:

錯(cuò)誤寫法:

圖片

正確寫法(3條OP_LOADNIL合成了一條):

圖片

3)Function類型CPU優(yōu)化——不返回多余的返回值

測(cè)試樣例 :外部對(duì)函數(shù)進(jìn)行調(diào)用,只請(qǐng)求獲取第一個(gè)返回值。

錯(cuò)誤寫法(返回了多余的對(duì)象):

local function BadRetFunc()  local value1 = 1  local value2 = 2  local value3 = 3  local value4 = 4  local value5 = 5  return value1, value2, value3, value4, value5endlocal ret = BadRetFunc()

正確寫法(外部調(diào)用者需要多少個(gè)返回值就返回多少個(gè)):

local function GoodRetFunc()  local value1 = 1  local value2 = 2  local value3 = 3  local value4 = 4  local value5 = 5  return value1endlocal ret = GoodRetFunc()

測(cè)試樣例及性能差異(循環(huán)100 0000次消耗):

圖片

結(jié)果(節(jié)省12毫秒):

圖片

原理分析:

函數(shù)返回對(duì)應(yīng)的OpCode為OP_RETURN0,OP_RETURN1,OP_RETURN。它們的邏輯復(fù)雜度由小到大,消耗由小到大。返回值的增加一方面導(dǎo)致了調(diào)用更復(fù)雜的return的OpCode;另一方面則是調(diào)用這些return的OpCode之前,Lua需要先把所有返回值獲取并放到棧的頂部,會(huì)多執(zhí)行一些賦值操作。

OpCode對(duì)比:

錯(cuò)誤寫法(多了一些OP_MOVE指令的調(diào)用,另外return的OpCode為最復(fù)雜的OP_RETURN):

圖片

正確寫法(只調(diào)用了一條更簡(jiǎn)單的return的OpCode,只需要一個(gè)返回值的OP_RETURN1):

圖片

0星優(yōu)化

推薦程度:無效優(yōu)化,不會(huì)帶來任何性能提升,也不會(huì)有性能下降,通常使用這種寫法是為了更好的代碼可讀性。

1)全類型通用 可讀性優(yōu)化—— for循環(huán)的終止條件不需要提前緩存

測(cè)試樣例 :for循環(huán),結(jié)束條件需要復(fù)雜的才能計(jì)算出來,為一個(gè)復(fù)雜函數(shù)的返回值。

錯(cuò)誤寫法:

local function ComplexGetCount()  for i = 1, 100 do    local r = math.random()  end  return 100end
local len = ComplexGetCount()for j = 1, len doend

正確寫法(不需要刻意緩存循環(huán)終止條件):

local function ComplexGetCount()  for i = 1, 100 do    local r = math.random()  end  return 100end
for j = 1, ComplexGetCount() doend

測(cè)試樣例及性能差異(循環(huán)1 0000次消耗):

圖片

結(jié)果(消耗是一樣的):

圖片

原理分析:

有很多一些編程語言的循環(huán)語句中,會(huì)把結(jié)束條件視作一個(gè)動(dòng)態(tài)的值,每輪循環(huán)結(jié)束會(huì)重新計(jì)算該值,然后再判斷循環(huán)是否終止。而在Lua中,循環(huán)表達(dá)式的終止條件是循環(huán)開始前就計(jì)算好了,只會(huì)計(jì)算一次,循環(huán)的過程中不會(huì)再被改變。

所以如果你覺得提前把循環(huán)結(jié)束條件計(jì)算出來,意圖在每輪循環(huán)中避免重復(fù)計(jì)算,那么這個(gè)操作在Lua中是多余沒有意義的,因?yàn)長(zhǎng)ua語言本身就幫你做了這件事情。不過也正因如此,在Lua中無法在for 循環(huán)中動(dòng)態(tài)修改循環(huán)結(jié)束條件控件循環(huán)次數(shù)。

OpCode對(duì)比:

正確寫法(循環(huán)體內(nèi)不會(huì)再有計(jì)算結(jié)束條件表達(dá)式的操作了):

圖片

2)Nil類型可讀性優(yōu)化——初始化的時(shí)候顯示賦值和隱式賦值效果一樣

測(cè)試樣例 :定義一個(gè)nil類型變量a。

寫法1:

local a = nil

寫法2(一些人以為的更好的寫法):

local a

原理分析: 無效優(yōu)化,上面兩種寫法在底層實(shí)現(xiàn)中是完全一模一樣的。兩種寫法最終都只是生成一條OP_LOADNIL的OpCode,寫法1并不會(huì)因?yàn)槎嗔速x值符號(hào)就額外多生成其它的OpCode。

總結(jié)

本文我們從源碼虛擬機(jī)層面上對(duì)Lua語言多數(shù)較為有效的優(yōu)化手段進(jìn)行了一個(gè)學(xué)習(xí)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19259

    瀏覽量

    229651
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5336

    瀏覽量

    120230
  • 虛擬機(jī)
    +關(guān)注

    關(guān)注

    1

    文章

    914

    瀏覽量

    28160
  • Lua語言
    +關(guān)注

    關(guān)注

    0

    文章

    9

    瀏覽量

    1488
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Faster Transformer v2.1版本源碼解讀

    寫在前面 :本文將對(duì) Faster Transformer v2.1 版本源碼進(jìn)行解讀,重點(diǎn)介紹該版本基于 v1.0 和 v2.0 所做的優(yōu)化內(nèi)容,剖析源碼作者
    的頭像 發(fā)表于 09-19 11:39 ?1383次閱讀
    Faster Transformer v2.1版本<b class='flag-5'>源碼</b>解讀

    分享高性能Android應(yīng)用開發(fā)超清版PDF

    升陽著][電子工業(yè)出版社][2012.10][840頁]Android系統(tǒng)源代碼情景分析Android應(yīng)用UI設(shè)計(jì)模式Android應(yīng)用性能優(yōu)化Android應(yīng)用性能
    發(fā)表于 08-13 10:40

    PIC16LF1939的代碼性能分析

    家族不起作用。你能建議使用哪種代碼剖析工具嗎?我想分析代碼性能,函數(shù)執(zhí)行時(shí)間等。讓我知道是否有任何其他有效的方法進(jìn)行代碼分析分析代碼
    發(fā)表于 03-10 10:26

    AN0004—AT32 性能優(yōu)化

    、算法的執(zhí)行時(shí)間和重要數(shù)據(jù)的訪問頻率等信息有個(gè)大致的判斷。再結(jié)合工程內(nèi)容的實(shí)際情況具體分析,一步步進(jìn)行系統(tǒng)優(yōu)化,以達(dá)到提升性能的目的。實(shí)際優(yōu)化過程可分為如下幾個(gè)大的步驟:1) 在未
    發(fā)表于 08-15 14:38

    《現(xiàn)代CPU性能分析優(yōu)化》收到書了

    周一上班就收到《現(xiàn)代CPU性能分析優(yōu)化》這本書了,其實(shí)周六就送到了,只不過周末休息沒去公司,周一才領(lǐng)到,迫不及待的拆開看了一下。整本書不是很厚,只有205頁,全是精華,下面是目錄。從目錄就可以看
    發(fā)表于 04-17 17:06

    《現(xiàn)代CPU性能分析優(yōu)化》---精簡(jiǎn)的優(yōu)化

    《現(xiàn)代CPU性能分析優(yōu)化》是一本非常實(shí)用的書籍,對(duì)于從事性能關(guān)鍵型應(yīng)用程序開發(fā)和進(jìn)行系統(tǒng)底層優(yōu)化的技術(shù)人員來說是不可或缺的。這本書也很適合
    發(fā)表于 04-18 16:03

    《現(xiàn)代CPU性能分析優(yōu)化》--讀書心得筆記

    很榮幸拿到這本<<現(xiàn)代CPU性能分析優(yōu)化>>,花了幾天的時(shí)間瀏覽了一遍,書比較單薄,正文只有不到200頁,但是里面的內(nèi)容確是非常豐富的,一般
    發(fā)表于 04-24 15:31

    使用Arm Streamline分析樹莓派的性能

    在本指南中,我們將探索Linux應(yīng)用和系統(tǒng)性能分析,并學(xué)習(xí)如何找到一個(gè)系統(tǒng)正在花費(fèi)時(shí)間的地方。說明應(yīng)用程序和發(fā)現(xiàn)性能瓶頸有助于集中軟件優(yōu)化努力,以改善系統(tǒng)
    發(fā)表于 08-29 06:30

    源碼級(jí)和算法級(jí)的功耗測(cè)試與優(yōu)化

    源碼級(jí)和算法級(jí)的功耗測(cè)試與優(yōu)化 引言軟件設(shè)計(jì)中,代碼優(yōu)化是一件非常有意義的事情。優(yōu)化的本質(zhì)是對(duì)代碼進(jìn)行等價(jià)變換,使變換前后的代碼運(yùn)行結(jié)果相同,
    發(fā)表于 03-13 10:59 ?1007次閱讀
    <b class='flag-5'>源碼</b>級(jí)和算法級(jí)的功耗測(cè)試與<b class='flag-5'>優(yōu)化</b>

    STL源碼剖析的PDF電子書免費(fèi)下載

    學(xué)習(xí)編程的人都知道,閱讀、剖析名家代碼乃是提高水平的捷徑。源碼之前,了無秘密。大師們的縝密思維、經(jīng)驗(yàn)結(jié)晶、技術(shù)思路、獨(dú)到風(fēng)格,都原原本本體現(xiàn)在源碼之中。
    發(fā)表于 06-29 08:00 ?0次下載
    STL<b class='flag-5'>源碼</b><b class='flag-5'>剖析</b>的PDF電子書免費(fèi)下載

    Linux性能分析工具perf詳解

    系統(tǒng)級(jí)性能優(yōu)化通常包括兩個(gè)階段:性能剖析(performance profiling)和代碼優(yōu)化。
    發(fā)表于 05-25 08:55 ?5014次閱讀
    Linux<b class='flag-5'>性能</b><b class='flag-5'>分析</b>工具perf詳解

    Faster Transformer v1.0源碼詳解

    寫在前面:本文將對(duì) Nvidia BERT 推理解決方案 Faster Transformer 源碼進(jìn)行深度剖析,詳細(xì)分析作者的優(yōu)化意圖,并對(duì)源碼
    的頭像 發(fā)表于 09-08 10:20 ?966次閱讀
    Faster Transformer v1.0<b class='flag-5'>源碼</b>詳解

    SDAccel環(huán)境剖析和最優(yōu)化指南

    電子發(fā)燒友網(wǎng)站提供《SDAccel環(huán)境剖析和最優(yōu)化指南.pdf》資料免費(fèi)下載
    發(fā)表于 09-15 11:37 ?0次下載
    SDAccel環(huán)境<b class='flag-5'>剖析</b>和最<b class='flag-5'>優(yōu)化</b>指南

    【串口屏LUA教程】lua基礎(chǔ)學(xué)習(xí)(借鑒)

    【串口屏LUA教程】lua基礎(chǔ)學(xué)習(xí)(借鑒)
    發(fā)表于 04-29 13:02 ?5次下載

    GPRS的性能分析優(yōu)化

    電子發(fā)燒友網(wǎng)站提供《GPRS的性能分析優(yōu)化.pdf》資料免費(fèi)下載
    發(fā)表于 11-17 16:31 ?0次下載
    GPRS的<b class='flag-5'>性能</b><b class='flag-5'>分析</b>及<b class='flag-5'>優(yōu)化</b>
    RM新时代网站-首页