网上有大量对 Lua 调优的推荐,我们应该如何看待?

Lua 的解析器有官方的 standard Lua 和 LuaJIT,需要明确的一点是目前大量的优化文章都比较陈旧,而且都是针对 standard Lua 解析器的,standard Lua 解析器在性能上需要书写者自己规避问题,才能写出高性能来。

需要各位看官注意的是,OpenResty 最新版默认已经绑定 LuaJIT,优化手段和方法已经略有不同。我们现在的做法是:代码易读是首位,目前还没有碰到同样代码换个写法就有质的提升,如果我们对某个单点功能有性能要求,那么建议用 LuaJIT 的 FFI 方法直接调用 C 接口更直接一点。

代码出处:http://www.cnblogs.com/lovevivi/p/3284643.html

  1. -- 3.0 避免使用 table.insert()
  2. -- 下面来看看 4 个实现表插入的方法。在 4 个方法之中 table.insert() 在效率上不如其他方法,是应该避免使用的。
  3. -- (1) 使用 table.insert()
  4. local a = {}
  5. local table_insert = table.insert
  6. for i = 1,100 do
  7. table_insert( a, i )
  8. end
  9. -- (2) 使用循环的计数
  10. local a = {}
  11. for i = 1,100 do
  12. a[i] = i
  13. end
  14. -- (3) 使用 table size
  15. local a = {}
  16. for i = 1,100 do
  17. a[#a+1] = i
  18. end
  19. -- (4) 使用计数器
  20. local a = {}
  21. local index = 1
  22. for i = 1,100 do
  23. a[index] = i
  24. index = index+1
  25. end
  26. -- 4.0 减少使用 unpack() 函数
  27. -- Lua unpack() 函数不是一个效率很高的函数。你完全可以写一个循环来代替它的作用。
  28. -- (1) 使用 unpack()
  29. local a = { 100, 200, 300, 400 }
  30. for i = 1,100 do
  31. print( unpack(a) )
  32. end
  33. -- (2) 代替方法
  34. local a = { 100, 200, 300, 400 }
  35. for i = 1,100 do
  36. print( a[1],a[2],a[3],a[4] )
  37. end

针对这篇文章内容写了一些测试代码:

  1. local start = os.clock()
  2. local function sum( ... )
  3. local args = {...}
  4. local a = 0
  5. for k,v in pairs(args) do
  6. a = a + v
  7. end
  8. return a
  9. end
  10. local function test_unit()
  11. -- t1: 0.340182 s
  12. -- local a = {}
  13. -- for i = 1,1000 do
  14. -- table.insert( a, i )
  15. -- end
  16. -- t2: 0.332668 s
  17. -- local a = {}
  18. -- for i = 1,1000 do
  19. -- a[#a+1] = i
  20. -- end
  21. -- t3: 0.054166 s
  22. -- local a = {}
  23. -- local index = 1
  24. -- for i = 1,1000 do
  25. -- a[index] = i
  26. -- index = index+1
  27. -- end
  28. -- p1: 0.708012 s
  29. -- local a = 0
  30. -- for i=1,1000 do
  31. -- local t = { 1, 2, 3, 4 }
  32. -- for i,v in ipairs( t ) do
  33. -- a = a + v
  34. -- end
  35. -- end
  36. -- p2: 0.660426 s
  37. -- local a = 0
  38. -- for i=1,1000 do
  39. -- local t = { 1, 2, 3, 4 }
  40. -- for i = 1,#t do
  41. -- a = a + t[i]
  42. -- end
  43. -- end
  44. -- u1: 2.121722 s
  45. -- local a = { 100, 200, 300, 400 }
  46. -- local b = 1
  47. -- for i = 1,1000 do
  48. -- b = sum(unpack(a))
  49. -- end
  50. -- u2: 1.701365 s
  51. -- local a = { 100, 200, 300, 400 }
  52. -- local b = 1
  53. -- for i = 1,1000 do
  54. -- b = sum(a[1], a[2], a[3], a[4])
  55. -- end
  56. return b
  57. end
  58. for i=1,10 do
  59. for j=1,1000 do
  60. test_unit()
  61. end
  62. end
  63. print(os.clock()-start)

从运行结果来看,除了 t3 有本质上的性能提升(六倍性能差距,但是 t3 写法相当丑陋),其他不同的写法都在一个数量级上。你是愿意让代码更易懂还是更牛逼,就看各位看官自己的抉择了。不要盲信,也不要不信,各位要睁开眼自己多做测试。

另外说明:文章提及的使用局部变量、缓存 table 元素,在 LuaJIT 中还是很有用的。