• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >Lua > Lua性能优化技巧(三):关于表

Lua性能优化技巧(三):关于表

作者:junjie 字体:[增加 减小] 来源:互联网

junjie 通过本文主要向大家介绍了lua性能优化,lua性能,lua脚本优化,lua代码优化,lua 优化等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

一般情况下,你不需要知道Lua实现表的细节,就可以使用它。实际上,Lua花了很多功夫来隐藏内部的实现细节。但是,实现细节揭示了表操作的性能开销情况。因此,要优化使用表的程序(这里特指Lua程序),了解一些表的实现细节是很有好处的。

Lua的表的实现使用了一些很聪明的算法。每个Lua表的内部包含两个部分:数组部分和哈希部分。数组部分以从1到一个特定的n之间的整数作为键来保存元素(我们稍后即将讨论这个n是如何计算出来的)。所有其他元素(包括在上述范围之外的整数键)都被存放在哈希部分里。

正如其名,哈希部分使用哈希算法来保存和查找键。它使用被称为开放地址表的实现方式,意思是说所有的元素都保存在哈希数组中。用一个哈希函数来获取一个键对应的索引;如果存在冲突的话(意即,如果两个键产生了同一个哈希值),这些键将会被放入一个链表,其中每个元素对应一个数组项。当Lua需要向表中添加一个新的键,但哈希数组已满时,Lua将会重新哈希。重新哈希的第一步是决定新的数组部分和哈希部分的大小。因此,Lua遍历所有的元素,计数并对其进行归类,然后为数组部分选择一个大小,这个大小相当于能使数组部分超过一半的空间都被填满的2的最大的幂;然后为哈希部分选择一个大小,相当于正好能容纳哈希部分所有元素的2的最小的幂。

当Lua创建空表时,两个部分的大小都是0。因此,没有为其分配数组。让我们看一看当执行下面的代码时会发生什么:
local a = {}
for i = 1, 3 do
    a[i] = true
end
</div>
这段代码始于创建一个空表。在循环的第一次迭代中,赋值语句
a[1] = true
</div>
触发了一次重新哈希;Lua将数组部分的大小设为1,哈希部分依然为空;第二次迭代时
a[2] = true
</div>
触发了另一次重新哈希,将数组部分扩大为2.最终,第三次迭代又触发了一次重新哈希,将数组部分的大小扩大为4。

类似下面的代码
a = {}
a.x = 1; a.y = 2; a.z = 3
</div>
做的事情类似,只不过增加的是哈希部分的大小。

对于大的表来说,初期的几次重新哈希的开销被分摊到整个表的创建过程中,一个包含三个元素的表需要三次重新哈希,而一个有一百万个元素的表也只需要二十次。但是当创建几千个小表的时候,重新哈希带来的性能影响就会非常显著。

旧版的Lua在创建空表时会预选分配大小(4,如果我没有记错的话),以防止在初始化小表时产生的这些开销。但是这样的实现方式会浪费内存。例如,如果你要创建数百万个点(表现为包含两个元素的表),每个都使用了两倍于实际所需的内存,就会付出高昂的代价。这也是为什么Lua不再为新表预分配数组。

如果你使用C编程,可以通过Lua的API函数lua_createtable来避免重新哈希;除lua_State之外,它还接受两个参数:数组部分的初始大小和哈希部分的初始大小[1]。只要指定适当的值,就可以避免初始化时的重新哈希。需要警惕的是,Lua只会在重新哈希时收缩表的大小,因此如果在初始化时指定了过大的值,Lua可能永远不会纠正你浪费的内存空间。

当使用Lua编程时,你可能可以使用构造式来避免初始化时的重新哈希。当你写下
{true, true, true}
</div>
时,Lua知道这个表的数组部分将会有三个元素,因此会创建相应大小的数组。类似的,如果你写下
{x = 1, y = 2, z = 3}
</div>
Lua也会为哈希部分创建一个大小为4的数组。例如,执行下面的代码需要2.0秒:
for i = 1, 1000000 do
    local a = {}
    a[1] = 1; a[2] = 2; a[3] = 3
end
</div>
如果在创建表时给定正确的大小,执行时间可以缩减到0.7秒:
for i = 1, 1000000 do
    local a = {true, true, true}
    a[1] = 1; a[2] = 2; a[3] = 3
end
</div>
但是,如果你写类似于
{[1] = true, [2] = true, [3] = true}
</div>
的代码,Lua还不够聪明,无法识别表达式(在本例中是数值字面量)指定的数组索引,因此它会为哈希部分创建一个大小为4的数组,浪费内存和CPU时间。

两个部分的大小只会在Lua重新哈希时重新计算,重新哈希则只会发生在表完全填满后,Lua需要插入新的元素之时。因此,如果你遍历一个表并清除其所有项(也就是全部设为nil),表的大小不会缩小。但是此时,如果你需要插入新的元素,表的大小将会被调整。多数情况下这都不会成为问题,但是,不要指望能通过清除表项来回收内存:最好是直接把表自身清除掉。

一个可以强制重新哈希的猥琐方法是向表中插入足够多的nil。例如:

a = {}
lim = 10000000
for i = 1, lim do a[i] = i end              -- 创建一个巨表
print(collectgarbage("count"))              --> 196626
for i = 1, lim do a[i] = nil end            -- 清除所有元素
print(collectgarbage("count"))              --> 196626
for i = lim + 1, 2 * lim do a[i] = nil end -- 创建一堆nil元素
print(collectgarbage("count"))              --> 17
</div>

除非是在特殊情况下,我不推荐使用这个伎俩:它很慢,并且没有简单的方法能知道要插入多少nil才够。

你可能会好奇Lua为什么不会在清除表项时收缩表。首先是为了避免测试写入表中的内容。如果在赋值时检查值是否为nil,将会拖慢所有的赋值操作。第二,也是最重要的,允许在遍历表时将表项赋值为nil。例如下面的循环:
for k, v in pairs(t) do
    if some_property(v) then
        t[k] = nil – 清除元素
    end
end
</div>
如果Lua在每次nil赋值后重新哈希这张表,循环就会被破坏。

如果你想要清除一个表中的所有元素,只需要简单地遍历它:

您可能想查找下面的文章:

  • Lua性能优化技巧(六):最后的提示
  • Lua性能优化技巧(五):削减、重用和回收
  • Lua性能优化技巧(四):关于字符串
  • Lua性能优化技巧(三):关于表
  • Lua性能优化技巧(二):基本事实
  • 编写高性能Lua代码的方法

相关文章

  • C语言模块回调Lua函数的两种方法
  • Lua中的函数知识总结
  • Lua中的捕获机制和转换技巧介绍
  • Lua中使用table.concat连接大量字符串实例
  • Lua编程示例(一):select、debug、可变参数、table操作、error
  • Lua教程(二十):Lua调用C函数
  • Lua中编译执行代码相关的函数详解
  • 用sysbench来测试MySQL的性能的教程
  • Lua的协程(coroutine)简介
  • 简单的Lua 连接操作mysql数据库的方法

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • Lua中实现递归删除一个文件夹
    • Lua教程(二):C++和Lua相互传递数据示例
    • 使用nginx+lua实现信息访问量统计
    • Lua中..和#运算符的使用方法
    • Lua获取文件长度和判断文件是否存在函数分享
    • 获取Lua表结构(table)数据实例
    • 简单的Lua 连接操作mysql数据库的方法
    • Lua中的面向对象编程详解
    • Lua脚本语言入门笔记
    • Lua判断字符串中包含中文字符的方法和计算字符串宽度函数分享

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有