最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

幾種Lua優(yōu)化技巧

2022-10-01 11:43 作者:Fuxfantx  | 我要投稿

Lua優(yōu)化技巧選講

內(nèi)存優(yōu)化

眾所周知,Lua使用的是糟糕程度僅次于“手動分配堆內(nèi)存、指針亂飄”的內(nèi)存模型——GC模型。這意味著,對于Table、String等復(fù)雜類型,只要你搞丟了相應(yīng)的引用名,對應(yīng)的東西就會徹底變成垃圾一團(tuán),并且不會自動釋放,也無法手動要求釋放,只能等著垃圾回收器去回收了。

舉一個簡單的栗子AwA

a = {x = 100, ? ?y = 200}

a = {x = 300, ? ?y = 400}

這里實際上我們構(gòu)造了兩個Table,然后第一個Table的引用(一開始是a)就這么被我們搞丟了,然后第一個Table就變成垃圾了。

一般情況下隨意點也沒啥大的問題,但我們在Defold做的游戲一般要跑60fps,一些音游甚至跑120fps,考慮這樣一個情境:

function update(self,dt)

? ?for k=1,60 do

? ? ? ?a = {

? ? ? ? ? ?x = k-1,

? ? ? ? ? ?y = k+1

? ? ? ?}

? ? ? ?b = {

? ? ? ? ? ?x = k+10,

? ? ? ? ? ?y = k-10

? ? ? ?}

? ? ? ?self.my_x += a.x*b.y

? ? ? ?self.my_y += a.y*b.x

? ?end

end

一秒跑完那60幀,就制造了7200個表的垃圾,這下非常可觀了。

因此,多復(fù)用現(xiàn)有的表

local a = {

??????x = 0,

????? y = 0

??????}

local b = {

? ????x = 0,

???? ?y = 0

???? ?}

function update(self,dt)

? ?for k=1,60 do

? ? ? ?a.x = k-1

? ? ? ?a.y = k+1

? ? ? ?b.x = k+10

? ? ? ?b.y = k-10

? ?end

? ?self.my_x += a.x*b.y

? ?self.my_y += a.y*b.x

end

這下update一個表都不會造了www

另外,大家提得比較多的就是String不可變的問題,先給個不好的例子:

names = {"甲","乙","丙","丁","戊"}

ages = {19,21,20,19,18}

position = "中南大學(xué)"

for i,value in ipairs(names) do

? ?print(value .. "," .. ages[i] .. "歲,現(xiàn)就職于" .. position)

end


-- 甲,19歲,現(xiàn)就職于中南大學(xué)

-- 乙,21歲,現(xiàn)就職于中南大學(xué)

-- 丙,20歲,現(xiàn)就職于中南大學(xué)

-- 丁,19歲,現(xiàn)就職于中南大學(xué)

-- 戊,18歲,現(xiàn)就職于中南大學(xué)


為了打出這5句狗話,系統(tǒng)額外申請了30塊堆區(qū)內(nèi)存用來存放拼接過程。。。。。。

然后這里有個好點的例子:

names = {"甲","乙","丙","丁","戊"}

ages = {19,21,20,19,18}

position = "中南大學(xué)"

pre_str = "%s,%d歲,現(xiàn)就職于%s"

for i=1,#names do

? ?local concat = string.format(pre_str, names[i], ages[i], position)

? ?print(concat)

end

現(xiàn)在我們引入print( collectgarbage("count") )統(tǒng)計下內(nèi)存消耗:

截屏2022-09-19 12.57.12.png

差距還是挺大的,嗯。

最后,Lua非常友善地提供了讓GC間歇運行的方法,考慮到游戲引擎往往提供更新函數(shù),潔癖比較深重的人推薦試試:

function init(self)

? ?collectgarbage("stop") ?--關(guān)閉GC觸發(fā)

? ?-- 然后干點別的

end


function update(self,dt)

? ?collectgarbage("step",4) ?--每幀回收相當(dāng)于4KB內(nèi)存的垃圾

? ?-- 這里干點別的

end


最后的最后……

Lua如果像上面一樣單步跑GC的話,可以借助協(xié)程把GC任務(wù)剝離到單獨的線程,但這樣做意義不大;如果是正常的倍率步進(jìn)GC的話,讓GC單獨占一個線程需要魔改Lua的本體——有點超過我們的討論范圍了,抱歉。

運行效率優(yōu)化

首先是緩存這件事,門道很深,先從Local部分講起。

有關(guān)Lua的一切,都是圍繞著“Table”這樣一種數(shù)據(jù)結(jié)構(gòu)展開的。舉例來說,為了允許把函數(shù)存儲在表內(nèi),Lua做了First-Class函數(shù),甚至閉包之類的支持。

我們直接在命令行打開Lua走交互式編程,寫點這種東西:

a = 2

b = 6

c = a*110 + b

print(c) ?--226

那么……這里的a、b、c在什么地方呢?答曰:儲存在全局表_G里。

Lua的Table是一種相當(dāng)之兼容并蓄的數(shù)據(jù)結(jié)構(gòu),而最常見的(非嵌套)Table大概長這兩種樣子:

hero = {

? ?hp = 100,

? ?mp = 100,

? ?weapon = "sword"

? ?}

main_subjects = {"Chinese","Math","English"}

前者對應(yīng)了一些語言的哈希表、字典,后者對應(yīng)一些動態(tài)語言的Array容器。

然后我們嘗試從這兩種Table里面拿點東西:

-- 書接上文

print(hero.hp)?-- 100

print(main_subjects[1]) ?-- "Chinese"

取hero表里的hp字段時,Lua首先會把"hp"這么一串字符作哈希化處理,然后在描述“hero”這個表的Hash Map里逐個問詢其中的元素,看看指向哪塊內(nèi)存。

于是,執(zhí)行任何東西都無法避免這么一個查表輪詢的過程,相當(dāng)?shù)托?。因此,Lua的虛擬機提供了一種類似于CPU“寄存器”的東西,并且把它用local這么一個關(guān)鍵字暴露了出來,順便為其增加了作用域相關(guān)的規(guī)則。

這也是為什么Lua的局部變量全都要顯式聲明。

就Defold引擎的Lua腳本而言,更新、輸入、消息函數(shù)調(diào)用頻度很高,對這些地方做Local緩存優(yōu)化效果會很明顯。

這里介紹一下個人比較傾向的優(yōu)化方式,先撿一段未經(jīng)優(yōu)化的腳本出來:

function on_message(self, message_id, message, sender)

? ?if message_id == hash("collision_response") then

? ? ? ?msg.post("main#gui", "add_score", {amount = score})

? ? ? ?particlefx.play("#pickup")

? ? ? ?go.delete()

? ?end

end

接著先做一下Local優(yōu)化,以及Table復(fù)用優(yōu)化:

local haxi = hash

local msg_post = msg.post

local particlefx_play = particlefx.play

local go_delete = go.delete

local scr_table = {amount = score}

function on_message(self, message_id, message, sender)

? ?if message_id == haxi("collision_response") then

? ? ? ?scr_table["amount"] = score

? ? ? ?msg_post("main#gui", "add_score", score)

? ? ? ?particlefx_play("#pickup")

? ? ? ?go_delete()

? ?end

end

可以看到,基本上都是體力活。。。。。。

就Defold而言,hash值、定位地址也是可以緩存的,這里進(jìn)一步對hash值作一次緩存:

local collision_response = hash("collision_response")

local msg_post = msg.post

local particlefx_play = particlefx.play

local go_delete = go.delete

local scr_table = {amount = score}

function on_message(self, message_id, message, sender)

? ?if message_id == collision_response then

? ? ? ?scr_table["amount"] = score

? ? ? ?msg_post("main#gui", "add_score", score)

? ? ? ?particlefx_play("#pickup")

? ? ? ?go_delete()

? ?end

end

String其實也可以預(yù)存一下,但是收益不大——因為Lua在識別到重復(fù)的String時,會自動轉(zhuǎn)換為對原有String的引用,并不存在同一String多次開辟內(nèi)存的行為。因此,預(yù)存String可以稍微提升一點點性能。

但就Defold采用的LuaJIT而言,Local變量過多可能會導(dǎo)致JIT放棄編譯,會有些得不償失,因此String預(yù)存的優(yōu)先級應(yīng)該靠后一些。

然后讓我們看向main_subjects[1]部分。這個語句恰好就是main_subject這個表的第一個元素,針對這種情況應(yīng)該就沒有必要再開個Hash Map然后大找特找了。

Lua在設(shè)計時也考慮到了這一點。因此,Table在內(nèi)部其實區(qū)分成了Array Part和Hash Part,并且Array Part不需要查表的這個步驟、和數(shù)組更像一點。

因此,多使用Array Part;然后,尊重一下Array的有序性,盡量預(yù)先定好Array的大小不要輕易改變。

此外,LuaJIT提供了Array預(yù)分配大小的支持:

local table_new = require "table.new"

local my_new_array = table_new(100,0)

-- 新建一個Array部分預(yù)分配100個元素、Hash部分不預(yù)分配元素的Table

-- my_new_array是剛剛新建的Table的引用

但是Defold Script不能引入這個函數(shù),洗洗睡吧()

接下來讓我們談?wù)劚闅v。在LuaJIT的世界里,for i=1,#tablefor k,v in ipairs(table)效果差不多,都遠(yuǎn)遠(yuǎn)優(yōu)于for k,v in pairs(table)。原因無他,LuaJIT對pairs()的JIT支持還是Not Yet Implemented。。。。。。

最后,不推薦硬寫OOP。Lua對OOP優(yōu)化遠(yuǎn)低于正經(jīng)OOP語言,建議選取更扁平的數(shù)據(jù)結(jié)構(gòu)。

同樣給個糟糕的例子:

--創(chuàng)建一個類,表示四邊形

local RectAngle = {length, width, area}

--聲明類名和類成員變量

function RectAngle: new (len,wid)

--聲明新建實例的New方法

local o = {

? ? ? --設(shè)定各個項的值

? ? ? length = len or 0,

? ? ? width = wid or 0,

? ? ? area =len*wid

???? ?}

setmetatable(o,{__index = self} )?--將自身的表映射到新new出來的表中 ? return o

end

然后來個比較簡單、優(yōu)化的例子,假設(shè)我們最后用到100個矩形:

local table_new = require "table.new"

local rect_length = table_new(100,0)

local rect_width = table_new(100,0)

local rect_area = table_new(100,0)

local rect_num = 0

function rect_new(length,width)

? ?rect_num += 1

? ?rect_length[rect_num] = length or 0

? ?rect_width[rect_num] = width or 0

? ?rect_area[rect_num] = length*width or 0

? ?return rect_num

end


function rect_get(id)

? ?return rect_length[id],rect_width[id],rect_area[id]

end


溜了溜了(

幾種Lua優(yōu)化技巧的評論 (共 條)

分享到微博請遵守國家法律
高唐县| 稷山县| 常州市| 富民县| 屯昌县| 安岳县| 九江县| 营口市| 海伦市| 额敏县| 贵南县| 合肥市| 太仆寺旗| 桃园市| 安多县| 德安县| 呼伦贝尔市| 通河县| 柏乡县| 宜宾县| 东阳市| 电白县| 介休市| 读书| 金坛市| 育儿| 潜山县| 江油市| 白银市| 湖口县| 建德市| 历史| 广西| 霍林郭勒市| 潮安县| 西贡区| 平邑县| 田东县| 布尔津县| 福鼎市| 神木县|