lua性能优化



http://hongjinwei.blog.51cto.com/9107852/1432724  原文地址

LuaJIT本身对Lua作了很多方面的优化工作,对很多Lua自带的库函数进行了优化。

优化详情:http://wiki.luajit.org/NYI

wiki:http://wiki.luajit.org/Home


关于Lua优化的一些细节:


1.经常使用的库函数,使用local方式来调用,注意仅一次调用是不起作用的。

1
2
3
4
5
- - this  is  the lowest method
     string.rep()
 
- - this  is  a better way
     local rep  =  string.rep

2.创建table时,事先分配好大小。

1
2
3
4
--allocate the table when you declare it
     local tbl = {nil,nil}
     table.insert(tbl, 1 )
     table.insert(tbl, 2 )

lua table的内存分配按照2的N次方递增,所以在不断table.insert的过程中,会在前期频繁的申请内存空间因此事先申请好内存能够提升性能。


3.避免一些长字符串的format操作,长字符串的format操作效率低下,对于长字符串可以考虑使用..操作来进行字符串连接,效率比format高。

1
2
3
4
5
local str = [==[ hello %s ]==]
string.format(str, "world" )
 
local str = [[hello]]
return  str..  "world"


4.一个文件中的函数尽量使用local方式来定义

1
2
3
     local plus =  function (a,b)
         return  a+b
     end

5.一些结构的优化。这里我们定义了一个Filter,其中有typeA和typeB 2种类型的filter,遍历这些filter,我们有3种方式封装API:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
--now we define a filter
--we  get  three ways to define it
 
---all filters listed
local Filter = {nil,nil}
local Filter.typeA = {nil,nil}
local Filter.typeB = {nil,nil}
 
 
Filter.typeA.filter1 =  function ()
end
 
Filter.typeA.filter2 =  function ()
end
 
Filter.typeB.filter1 =  function ()
end 
 
Filter.typeB.filter2 =  function ()
end
 
--first way
function  handler(arg)
     if  not Filter.typeA.filter1() then
         return  false
     end
     
     if  not Filter.typeA.filter2() then
         return  false
     end
     
     if  not Filter.typeB.filter1() then
         return  false
     end
     
     if  not Filter.typeB.filter2() then
         return  false
     end
     
     return  true
end 
 
--second way
function  handler(arg)
     for  k,func  in  pairs(Filter.typeA)  do
         if  not func() then
             return  false
     end
    
     for  k,func  in  pairs(Filter.typeB)  do
         if  not func() then
             return  false
         end
     end
     
     return  true
end
 
--third way
local func_name_list = {  'filter1' , 'filter2' }
 
function  handler(arg)
     for  _,name  in  ipairs(func_name_list)  do
         if  not Filter.typeA[name]() then
             return  false
         end
         
         if  not Filter.typeB[name]() then
             return  false
         end
      end
 
      return  true
end
 
 
--fourth way
local typeA_func_list = {
                              Filter.typeA.filter1(),
                              Filter.typeA.filter2()                   
                         }
                         
local typeB_func_list = {
                              Filter.typeB.filter1(),
                              Filter.typeB.filter2()
                         }
function  handler(arg)
     for  _,func  in  ipairs(typeA_func_list)  do
         if  not func() then
             return  false
         end
     end
     
     for  _,func  in  ipairs(typeB_func_list)  do
         if  not func() then
             return  false
         end
     end
     
     return  true
end

这里值得注意的是,LuaJIT针对ipairs作了相当的优化,但是对于pairs,LuaJIT中没有作优化,在一些table中我们尽量避免使用hash表,遍历的时候使用iparis。

第一种方式是效率最高的,但是结构很糟糕,代码维护起来比较难。

第二种方式遍历效率极低。

第三种效率不错,代码结构一般。

第四种是最好的遍历方式,结构也很不错。我们采用这种方式是最优的。


6.关于table的遍历


在LuaJIT中针对ipairs作了相当的优化,但是对pairs没有优化。另外对于一些遍历,尽量使用for i,#table do end 来进行遍历。LuaJIT中针对for遍历也进行了优化。对于数组尽量使用for来遍历,这是最快的遍历方式。


7.关于config的加载,首先看测试脚本

1
2
3
4
5
6
7
8
9
-- conf_table.lua
    
    local conf_table = {nil,nil,nil} 
    
    conf_table.A =  "string A"
    conf_table.B =  "string B"
    conf_table.C =  "string C"
   
    return  conf_table
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  -- conf_notbl.lua
      
      A =  "string A"
      B =  "string B"
      C =  "string C"
-- conf_test.lua
     require  'conf_notbl'
     local conf = require  'conf_table'
     local socket = require  'socket'
     local round =  10000000
     local time = socket.gettime()
     for  i= 1 ,round  do
         local x = A
         local y = B
         local z = C
     end
     print( "conf before :"  .. (socket.gettime()-time)* 1000  ..  "ms" )
     local time = socket.gettime()
     for  i= 1 ,round  do
         local x = conf.A
         local y = conf.B
         local z = conf.C
     end    
     print( "conf table  :"  .. (socket.gettime()-time)* 1000  ..  "ms" )
     local a1=A
     local a2=B
     local a3=C
     local time = socket.gettime()
     for  i= 1 ,round  do
         local x = a1
         local y = a2
         local z = a3
     end
     print( "local conf :"  .. (socket.gettime()-time)* 1000  ..  "ms" )  
测试结果:
Lua:
     conf before : 742 .80691146851ms
     conf table  : 789 .02816772461ms
     local conf : 403 .17010879517ms
LuaJIT:
     conf before : 4 .1639804840088ms
     conf table  : 4 .1368007659912ms
     local conf : 3 .9420127868652ms

在使用lua文件来加载config的时候,尽量使用local方式来将经常使用的conf的数据存起来,如果使用次数不多,这样做就没有必要了。

另外特意使用一个table来存储conf数据并没有必要,但是这样做config结构比较好。


8.关于table.insert

luajit中对于table.insert作了优化。对于数组类型的table,可以利用tbl[#tbl+1]的方式来进行赋值操作,直接用lua解释器可以看到明显的性能差距,用luajit的话差距就没有这么明显了。

1
2
3
4
5
6
7
8
9
10
11
12
local tbl = {}
table.insert(tbl, 1 )
tbl[#tbl+ 1 ] =  1
--运行 1000000 次的结果
Lua:
     table.insert time : 293 .66397857666ms
     tbl[#tbl+ 1 ] time : 186 .48815155029ms
     local insert time : 222 .85604476929ms  --添加了 local insert = table.insert
LuaJIT:
     table.insert time : 137 .73202896118ms
     tbl[#tbl+ 1 ] time : 134 .56797599792ms
     local insert time : 137 .2811794281ms

可以看到LuaJIT对于table.insert的操作作了优化,所以使用luajit时可以不必刻意使用tbl[#tbl+1]=1的方式来进行insert操作。另外LuaJIT对于local调用也作了优化。


9.对于数组table是否为空的判断

    尽量使用table.getn,对于hash表则必须使用next,table.getn不能判断hash表是否为空。

你可能感兴趣的:(lua性能优化)