Lua性能优化
文章目录
table.new(narray, nhash)
This creates a pre-sized table, just like the C API equivalent
lua_createtable()
.This is useful for big tables if the final table size is known and automatic table resizing is too expensive.
预分配好数组和hash的大小,避免resize和rehash。
-
数组类型的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 44
local table_new = table.new local start_time local used_time local tableSize = 1000000 local stimer = function() ngx.update_time() start_time = ngx.now() end local etimer = function() ngx.update_time() used_time = ngx.now() - start_time end do local testTable = {} stimer() for i = 1, tableSize do local key = i local value = i testTable[key] = value end etimer() ngx.say("time used : ", used_time, " sec") end do local testTable = table_new(tableSize, tableSize) stimer() for i = 1, tableSize do local key = i local value = i testTable[key] = value end etimer() ngx.say("time used : ", used_time, " sec") end -- time used : 0.010999917984009 sec -- time used : 0.00099992752075195 sec
对于数组类型的table,性能相差近20倍
-
hash类型的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
math.randomseed(os.time()) local table_new = table.new local start_time local used_time local tableSize = 1000000 local stimer = function() ngx.update_time() start_time = ngx.now() end local etimer = function() ngx.update_time() used_time = ngx.now() - start_time end do local testTable = {} stimer() for i = 1, tableSize do local key = "key_" .. i local value = i testTable[key] = value end etimer() ngx.say("time used : ", used_time, " sec") end do local testTable = table_new(tableSize, tableSize) stimer() for i = 1, tableSize do local key = "key_" .. i local value = i testTable[key] = value end etimer() ngx.say("time used : ", used_time, " sec") end -- time used : 0.43399977684021 sec -- time used : 0.21200013160706 sec
差了2倍,不是很大。
pairs() vs ipairs()
|
|
数组类型的table,使用ipairs、pairs相差4倍。
table.clear(tab)
This clears all keys and values from a table, but preserves the allocated array/hash sizes. This is useful when a table, which is linked from multiple places, needs to be cleared and/or when recycling a table for use by the same context. This avoids managing backlinks, saves an allocation and the overhead of incremental array/hash part growth.
Please note, this function is meant for very specific situations. In most cases it’s better to replace the (usually single) link with a new table and let the GC do its work.
-
对于数组型的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
-- math.randomseed(os.time()) local table_new = table.new local table_clear = table.clear local start_time local used_time local tableSize = 1000000 local stimer = function() ngx.update_time() start_time = ngx.now() end local etimer = function() ngx.update_time() used_time = ngx.now() - start_time end local testTable = table_new(tableSize, 0) for i = 1, tableSize do local key = i local value = i testTable[key] = value end do stimer() table_clear(testTable) etimer() ngx.say("time used : ", used_time, " sec") end -- time used : 0 sec
-
对于hash table,根据清除再赋值的key是否一样又分两种情况:
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
-- math.randomseed(os.time()) local table_new = table.new local table_clear = table.clear local start_time local used_time local tableSize = 1000000 local stimer = function() ngx.update_time() start_time = ngx.now() end local etimer = function() ngx.update_time() used_time = ngx.now() - start_time end local testTable = table_new(tableSize, 0) do stimer() for i = 1, tableSize do local key = "key_" .. i local value = i testTable[key] = value end etimer() ngx.say("time used[创建表] : ", used_time, " sec") end do stimer() table_clear(testTable) etimer() ngx.say("time used[清除表] : ", used_time, " sec") end do stimer() for i = 1, tableSize do local key = "key_" .. i local value = i testTable[key] = value end etimer() ngx.say("time used[重新初始化表] : ", used_time, " sec") end do stimer() table_clear(testTable) etimer() ngx.say("time used[清除表] : ", used_time, " sec") end do stimer() for i = 1, tableSize do -- key变了 local key = "key_key" .. i local value = i testTable[key] = value end etimer() ngx.say("time used[重新初始化表,key变了] : ", used_time, " sec") end -- time used[创建表] : 0.41700005531311 sec -- time used[清除表] : 0.002000093460083 sec -- time used[重新初始化表] : 0.16700005531311 sec -- time used[清除表] : 0.0019998550415039 sec -- time used[重新初始化表,key变了] : 0.34599995613098 sec
可以看到,如果表清除后,如果赋值的key不变,还是之前的那些那么使用clear要相比重新创建和初始化要快2倍。
如果新赋值的key变了,并没有很节省时间,因此官方文档中也强调了,此函数只适用很特定的场景。
除非清除前后key不变,否则大多情况还是推荐创建新表的方式。
table.concat
|
|
两种方式性能差别巨大。
resty.lrucache vs resty.lrucache.pureffi
This library offers two different implementations in the form of two classes:
resty.lrucache
andresty.lrucache.pureffi
. Both implement the same API. The only difference is that the latter is a pure FFI implementation that also implements an FFI-based hash table for the cache lookup, while the former uses native Lua tables.If the cache hit rate is relatively high, you should use the
resty.lrucache
class which is faster thanresty.lrucache.pureffi
.However, if the cache hit rate is relatively low and there can be a lot of variations of keys inserted into and removed from the cache, then you should use the
resty.lrucache.pureffi
instead, because Lua tables are not good at removing keys frequently. You would likely see theresizetab
function call in the LuaJIT runtime being very hot in on-CPU flame graphs if you use theresty.lrucache
class instead ofresty.lrucache.pureffi
in such a use case.
|
|
原生lua table实现的版本插入要比纯ffi实现的要慢,读的速度则相反,所以读多插入少的场景应该用原生lua实现的版本,反之插入多读少的场景应该用纯ffi实现的版本。
参考
文章作者 XniLe
上次更新 2023-10-10