1.字符串
-
原理
- Lua 的字符串都是内化的(internalized);这意味着字符串在 Lua 中都只有一份拷贝。每当一个新字符串出现时,Lua 会先检查这个字符串是否已经有一份拷贝,如果有,就重用这份拷贝。内化(internalization)使字符串比较及表索引这样的操作变得非常快,但是字符串的创建会变慢。
- Lua 的字符串变量从来不会包含字符串本身,包含的只是字符串的引用。这种实现加快了某些字符串操作。
简单的说lua维护了一个table存放了所有的字符串。
任何新创建的字符串都会先hash去table查找一下,有的话直接返回字符串的引用。
没有的话创建个新的字符串放入table,返回新的字符串的引用。
--引用列子
local value = "a"
value = value .."b"
print(value ) -- 输出 'ab'
现在 lua string这个大table里,就有了 'a' 和 'ab' 两个字符串了。value 实际引用的是 'ab'。
--字符串的连接列子
'x' .. 'y' .. 'z'
这种就是 x找一回,xy找一回,xyz找一回。
生成3个串 找3回最后table里有了'x','xy','xyz' 三个字符串了。
-
优化
-
使用运算符
' .. '
每次拼接都需要申请新的空间,旧的result对应的空间会在某时刻被Lua的垃圾回收期GC,且随着result不断增长,越往后会开辟更多新的空间,并进行拷贝操作,产生更多需要被GC的空间,所以性能降低。 -
使用table.concat (table [, sep [, start [, end]]])函数
table.concat 底层拼接字符串的方式也是使用运算符.. ,但是其使用算法减少了使用运算符..的次数,减少了GC,从而提高效率。
-
2.Table
-
原理
- Lua 实现表的算法颇为巧妙。每个表包含两部分:数组(array)部分和哈希(hash)部
分,数组部分保存的项(entry)以整数为键(key),从 1 到某个特定的 n,(稍后会讨
论 n 是怎么计算的。)所有其他的项(包括整数键超出范围的)则保存在哈希部分。
顾名思义,哈希部分使用哈希算法来保存和查找键值。它使用的是开放寻址(open
address)的表,意味着所有的项都直接存在哈希数组里。键值的主索引由哈希函数给出;
如果发生冲突(两个键值哈希到相同的位置),这些键值就串成一个链表,链表的每个元素
占用数组的一项。 - 执行扩容的过程叫做rehash,每次rehash时,会遍历整个table的数组部分和哈希表部分,统计其中有效的键值对,大小不够,则会扩容,扩容后的大小为2的整数幂次方,且保证rehash操作后整个数组部分的使用率大于50%。每次rehash都很耗时,使用table,我们应该尽量减少rehash。
- Lua 实现表的算法颇为巧妙。每个表包含两部分:数组(array)部分和哈希(hash)部
-
优化
- 初始化优化减少, rehash的次数.
local param = {};param.type = 1;param.id = 1; param.name = "lua"; --优化成 local param = {type= 1, id = 1,name = "lua"};
2. 扩容
当新key要加入,table大小从0->1,1->2,2->4,类似这种预先分配好空间能减少内存分配。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 --优化为 local a = {0,0,0} for i = 1, 3 do a[i] = true end
3.局部变量
-
原理
Lua 使用了一个基于寄存器的虚拟机。这些「寄存器」
跟 CPU 中真实的寄存器并无关联,因为这种关联既无可移植性,也受限于可用的寄存器数
量。Lua 使用一个栈(由一个数组加上一些索引实现)来存放它的寄存器。每个活动的
(active)函数都有一份活动记录(activation record),活动记录占用栈的一小块,存放
着这个函数对应的寄存器。因此,每个函数都有其自己的寄存器。由于每条指令只有 8 个
bit 用来指定寄存器,每个函数便可以使用多至 250 个寄存器。
Lua 的寄存器如此之多,预编译时便能将所有的局部变量存到寄存器中。所以,在 Lua 中
访问局部变量是很快的。 -
优化
- 高频调用类优化
local x = math.sin(i) --优化成 local sin = math.sin local x = sin(i)
- 在大项目基于元表,元方法实现的类和继承时,local优化是很高效的。
local变量优化
4.元表与元方法
-
原理
因为Lua本身是没有面向对象支持的,但在项目开发中需要面向对象编程,于是很多人用Lua本身的数据结构table+元表来模拟面向对象实现类、继承、多重继承。当元方法 __index 和 __newindex 变成函数时,会因为 函数本身的复杂度导致逻辑消耗时间变多。在大型项目里是不小的性能开销。
-
元方法 __index 用来对表访问,访问规则:
1.在表中查找,如果找到,返回该元素,找不到则继续。
2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。 -
元方法 __newindex 用来对表更新,更新规则:
1.如果是表已存在的索引键,则直接更新,没有的话就继续。
2.解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
3.如果不存在则直接对表赋值。
-
优化
- 利用local话,提升访问速度。
- C++、C 去实现,毕竟C、C++的效率高。
5.函数
- 参数优先boolean, number, string, function,少table。防止new table 频繁rehash,gc。
- Lua编译一个函数时,会为它生成一个原型(prototype),其中包含了函数体对应的虚拟机指令、函数用到的常量值(数,文本字符串等等)和一些调试信息。在运行时,每当Lua执行一个形如function...end 这样的表达式时,它就会创建一个新的数据对象,其中包含了相应函数原型的引用、环境(environment,用来查找全局变量的表)的引用以及一个由所有upvalue引用组成的数组,而这个数据对象就称为闭包。由此可见,函数是编译期概念,是静态的,而闭包是运行期概念,是动态的。
function f1(n)
local function f2()
--用来查找全局变量的表)的引用以及一个由所有upvalue引用组成的数组
-{n}
print(n)
end
n=n + 10
return f2
end
g1 = f1(1979)
g1()--打印出1989
网友评论