美文网首页Lua
Lua 性能,内存优化

Lua 性能,内存优化

作者: ClownWang | 来源:发表于2020-02-25 21:17 被阅读0次

    1.字符串

    • 原理

      1. Lua 的字符串都是内化的(internalized);这意味着字符串在 Lua 中都只有一份拷贝。每当一个新字符串出现时,Lua 会先检查这个字符串是否已经有一份拷贝,如果有,就重用这份拷贝。内化(internalization)使字符串比较及表索引这样的操作变得非常快,但是字符串的创建会变慢。
      2. 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' 三个字符串了。
    
    • 优化

      1. 使用运算符 ' .. '
        每次拼接都需要申请新的空间,旧的result对应的空间会在某时刻被Lua的垃圾回收期GC,且随着result不断增长,越往后会开辟更多新的空间,并进行拷贝操作,产生更多需要被GC的空间,所以性能降低。

      2. 使用table.concat (table [, sep [, start [, end]]])函数
        table.concat 底层拼接字符串的方式也是使用运算符.. ,但是其使用算法减少了使用运算符..的次数,减少了GC,从而提高效率。

    2.Table

    • 原理

      1. Lua 实现表的算法颇为巧妙。每个表包含两部分:数组(array)部分和哈希(hash)部
        分,数组部分保存的项(entry)以整数为键(key),从 1 到某个特定的 n,(稍后会讨
        论 n 是怎么计算的。)所有其他的项(包括整数键超出范围的)则保存在哈希部分。
        顾名思义,哈希部分使用哈希算法来保存和查找键值。它使用的是开放寻址(open
        address)的表,意味着所有的项都直接存在哈希数组里。键值的主索引由哈希函数给出;
        如果发生冲突(两个键值哈希到相同的位置),这些键值就串成一个链表,链表的每个元素
        占用数组的一项。
      2. 执行扩容的过程叫做rehash,每次rehash时,会遍历整个table的数组部分和哈希表部分,统计其中有效的键值对,大小不够,则会扩容,扩容后的大小为2的整数幂次方,且保证rehash操作后整个数组部分的使用率大于50%。每次rehash都很耗时,使用table,我们应该尽量减少rehash。
    • 优化

      1. 初始化优化减少, rehash的次数.
      local param = {};param.type = 1;param.id = 1; param.name = "lua"; 
      --优化成
      local param = {type= 1, id = 1,name = "lua"};
      

      2. 扩容

      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
      
      当新key要加入,table大小从0->1,1->2,2->4,类似这种预先分配好空间能减少内存分配。

    3.局部变量

    • 原理

      Lua 使用了一个基于寄存器的虚拟机。这些「寄存器」
      跟 CPU 中真实的寄存器并无关联,因为这种关联既无可移植性,也受限于可用的寄存器数
      量。Lua 使用一个栈(由一个数组加上一些索引实现)来存放它的寄存器。每个活动的
      (active)函数都有一份活动记录(activation record),活动记录占用栈的一小块,存放
      着这个函数对应的寄存器。因此,每个函数都有其自己的寄存器。由于每条指令只有 8 个
      bit 用来指定寄存器,每个函数便可以使用多至 250 个寄存器。
      Lua 的寄存器如此之多,预编译时便能将所有的局部变量存到寄存器中。所以,在 Lua 中
      访问局部变量是很快的。
    • 优化

      1. 高频调用类优化
      local x = math.sin(i) 
      --优化成
      local sin = math.sin
      local x = sin(i)
      
      1. 在大项目基于元表,元方法实现的类和继承时,local优化是很高效的。
        local变量优化

    4.元表与元方法

    • 原理

    因为Lua本身是没有面向对象支持的,但在项目开发中需要面向对象编程,于是很多人用Lua本身的数据结构table+元表来模拟面向对象实现类、继承、多重继承。当元方法 __index 和 __newindex 变成函数时,会因为 函数本身的复杂度导致逻辑消耗时间变多。在大型项目里是不小的性能开销。

    1. 元方法 __index 用来对表访问,访问规则:
      1.在表中查找,如果找到,返回该元素,找不到则继续。
      2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
      3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。

    2. 元方法 __newindex 用来对表更新,更新规则:
      1.如果是表已存在的索引键,则直接更新,没有的话就继续。
      2.解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
      3.如果不存在则直接对表赋值。

    • 优化

      1. 利用local话,提升访问速度。
      2. C++、C 去实现,毕竟C、C++的效率高。

    5.函数

    1. 参数优先boolean, number, string, function,少table。防止new table 频繁rehash,gc。
    2. 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
    
    

    相关文章

      网友评论

        本文标题:Lua 性能,内存优化

        本文链接:https://www.haomeiwen.com/subject/lieqchtx.html