1.字符串
-
原理
- Lua 的字符串都是內(nèi)化的(internalized);這意味著字符串在 Lua 中都只有一份拷貝丧凤。每當(dāng)一個新字符串出現(xiàn)時募闲,Lua 會先檢查這個字符串是否已經(jīng)有一份拷貝,如果有愿待,就重用這份拷貝浩螺。內(nèi)化(internalization)使字符串比較及表索引這樣的操作變得非常快仍侥,但是字符串的創(chuàng)建會變慢要出。
- Lua 的字符串變量從來不會包含字符串本身,包含的只是字符串的引用农渊。這種實現(xiàn)加快了某些字符串操作患蹂。
簡單的說lua維護了一個table存放了所有的字符串。
任何新創(chuàng)建的字符串都會先hash去table查找一下砸紊,有的話直接返回字符串的引用传于。
沒有的話創(chuàng)建個新的字符串放入table,返回新的字符串的引用醉顽。
--引用列子
local value = "a"
value = value .."b"
print(value ) -- 輸出 'ab'
現(xiàn)在 lua string這個大table里沼溜,就有了 'a' 和 'ab' 兩個字符串了。value 實際引用的是 'ab'游添。
--字符串的連接列子
'x' .. 'y' .. 'z'
這種就是 x找一回系草,xy找一回,xyz找一回唆涝。
生成3個串 找3回最后table里有了'x'找都,'xy','xyz' 三個字符串了石抡。
-
優(yōu)化
使用運算符
' .. '
每次拼接都需要申請新的空間檐嚣,舊的result對應(yīng)的空間會在某時刻被Lua的垃圾回收期GC,且隨著result不斷增長,越往后會開辟更多新的空間嚎京,并進行拷貝操作嗡贺,產(chǎn)生更多需要被GC的空間,所以性能降低鞍帝。使用table.concat (table [, sep [, start [, end]]])函數(shù)
table.concat 底層拼接字符串的方式也是使用運算符.. 诫睬,但是其使用算法減少了使用運算符..的次數(shù),減少了GC帕涌,從而提高效率摄凡。
2.Table
-
原理
- Lua 實現(xiàn)表的算法頗為巧妙。每個表包含兩部分:數(shù)組(array)部分和哈希(hash)部
分蚓曼,數(shù)組部分保存的項(entry)以整數(shù)為鍵(key)亲澡,從 1 到某個特定的 n,(稍后會討
論 n 是怎么計算的纫版。)所有其他的項(包括整數(shù)鍵超出范圍的)則保存在哈希部分床绪。
顧名思義,哈希部分使用哈希算法來保存和查找鍵值其弊。它使用的是開放尋址(open
address)的表癞己,意味著所有的項都直接存在哈希數(shù)組里。鍵值的主索引由哈希函數(shù)給出梭伐;
如果發(fā)生沖突(兩個鍵值哈希到相同的位置)痹雅,這些鍵值就串成一個鏈表,鏈表的每個元素
占用數(shù)組的一項糊识。 - 執(zhí)行擴容的過程叫做rehash绩社,每次rehash時,會遍歷整個table的數(shù)組部分和哈希表部分技掏,統(tǒng)計其中有效的鍵值對铃将,大小不夠项鬼,則會擴容哑梳,擴容后的大小為2的整數(shù)冪次方,且保證rehash操作后整個數(shù)組部分的使用率大于50%绘盟。每次rehash都很耗時鸠真,使用table,我們應(yīng)該盡量減少rehash龄毡。
- Lua 實現(xiàn)表的算法頗為巧妙。每個表包含兩部分:數(shù)組(array)部分和哈希(hash)部
-
優(yōu)化
- 初始化優(yōu)化減少吠卷, rehash的次數(shù).
local param = {};param.type = 1;param.id = 1; param.name = "lua"; --優(yōu)化成 local param = {type= 1, id = 1,name = "lua"};
2. 擴容
當(dāng)新key要加入,table大小從0->1,1->2,2->4,類似這種預(yù)先分配好空間能減少內(nèi)存分配。local a = {} --a = 0 for i = 1, 3 do a[i] = true --i == 1 a == 1 --i == 2 a == 2 --i == 3 a == 4 end --優(yōu)化為 local a = {0,0,0} for i = 1, 3 do a[i] = true end
3.局部變量
-
原理
Lua 使用了一個基于寄存器的虛擬機沦零。這些「寄存器」
跟 CPU 中真實的寄存器并無關(guān)聯(lián)祭隔,因為這種關(guān)聯(lián)既無可移植性,也受限于可用的寄存器數(shù)
量路操。Lua 使用一個棧(由一個數(shù)組加上一些索引實現(xiàn))來存放它的寄存器疾渴。每個活動的
(active)函數(shù)都有一份活動記錄(activation record)千贯,活動記錄占用棧的一小塊,存放
著這個函數(shù)對應(yīng)的寄存器搞坝。因此搔谴,每個函數(shù)都有其自己的寄存器。由于每條指令只有 8 個
bit 用來指定寄存器桩撮,每個函數(shù)便可以使用多至 250 個寄存器敦第。
Lua 的寄存器如此之多,預(yù)編譯時便能將所有的局部變量存到寄存器中店量。所以芜果,在 Lua 中
訪問局部變量是很快的。 -
優(yōu)化
- 高頻調(diào)用類優(yōu)化
local x = math.sin(i) --優(yōu)化成 local sin = math.sin local x = sin(i)
- 在大項目基于元表融师,元方法實現(xiàn)的類和繼承時师幕,local優(yōu)化是很高效的。
local變量優(yōu)化
4.元表與元方法
-
原理
因為Lua本身是沒有面向?qū)ο笾С值奈芴玻陧椖块_發(fā)中需要面向?qū)ο缶幊膛啵谑呛芏嗳擞肔ua本身的數(shù)據(jù)結(jié)構(gòu)table+元表來模擬面向?qū)ο髮崿F(xiàn)類、繼承疼鸟、多重繼承后控。當(dāng)元方法 __index 和 __newindex 變成函數(shù)時,會因為 函數(shù)本身的復(fù)雜度導(dǎo)致邏輯消耗時間變多空镜。在大型項目里是不小的性能開銷浩淘。
元方法 __index 用來對表訪問,訪問規(guī)則:
1.在表中查找吴攒,如果找到张抄,返回該元素,找不到則繼續(xù)洼怔。
2.判斷該表是否有元表署惯,如果沒有元表,返回 nil镣隶,有元表則繼續(xù)极谊。
3.判斷元表有沒有 __index 方法,如果 __index 方法為 nil安岂,則返回 nil轻猖;如果 __index 方法是一個表,則重復(fù) 1域那、2咙边、3;如果 __index 方法是一個函數(shù),則返回該函數(shù)的返回值败许。元方法 __newindex 用來對表更新友瘤,更新規(guī)則:
1.如果是表已存在的索引鍵,則直接更新檐束,沒有的話就繼續(xù)辫秧。
2.解釋器就會查找__newindex 元方法:如果存在則調(diào)用這個函數(shù)而不進行賦值操作。
3.如果不存在則直接對表賦值被丧。
-
優(yōu)化
- 利用local話盟戏,提升訪問速度。
- C++甥桂、C 去實現(xiàn)柿究,畢竟C、C++的效率高黄选。
5.函數(shù)
- 參數(shù)優(yōu)先boolean, number, string, function蝇摸,少table。防止new table 頻繁rehash办陷,gc貌夕。
- Lua編譯一個函數(shù)時,會為它生成一個原型(prototype)民镜,其中包含了函數(shù)體對應(yīng)的虛擬機指令啡专、函數(shù)用到的常量值(數(shù),文本字符串等等)和一些調(diào)試信息制圈。在運行時们童,每當(dāng)Lua執(zhí)行一個形如function...end 這樣的表達式時,它就會創(chuàng)建一個新的數(shù)據(jù)對象鲸鹦,其中包含了相應(yīng)函數(shù)原型的引用慧库、環(huán)境(environment,用來查找全局變量的表)的引用以及一個由所有upvalue引用組成的數(shù)組馋嗜,而這個數(shù)據(jù)對象就稱為閉包齐板。由此可見,函數(shù)是編譯期概念嵌戈,是靜態(tài)的覆积,而閉包是運行期概念听皿,是動態(tài)的熟呛。
function f1(n)
local function f2()
--用來查找全局變量的表)的引用以及一個由所有upvalue引用組成的數(shù)組
-{n}
print(n)
end
n=n + 10
return f2
end
g1 = f1(1979)
g1()--打印出1989