Upload
ho-kim
View
355
Download
16
Embed Size (px)
DESCRIPTION
Lua Programming Skills and Optimizations
Citation preview
LuaLua 基础技巧与性能优基础技巧与性能优化化
http://kimbs.cnhttp://kimbs.cn
Lua Lua 基础技巧基础技巧
Lua Lua 程序块程序块LuaLua 程序可以分为:程序可以分为:11 )控制结构的执行体)控制结构的执行体 (if … else …)(if … else …)
22 )函数执行体()函数执行体( doFunction()doFunction() ))33 )程序块()程序块( do ...... enddo ...... end ))
三元运算三元运算三元运算:三元运算:local a = 2 > 1 and “aa” or “bb”local a = 2 > 1 and “aa” or “bb”
大小写敏感大小写敏感and and 是保留字是保留字
但是 但是 And And 和 和 AND AND 则不是 则不是 Lua Lua 的保留字的保留字
For For 循环循环泛型:泛型:for i, v in pairs(t) dofor i, v in pairs(t) do
print(v)print(v)
endend
数字型:数字型:for i = 1, N dofor i = 1, N do
......
endend
注释注释单行注释:单行注释:-- -- 被注释的行被注释的行
多行注释:多行注释:--[[--[[
技巧:只需在前面加一个 技巧:只需在前面加一个 - - 就可以重启本段代码了就可以重启本段代码了--]]--]]
变量赋值变量赋值a = 1 b = 2 c = 3a = 1 b = 2 c = 3
-- -- 可以。。。但相当难看可以。。。但相当难看
a = 1a = 1
b = 2b = 2
c = 3c = 3
-- -- 最好是分行最好是分行
默认值默认值设置默认值的方法设置默认值的方法 ::
local x = x or defaultValuelocal x = x or defaultValue
局部(局部( locallocal )变量)变量11 )只作用于当前程序块)只作用于当前程序块22 )安全)安全33 )访问比全局变量快)访问比全局变量快44 )内存回收及时)内存回收及时
变量类型变量类型11 )) Lua Lua 是弱类型语言是弱类型语言
22 )很多时候都要使用 )很多时候都要使用 type() type() 来判断变量类型来判断变量类型
33 )熟练运用 )熟练运用 type type 是避免是避免 bugbug 的有效手段的有效手段
删除变量删除变量a = nila = nil
保留字保留字以 下划线以 下划线 ++ 字符 组成的变量都是 字符 组成的变量都是 Lua Lua 保留标识符保留标识符尽量不用这样的变量命名方式尽量不用这样的变量命名方式
例如 例如 _NAME, _M, _G, … _NAME, _M, _G, … 等等等等
字符串下标字符串下标String String 的下标(的下标( indexindex )是从)是从 11 开始,而不是开始,而不是 00
string.sub(“hhelloo”,2,-2) -- hellostring.sub(“hhelloo”,2,-2) -- hello
字符串长度字符串长度 str = “hello”str = “hello”
print(#str)print(#str)
注意:注意: # # 返回的只是线性表最后一个索引值,在 返回的只是线性表最后一个索引值,在 table table 中间有空隙(中间有空隙( nilnil )的情况下,就不靠谱)的情况下,就不靠谱了。了。
字符串连接字符串连接连接符号连接符号““ ..”..” 的陷阱的陷阱
print(123.. 456) -- print(123.. 456) -- 报错报错print(123 .. 456) -- print(123 .. 456) -- 字符串“字符串“ 123456”123456”
空字符串空字符串数字数字 0 0 和 空字符串 在条件判断中都视为“真”!和 空字符串 在条件判断中都视为“真”!
类似 类似 PHP PHP 的经典错误的经典错误 if (empty(‘’)) if (empty(‘’)) 不会再现不会再现
字符串格式化字符串格式化string.format("%02d/%02d/%04d", d, m, y) string.format("%02d/%02d/%04d", d, m, y)
-- 05/11/1990-- 05/11/1990
跟跟 CC 语言里面的 语言里面的 printf printf 用法基本相同用法基本相同
字符串修改字符串修改String String 是不可变值,修改实际上是“重新组装”是不可变值,修改实际上是“重新组装”
s2 = string.gsub(s1, 'before', 'after');s2 = string.gsub(s1, 'before', 'after');
-- s1 -- s1 的值不变的值不变
多行字符串多行字符串local str = [[local str = [[
hihi
kimkim
]]]]
类似 类似 PHP PHP 的 的 heredocheredoc
模式匹配符模式匹配符Lua Lua 模式匹配使用 模式匹配使用 % % 来转义,而不是来转义,而不是““ \”\”
string.find(str,”%.”) -- string.find(str,”%.”) -- 寻找逗号位置寻找逗号位置
数组下标数组下标TableTable 的数字下标从的数字下标从 11 开始,而不是开始,而不是 00
table = {10, a = 20, 30}table = {10, a = 20, 30}
print(table[1]) -- 10print(table[1]) -- 10
print(table[2]) -- 30print(table[2]) -- 30
二维数组二维数组mt = {}mt = {}
for i = 1, N dofor i = 1, N do
mt[i] = {} -- mt[i] = {} -- 注意要先定义注意要先定义 for j = 1, M dofor j = 1, M do
mt[i][j] = i * jmt[i][j] = i * j
endend
endend
复制复制 TableTable
Lua Lua 的 的 table table 复制都是引用赋值复制都是引用赋值即新增一个对 即新增一个对 table table 的引用的引用
local a = {}local a = {}
local b = a -- a local b = a -- a 和 和 b b 都引用同一个 都引用同一个 tabletable
比较 比较 TableTable
Lua Lua 通过“引用”对 通过“引用”对 tabletable 、、 userdata userdata 和 函和 函数 等进行比较,只有当他们引用同一个对象时,数 等进行比较,只有当他们引用同一个对象时,才视为相等。才视为相等。
local a, b = {1}, {1}local a, b = {1}, {1}
a == b -- falsea == b -- false
稀疏矩阵稀疏矩阵Table Table 天生就是稀疏矩阵天生就是稀疏矩阵
函数的意义函数的意义函数 函数 print print ::
持有“打印字符串功能”这个函数的名为 持有“打印字符串功能”这个函数的名为 print print
的变量的变量
函数函数 vsvs 方法方法调用 调用 obj obj 模块下的函数:模块下的函数:
11 )) obj.foo(obj, x)obj.foo(obj, x)
22 )) obj:foo(x) -- obj:foo(x) -- 转换成方法调用,实际上是把 转换成方法调用,实际上是把 obj obj 隐式的作为第一个参数隐式的作为第一个参数
函数调用函数调用建议统一使用 建议统一使用 () () 来调用函数:来调用函数: print “hello world” -- print “hello world” -- 不建议不建议print("hello world") -- print("hello world") -- 建议建议
模块定义模块定义module(...) module(...) 相当于做了以下的事情:相当于做了以下的事情:
local modname = ... -- local modname = ... -- 当前模块名当前模块名local M = {} -- local M = {} -- 定义模块定义模块_G[modname] = M -- _G[modname] = M -- 把模块加到全局变量中把模块加到全局变量中package.loaded[modname] = M -- package.loaded[modname] = M -- 模块已加载成功模块已加载成功setmetatable(M, {__index = _G}) -- setmetatable(M, {__index = _G}) -- 模块可以直接访模块可以直接访
问所有全局变量,相当于 问所有全局变量,相当于 module(..., module(..., package.seeall)package.seeall)
setfenv(1, M) -- setfenv(1, M) -- 保证在模块内定义和调用的函数都以模保证在模块内定义和调用的函数都以模块名为前缀块名为前缀
模块加载模块加载模块加载(模块加载( requirerequire )原理:)原理: function require(name)function require(name)
if not package.loaded[name] thenif not package.loaded[name] then
local loader = findloader(name)local loader = findloader(name)
if loader == nil thenif loader == nil then
error("unable to load module " .. name)error("unable to load module " .. name)
endend
package.load[name] = truepackage.load[name] = true
local res = loader(name)local res = loader(name)
if res ~= nil thenif res ~= nil then
package.loaded[name] = respackage.loaded[name] = res
endend
endend
return package.loaded[name]return package.loaded[name]
endend
访问模块外的变量访问模块外的变量11 )) setmetatable(M, {__index = _G}) -- setmetatable(M, {__index = _G}) -- 模块可直接访问全局变模块可直接访问全局变
量量
22 )) local _G = _G -- local _G = _G -- 可通过 可通过 _G.abc _G.abc 的方式来调用全局的 的方式来调用全局的 abc abc 变变量量
33 )按需加载,例如:)按需加载,例如: local math = math -- local math = math -- 在模块顶部显示的调用在模块顶部显示的调用 (如果是使用 (如果是使用 lua5.1 lua5.1 的 的 module module 函数来创建模块的话函数来创建模块的话 就只能通过这种方式来提供外部访问。)就只能通过这种方式来提供外部访问。)
Lua Lua 性能优化性能优化
尽量使用 尽量使用 localslocals
for i = 1, 1000000 dofor i = 1, 1000000 do
local x = math.sin(i)local x = math.sin(i)
endend
-- -- 下面这个快下面这个快 30%30% 左右左右 ::
local sin = math.sinlocal sin = math.sin
for i = 1, 1000000 dofor i = 1, 1000000 do
local x = sin(i)local x = sin(i)
endend
Loadstring Loadstring 和 和 ClosureClosure
尽量不用 尽量不用 loadstring loadstring ,用 ,用 closure closure 来代替,执行时间减少到 来代替,执行时间减少到 1/101/10
当然 当然 closure closure 闭包能不用就不用闭包能不用就不用
数字索引 数字索引 tabletable
lua lua 的 的 table table 由数字索引和字符串索引两部分组成。由数字索引和字符串索引两部分组成。
尽量使用数字索引 尽量使用数字索引 arrayarray ,而不使用 ,而不使用 hash tablehash table ::1. 1. 较大幅度提升性能较大幅度提升性能2. 2. 能减少内存使用能减少内存使用
提前 提前 hashhash为 为 table table 预先留位,可以省去 预先留位,可以省去 rehash rehash 的消耗的消耗
for i = 1, 1000000 dofor i = 1, 1000000 do
local a = {}local a = {}
a[1] = 1; a[2] = 2; a[3] = 3a[1] = 1; a[2] = 2; a[3] = 3
endend
-- -- 下面的快了下面的快了 60%60%
for i = 1, 1000000 dofor i = 1, 1000000 do
local a = {true, true, true}local a = {true, true, true}
a[1] = 1; a[2] = 2; a[3] = 3a[1] = 1; a[2] = 2; a[3] = 3
endend
慎用正则慎用正则
Lua Lua 没有 没有 POSIX POSIX 和 和 PCREPCRE ,这正是告诉我们一个明确的信号:,这正是告诉我们一个明确的信号:尽量不要使用正则表达式!尽量不要使用正则表达式!
字符串缓冲字符串缓冲字符串连接可以使用 字符串连接可以使用 table table 作为缓冲,然后用 作为缓冲,然后用 concat concat 来连接来连接
local s = '';local s = '';
for line in io.lines() dofor line in io.lines() do
s = s .. line .. “\n” -- s = s .. line .. “\n” -- 创建一个新字符串 创建一个新字符串 ss ,内存移动 ,内存移动 s s 这么大这么大endend
-- -- 下面这个要快十倍,特别是大文件下面这个要快十倍,特别是大文件local t = {}local t = {}
for line in io.lines() dofor line in io.lines() do
t[#t + 1] = linet[#t + 1] = line
endend
t[#t + 1] = ''t[#t + 1] = ''
s = table.concat(t, "\n")s = table.concat(t, "\n")
高效输出高效输出
print(a, b, c) print(a, b, c) 比 比 print(a..b..c) print(a..b..c) 更高效更高效io.write(a, b, c) io.write(a, b, c) 比 比 io.write(a..b..c) io.write(a..b..c) 更高效更高效
lua lua 还有很多这样的例子,原则是尽量避免创建字符串中间变量还有很多这样的例子,原则是尽量避免创建字符串中间变量
一次性定义一次性定义-- -- 定义一个对象并初始化定义一个对象并初始化a = {}; a.x = 10; a.y = 20a = {}; a.x = 10; a.y = 20
-- -- 用一次性定义,快用一次性定义,快 60%60%
a = { x = 10, y = 20 }a = { x = 10, y = 20 }
Table Table 重用重用local t = {}local t = {}
for i = 1970, 2000 dofor i = 1970, 2000 do
t[i] = os.time({year = i, month = 6, day = 14})t[i] = os.time({year = i, month = 6, day = 14})
endend
-- -- 下面的快下面的快 50%50% ,内存也使用更少,内存也使用更少local t = {}local t = {}
local aux = {year = nil, month = 6, day = 14}local aux = {year = nil, month = 6, day = 14}
for i = 1970, 2000 dofor i = 1970, 2000 do
aux.year = iaux.year = i
t[i] = os.time(aux)t[i] = os.time(aux)
endend
慎用回收慎用回收
不要随便调用 不要随便调用 collectgarbagecollectgarbage
因为它不一定有助于改善性能,要看实际情况而定因为它不一定有助于改善性能,要看实际情况而定
慎用变长参数慎用变长参数
除非是公开、不变的协议,否则函数除非是公开、不变的协议,否则函数 //方法中尽量不使用变长参数 方法中尽量不使用变长参数 ......
1. 1. 不容易理解不容易理解2. 2. 容易出容易出 bugbug
3. 3. 不容易移植不容易移植
低效的低效的 dofiledofile
-- -- 通过 通过 dofile dofile 或者或者 loadfile loadfile 加载解析的 加载解析的 lua lua 文件是不会被缓存的文件是不会被缓存的function dofile(filename)function dofile(filename)
local f = assert(loadfile(filename)) -- file io, no cachelocal f = assert(loadfile(filename)) -- file io, no cache
return f()return f()
endend
LUA_PATHLUA_PATH
尽量把经常用到的模块,或者自定义模块路径放到尽量把经常用到的模块,或者自定义模块路径放到package.pathpackage.path (( LUA_PATHLUA_PATH ) 最前面,比如:) 最前面,比如:
lua_package_path “./?.lua;/mylib/?.lua;lua_package_path “./?.lua;/mylib/?.lua;
/home/openresty/lualib/?.lua;/otherlib/?.lua;?.lua/home/openresty/lualib/?.lua;/otherlib/?.lua;?.lua
;;”;;”
全局扫描全局扫描
尽量不要在调用 尽量不要在调用 module module 时使用 时使用 package.seeall package.seeall 参数参数因为这会导致全局扫描和跨环境调用因为这会导致全局扫描和跨环境调用
全局扫描全局扫描
尽量不要在调用 尽量不要在调用 module module 时使用 时使用 package.seeall package.seeall 参数参数因为这会导致全局扫描和跨环境调用因为这会导致全局扫描和跨环境调用
Debug Debug 库库
线上环境尽量不要使用 线上环境尽量不要使用 debug debug 库,必要时 库,必要时 debug = nil debug = nil 清理掉清理掉
解析器解析器
LuaJIT LuaJIT 是目前为止最快的解析器,非必要情况不要更换。是目前为止最快的解析器,非必要情况不要更换。
FAQFAQ
谢谢!谢谢!