Lua性能优化之table

Posted 游戏开发交流学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua性能优化之table相关的知识,希望对你有一定的参考价值。


通常情况下,我们不需要知道Lua的table是如何实现的,但是为了对lua性能进行优化,去了解Lua table的实现细节是非常关键的。

01

table是如何实现的?

为了了解table的实现,我们可以查看Lua的C源码,如下:
    

图1-1 lua表的构成

Lua中的table是由两部分组成,数组部分和哈希表部分。 数组部分储存着索引为整型的数据,哈希表存储着以其它类型为索引的键值对。 哈希表使用一个哈希算法来计算键值对的key值,采用开链法来处理哈希冲突。 当我们声明一个表t = { },此时t的数组部分和哈希表部分的大小都是0,当不断向数组插入键值对的时候 如下:
  • a[1] = 1 ,大小不够,需要扩容,扩容后数组部分的大小为1

  • a[2] = 2 ,大小不够,需要扩容,扩容后数组部分的大小为2

  • a[3] = 3 ,大小不够,需要扩容,扩容后数组大小为4.

执行扩容的过程叫做rehash,每次rehash时,会遍历整个table的数组部分和哈希表部分,统计其中有效的键值对,大小不够,则会扩容,扩容后的大小为2的整数幂次方,且保证rehash操作后整个数组部分的使用率大于50%。每次rehash都很耗时,使用table,我们应该尽量减少rehash。

  

02


如何减少Rehash?

    1.不要使用多个size较小的table,如果可以使用大的table来代替。

    由第一部分可以看到,当table的size从0扩展到4的时候需要3次rehash。因此当table尺寸较小时,向table中插入元素,很容易触发rehash,因此不要过多的使用小table,使用一个大的table来整合小的table。

     2.初始化table的写法。

    对于第一部分的t={},我们可以声明t的时候就对t进行初始化 即:

            t={1,2,3},

    这样会直接产生一个size=3 的表t,也就没有了3次rehash。需要注意的是:

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

    这种写法t的数组部分的大小也为4,会执行三次rehash。

       3.将table置空的注意点

table size大小的变化只会在触发rehash的时候进行,因此当我们将table中的某个键值对的值设置为nil的时候,Lua并不会执行减小size的操作。当执行rehash的时候,Lua才会查找所有为nil的元素,并且决定是否reduce size。例如

        local t = {} 

        for i =1,100000 do

        t[i] = i

    end

    print(collectgarbage("count"))

    --置为nil

    for i =1,100000 do 

        t[i]=nil

    end

    print(collectgarbage("count"))

    上面两次结果相同,但是当我们

    for i= 100001,200000 do

        t[i] = nil

    end

    print(collectgarbage("count"))

    此时结果会变小,因为当遍历到20000时,发现数组大小不够,触发rehash,rehash会保证数组部分的使用率大于50%,因此会减小数组部分的大小。为nil的也会被删除。
    又比如当我们直接声明一个local t ={}  t[100] =1,此时为了保证数组的使用效率大于50%,会直接将键值对{100,1}储存在hash表部分。
     4. 常见的清空table的写法。

        方法一:

                 for k in pairs(t) do  t[k] =nil end

        方法二

            while true do

                local k  =next(t,nil)

                    if not k then

                        break

                    end

                 t[k] = nil

            end

    第二种写法比第一种写法要耗时。 next每次返回当前key的值和下一 个不为nil的元素的key。 当传入的key为nil的时候,每次返回第一个部位nil的键值对的值。 第二种写法每次传入的key都为nil,随着table被不断置为nil,寻找第一个不为nil的键值对,将会越来越耗时。 而pair虽然也是用next实现的,但是配合上泛型迭代器for,每个循环都会获得上个循环的状态,因此效率上要快很多。 第一种写法等价于:
    for k,v in next(t,nil) do,每个循环都将next返回的key传入下一个循环的next。





以上是关于Lua性能优化之table的主要内容,如果未能解决你的问题,请参考以下文章

mysql性能优化之table_open_cache

Unity+Lua实测如何性能优化(Lua和C#交互篇)

lua 性能优化

lua 性能优化知识点

使用Lua 局部变量来优化性能,同一时候比較局部变量和全局变量

Unreal引擎中Lua代码的性能瓶颈定位与优化