【Lua】Lua的几点优化原则
Lua是一门以性能著称的脚本语言,被广泛的应用在很多方面,比如很多游戏的插件。
很多时候,没有必要去考虑性能的问题,不过,如果我们在开始编写代码的时候就以更适当,性能更高的方式与结构去组织代码,对于程序最后的性能有很大的好处。这不是强调过早的优化代码,只是一种很好的代码编写习惯。
在Lua中,我们需要知道下面这些:
使用local
在代码运行前,Lua会把源码预编译成一种中间码,类似于Java的虚拟机。这种格式然后会通过C的解释器进行解释,整个过程其实就是通过一个while循环,里面有很多的switch...case语句,一个case对应一条指令来解析。
自Lua 5.0之后,Lua采用了一种类似于寄存器的虚拟机模式。Lua用栈来储存其寄存器。每一个活动的函数,Lua都会其分配一个栈,这个栈用来储存函数里的活动记录。每一个函数的栈都可以储存至多250个寄存器,因为栈的长度是用8个比特表示的。
有了这么多的寄存器,Lua的预编译器能把所有的local变量储存在其中。这就使得Lua在获取local变量时其效率十分的高。
来做个试验:
a = os.clock()
for i = , do
local x = math.sin(i)
end
b = os.clock()
print(b-a)
运行时间:

a = os.clock()
local sin = math.sin
for i = , do
local x = sin(i)
end
b = os.clock()
print(b-a)
运行时间:

可见,使用了local效率得到了很大的提升。
表(table)
表在Lua中使用十分频繁,因为表几乎代替了Lua的所有容器。所以快速了解一下Lua底层是如何实现表,对我们编写Lua代码是有好处的。
Lua的表分为两个部分:数组(array)部分和哈希(hash)部分。数组部分包含所有从1到n的整数键,其他的所有键都储存在哈希部分中。
哈希部分其实就是一个哈希表,哈希表本质是一个数组,它利用哈希算法将键转化为数组下标,若下标有冲突(即同一个下标对应了两个不同的键),则它会将冲突的下标上创建一个链表,将不同的键串在这个链表上,这种解决冲突的方法叫做:链地址法。
当我们把一个新键值赋给表时,若数组和哈希表已经满了,则会触发一个再哈希(rehash)。再哈希的代价是高昂的。首先会在内存中分配一个新的长度的数组,然后将所有记录再全部哈希一遍,将原来的记录转移到新数组中。新哈希表的长度是最接近于所有元素数目的2的乘方。
从上面可以看出来,表的赋值所需要付出的代价是由表的初始大小与表最后的规模来决定的。
于是我们可以这么来做:
如果我们以:
a = {}
来创建一个初始表,这个表的大小首先为0,当我们往表中插入数据时,如果表内已经满了,则将触发一次rehash,lua将新创建一个更大的表,直至表再次填满。
如果在程序运行期间去做这个工作,我们将需要付出更多的时间与性能的代价,于是我们可以这么做:
如果可以预期表内将有3个元素,则在创建表的时候就填充表的大小,譬如:
a = {1,1,1}
这样将使表的填充效率成倍的提升。
可以做个试验:
a = os.clock()
for i = , do
local a = {}
a[] =
a[] =
a[] =
end
b = os.clock()
print(b-a)
以上代码的运行时间为:

a = os.clock()
for i = , do
local a = {,,}
a[] =
a[] =
a[] =
end
b = os.clock()
print(b-a)
如果创建时便填充了表的大小,结果:

效率提升了差不多1倍。
关于字符串
在Lua中,实现字符串类型有一下两个特点:
第一,所有的字符串在Lua中都只储存一份拷贝。当新字符串出现时,Lua检查是否有其相同的拷贝,若没有则创建它,否则,指向这个拷贝。这可以使得字符串比较和表索引变得相当的快,因为比较字符串只需要检查引用是否一致即可;但是这也降低了创建字符串时的效率,因为Lua需要去查找比较一遍。
第二,所有的字符串变量,只保存字符串引用,而不保存它的buffer。这使得字符串的赋值变得十分高效。
在连接字符串时,一般使用的是连接符“..”,不过这个连接方式将会获取连接双方前者的整个拷贝,而如果我们有前者的一个buffer,当连接字符串的时候,只需要在buffer后面直接插入到末尾,这样效率将有很大的提升。于是,在Lua中,可以用table来模仿buffer来处理大字符串连接,可以这么做:
a = os.clock()
local s = ''
local t = {}
for i = , do
t[#t + ] = 'a'
end
s = table.concat( t, '')
b = os.clock()
print(b-a)
输出为:

0.05s
如果不用table,依然用"..":
a = os.clock()
local s = ''
for i = , do
s = s .. 'a'
end
b = os.clock()
print(b-a)
那么时间为:

12.9s,好吧,这个差距确实很大。
所以,在大字符串连接中,我们应避免使用".."。应用table来模拟buffer,然后concat得到最终字符串。
【Lua】Lua的几点优化原则的更多相关文章
- [译] Closures in Lua - Lua中的闭包
原文:(PDF) . 摘要 一等(first-class)函数是一种非常强大的语言结构,并且是函数式语言的基础特性.少数过程式语言由于其基于栈的实现,也支持一等函数.本文讨论了Lua 5.x用于实现一 ...
- [转][译] Closures in Lua - Lua中的闭包
http://www.cnblogs.com/plodsoft/p/5900270.html?utm_source=tuicool&utm_medium=referral 原文:(PDF) . ...
- Oracle SQL 优化原则(实用篇)
由于SQL优化优化起来比较复杂,并且还受环境限制,在开发过程中,写SQL必须遵循以下几点原则: 1.Oracle 采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他Where ...
- oracle优化原则(二)
SQL优化原则 二.SQL语句编写注意问题 www.2cto.com 下面就某些SQL语句的where子句编写中需要注意的问题作详细介绍.在这些where子句中,即使某些列存在索引,但是由于编写了劣质 ...
- 我的mysql数据库sql优化原则
原文 我的mysql数据库sql优化原则 一.前提 这里的原则 只是针对mysql数据库,其他的数据库 某些是殊途同归,某些还是存在差异.我总结的也是mysql普遍的规则,对于某些特殊情况得特殊对待. ...
- js优化原则
首先,与其他语言不同,JS的效率很大程度是取决于JS engine的效率.除了引擎实现的优劣外,引擎自己也会为一些特殊的代码模式采取一些优化的策略.例如FF.Opera和Safari的JS引擎,都对字 ...
- 从字符串拼接看JS优化原则
来自知乎的问题:JavaScript 怎样高效拼接字符串? 请把以下用于连接字符串的JavaScript代码修改为更高效的方式: var htmlString ='< div class=”co ...
- Flex内存泄露解决方法和内存释放优化原则
Flex内存泄露解决方法和内存释放优化原则 你对Flex内存泄露的概念是否了解,这里和大家分享一下Flex内存释放优化原则和Flex内存泄露解决方法,希望本文的介绍能让你有所收获. Flex内存释放优 ...
- ORACLE 数据库优化原则
ORACLE 数据库优化原则 一.SQL语句用大写的: 因为Oracle总是先解析SQL语句,把小写的字母转换成大写的再厉行. 二.避免在索引列上利用NOT等闲 我们要避免在索引列上利用NOT, NO ...
- MySQL 索引优化原则
一.索引优化原则 1.最左前缀匹配原则,联合索引,mysql会从做向右匹配直到遇到范围查询(>.<.between.like)就停止匹配,比如a = 1 and b = 2 and c & ...
随机推荐
- C# 软件绑定QQ群类开源放出
周天闲来无事写个公共类,可以添加到你们自己项目中限制必须加入你QQ群才可以使用. 代码简单,高手勿喷,有哪里不合理的请回帖让大家学习学习. using System; using System.Tex ...
- ue4框架C++语法汇总文章
1.Run external .exe file TCHAR* url = TEXT("C:\\windows\\system32\\calc.exe"); FPlatformPr ...
- Quartz.NET作业调度框架详解(转)
Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...
- 【转载】new和malloc的区别
本篇随笔为转载,原贴地址:C++中new和malloc的十点区别. 前言 几个星期前去面试C++研发的实习岗位,面试官问了个问题: new与malloc有什么区别? 这是个老生常谈的问题.当时我回答n ...
- PHP连接MySQL的时候报错SQLSTATE[HY000] [2002] No such file or directory
错误环境:Mac OS 10.10 找到mysql.sock文件的位置 $sudo find / -name mysql.sock ------结果如下---------- find: /dev/fd ...
- android Spinner的简单用法
上代码: spinner = (Spinner) findViewById(R.id.spinner); tv = (TextView) findViewById(R.id.tv); final Ar ...
- 树莓派(rasperberry pi 2)上装mysql远程无法访问
于是组合关键字(树莓派 mysql)求百度问谷歌.未果.操刀自己来吧.检查mysql的配置文件 /etc/mysql/my.cnf 发现如下配置 # Instead of skip-networkin ...
- iOS视频播放器
用AVPlayer写的一个简单的播放器,支持横竖屏旋转! https://github.com/shumingli/module 欢迎加iOS开发交流群:516318591
- HMC破解控制台密码
一般我们装完HMC以后默认的登录控制台的账户是:admin 密码:abc123当我们可以自创建登录账户后,忘记密码了怎么办? 这里有两种方法: 1 由于HMC是suse系统,基于linux内核的,所以 ...
- 使用Eclipse上传/下载Git项目
使用Eclipse上传/下载Git项目 前提: Eclipse已安装EGit插件 已拥有GitLab / GitHub / 其它Git托管服务账号 SSH方式 配置 配置Git信息 配置用户信息 Ec ...