├── .github └── workflows │ ├── ci_lj20.yml │ ├── ci_lj21.yml │ ├── ci_lj2resty.yml │ ├── ci_lua51.yml │ ├── ci_lua52.yml │ ├── ci_lua53.yml │ └── ci_lua54.yml ├── Guide.zh.md ├── LICENSE ├── README.md ├── main.lua ├── scripts └── find-lua.sh └── src ├── function-call.lua ├── function-localize.lua ├── init.lua ├── meta-function.lua ├── multi-conditions.lua ├── name-length.lua ├── set-default-value.lua ├── table-append.lua └── tail-recursion.lua /.github/workflows/ci_lj20.yml: -------------------------------------------------------------------------------- 1 | name: CI LuaJIT 2.0 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup LuaJIT 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "luajit-2.0.5" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_lj21.yml: -------------------------------------------------------------------------------- 1 | name: CI LuaJIT 2.1 beta 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup LuaJIT 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "luajit-2.1.0-beta3" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_lj2resty.yml: -------------------------------------------------------------------------------- 1 | name: CI LuaJIT 2 OpenResty Fork 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup LuaJIT 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "luajit-openresty" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_lua51.yml: -------------------------------------------------------------------------------- 1 | name: CI Lua 5.1 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Lua 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "5.1.5" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_lua52.yml: -------------------------------------------------------------------------------- 1 | name: CI Lua 5.2 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Lua 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "5.2.4" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_lua53.yml: -------------------------------------------------------------------------------- 1 | name: CI Lua 5.3 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Lua 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "5.3.6" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /.github/workflows/ci_lua54.yml: -------------------------------------------------------------------------------- 1 | name: CI Lua 5.4 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | ReportOnLua: 15 | name: Lua Performance Test Report 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Lua 21 | uses: leafo/gh-actions-lua@v9 22 | with: 23 | luaVersion: "5.4.4" 24 | 25 | - name: Show Lua Version Report 26 | working-directory: ${{ github.workflow.workflow_root }} 27 | run: | 28 | lua -v 29 | 30 | - name: Run Lua Performance Test 31 | working-directory: ${{ github.workflow.workflow_root }} 32 | run: | 33 | lua main.lua run 34 | -------------------------------------------------------------------------------- /Guide.zh.md: -------------------------------------------------------------------------------- 1 | Lua脚本性能优化指南 2 | ================= 3 | 4 | [![CI Lua 5.1](https://github.com/flily/lua-performance/actions/workflows/ci_lua51.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua51.yml) 5 | [![CI Lua 5.2](https://github.com/flily/lua-performance/actions/workflows/ci_lua52.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua52.yml) 6 | [![CI Lua 5.3](https://github.com/flily/lua-performance/actions/workflows/ci_lua53.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua53.yml) 7 | [![CI Lua 5.4](https://github.com/flily/lua-performance/actions/workflows/ci_lua54.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua54.yml) 8 | [![CI LuaJIT 2.0](https://github.com/flily/lua-performance/actions/workflows/ci_lj20.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lj20.yml) 9 | [![CI LuaJIT 2.1 beta](https://github.com/flily/lua-performance/actions/workflows/ci_lj21.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lj21.yml) 10 | [![CI LuaJIT 2 OpenResty Fork](https://github.com/flily/lua-performance/actions/workflows/ci_lj2resty.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lj2resty.yml) 11 | 12 | 13 | 14 | Lua脚本是C语言实现的脚本,广泛应用于客户端扩展脚本,例如魔兽世界等网游。但是Lua的性能一般,并且有许多不好的实现,误用会大大降低系统的性能。 15 | 网络上有一些关于Lua脚本性能优化的资料,但是都是针对Lua撰写的,写作年代较早,一些优化技巧不完全正确,而且没有针对LuaJIT优化过后的代码进行考虑。 16 | 本章对于Lua的一些语法,在Lua和LuaJIT中进行比较测试,并给出相关优化数据和结论。 17 | 18 | 由于LuaJIT的性能较Lua有很大的提高,在测试时使用的循环次数不同,以避免时间太短导致测量不准确。 19 | 在结果中,`In LuaJIT (100x)`表示LuaJIT中执行性能但愿测试次数是Lua中的100倍。 20 | 21 | 相关参考资料: 22 | 23 | 1. Roberto Ierusalimschy. Lua Performance Tips. http://www.lua.org/gems/sample.pdf 24 | 2. Lua Performance: http://springrts.com/wiki/Lua_Performance 25 | 26 | 目录 27 | ---- 28 | * [基本语法](#基本语法) 29 | + [变量局部化](#变量局部化) 30 | + [多重条件判断](#多重条件判断) 31 | + [函数调用](#函数调用) 32 | + [尾递归](#尾递归) 33 | * [Table](#Table) 34 | + [table追加](#table追加) 35 | 36 | 37 | 变量局部化 38 | --------- 39 | 40 | Lua变量区分为全局变量和局部变量。Lua为每一个函数分配了一套多达250个的寄存器,并用这些寄存器存储局部变量,这使得Lua中局部变量的访问速度很快。 41 | 相反的是,对于全局变量,Lua需要将全局变量读出存入当前函数的寄存器中,然后完成计算之后再存回全局变量表中。 42 | 这样,一个类似`a = a + b`这样的简单计算,若`a`和`b`为局部变量,则编译之后只生成一条Lua指令,而若为全局变量,则生成4条Lua指令。*Lua Performance Tips*中提到,将全局变量变为局部变量,然后在使用进行访问,速度可以提升约30%,编写如下代码进行实验: 43 | 44 | **代码和结果** 45 | ```lua 46 | function nonlocal() 47 | local x = 0 48 | for i = 1, 100 do 49 | x = x + math.sin(i) 50 | end 51 | return x 52 | end 53 | 54 | local sin = math.sin 55 | function localized() 56 | local x = 0 57 | for i = 1, 100 do 58 | x = x + sin(i) 59 | end 60 | return x 61 | end 62 | 63 | --[[------------------------ 64 | In Lua (1x) 65 | ID Case name t1 t2 t3 t4 t5 avg. % 66 | 1 Non-local 1.66 1.65 1.65 1.66 1.66 1.656 100% 67 | 2 Localized 1.25 1.25 1.25 1.25 1.25 1.25 75.48% 68 | In LuaJIT (2x) 69 | ID Case name t1 t2 t3 t4 t5 avg. % 70 | 1 Non-local 1.3 1.31 1.3 1.3 1.3 1.302 100% 71 | 2 Localized 1.3 1.3 1.3 1.29 1.3 1.298 99.69% 72 | --]]------------------------ 73 | ``` 74 | 75 | **结论** 76 | 在普通Lua的解释下,行为和//Lua Performance Tips//中所述大致一致,调用全局变量中的函数大约会慢30%。 77 | 而在LuaJIT的解释下,两者差异不明显。 78 | 不是一定需要将全局变量中的变量转变为局部变量。 79 | 80 | 81 | 82 | 多重条件判断 83 | ---------- 84 | 在Lua中,唯一的数据结构table是哈希表,创建、销毁和迭代都需要创建很多资源。 85 | 本例对比当判断较多条件时,使用连续逻辑表达式和使用table的性能。 86 | 87 | **代码和结果** 88 | ```lua 89 | function use_or() 90 | local x = 0 91 | for _, v in pairs(states) do 92 | if "alabama" == v or 93 | "california" == v or 94 | "missouri" == v or 95 | "virginia" == v or 96 | "wisconsin" == v then 97 | x = x + 1 98 | end 99 | end 100 | return x 101 | end 102 | 103 | function use_table() 104 | local x, vals = 0, {"alabama", "california", "missouri", "virginia", "wisconsin"} 105 | for _, v in pairs(states) do 106 | for _, s in pairs(vals) do 107 | if s == v then x = x + 1 end 108 | end 109 | end 110 | return x 111 | end 112 | 113 | --[[------------------------ 114 | In Lua (1x) 115 | ID Case name t1 t2 t3 t4 t5 avg. % 116 | 1 Use OR 0.39 0.38 0.39 0.39 0.38 0.386 100% 117 | 2 Use table 1.85 1.84 1.84 1.85 1.84 1.844 477.72% 118 | In LuaJIT (10x) 119 | ID Case name t1 t2 t3 t4 t5 avg. % 120 | 1 Use OR 0.6 0.6 0.59 0.59 0.6 0.596 100% 121 | 2 Use table 2.82 2.85 2.86 2.86 2.85 2.848 477.85% 122 | --]]------------------------ 123 | ``` 124 | 125 | **结论** 126 | 当判断较多逻辑条件时,应当使用简单的逻辑运算,而table创建、销毁和迭代的开销较大,应避免使用。 127 | 尽管使用循环的方法,程序可能具有较好的可读性,但是性能会严重下降。 128 | 使用逻辑表达式判断,采取较好的写法也可以获得可读性。 129 | 130 | 函数调用 131 | -------- 132 | 函数是我们对功能进行封装的基本方法,但是函数的调用也是具有一定的开销,本例反映了函数调用的时间开销问题。 133 | 其中,直接计算部分在测试函数中内嵌代码进行阶乘计算,而函数调用部分则使用另外封装的阶乘计算函数。 134 | 135 | **代码和结果** 136 | ```lua 137 | function direct_compute() 138 | local x = 0 139 | for i = 1, 30 do 140 | local r = 1 141 | for j = 1, i do 142 | r = r * j 143 | end 144 | x = x + r 145 | end 146 | return x 147 | end 148 | 149 | function fact(x) 150 | local result = 1 151 | for i = 1, x do 152 | result = result * i 153 | end 154 | return result 155 | end 156 | 157 | function call_function() 158 | local x = 0 159 | for i = 1, 30 do 160 | x = x + fact(i) 161 | end 162 | return x 163 | end 164 | 165 | --[[------------------------ 166 | In Lua (1x) 167 | ID Case name t1 t2 t3 t4 t5 avg. % 168 | 1 Direct compute 1.17 1.17 1.17 1.17 1.18 1.172 100% 169 | 2 Call function 1.5 1.5 1.51 1.51 1.5 1.504 128.33% 170 | In LuaJIT (10x) 171 | ID Case name t1 t2 t3 t4 t5 avg. % 172 | 1 Direct compute 1.08 1.08 1.08 1.08 1.07 1.078 100% 173 | 2 Call function 1.32 1.32 1.33 1.32 1.32 1.322 122.63% 174 | --]]------------------------ 175 | ``` 176 | 177 | **结论** 178 | 使用函数封装之后,消耗的时间Lua中增加28%,LuaJIT中增加22%。如果是会反复调用的功能,如无必要,如代码复用等问题,则应当尽可能避免多次调用函数。 179 | 180 | 尾递归 181 | ------ 182 | 在一般的编程语言中,函数递归会占用栈空间,层次很深的递归容易导致栈溢出。 183 | 在一些编程语言,尤其是函数式编程语言中,对一种特殊的递归方法——尾递归进行了优化。 184 | Lua中也是如此,使得尾递归的函数在递归开始时会释放当前函数的调用栈空间,从而保证多级递归也不会额外占用栈空间。 185 | 本例中展示尾递归的性能,使用普通递归、普通循环和尾递归三种方法编写计算阶乘的程序,并以普通递归方法作的数据为对比的基准。 186 | 187 | **代码和结果** 188 | ```lua 189 | -- 普通递归 190 | function fact_recurse(x) 191 | if x <= 1 then return 1 end 192 | return x * fact_recurse(x - 1) 193 | end 194 | 195 | -- 普通循环 196 | function fact_normal(x) 197 | local result = 1 198 | for i = 2, x do 199 | result = result * i 200 | end 201 | return result 202 | end 203 | 204 | -- 尾递归 205 | function fact_tail(x, r) 206 | local result = r or 1 207 | if x <= 1 then return result end 208 | return fact_tail(x - 1, result * x) 209 | end 210 | 211 | --[[------------------------ 212 | In Lua (1x) 213 | ID Case name t1 t2 t3 t4 t5 avg. % 214 | 1 Normal recurse 1.88 1.88 1.89 1.88 1.88 1.882 100% 215 | 2 Normal loop 0.5 0.49 0.5 0.5 0.49 0.496 26.35% 216 | 3 Tail recurse 1.99 2 1.99 1.99 1.99 1.992 105.84% 217 | In LuaJIT (20x) 218 | ID Case name t1 t2 t3 t4 t5 avg. % 219 | 1 Normal recurse 3.02 3 3 3 2.99 3.002 100% 220 | 2 Normal loop 0.98 0.97 0.97 0.97 0.98 0.974 32.45% 221 | 3 Tail recurse 1.06 1.07 1.07 1.07 1.07 1.068 35.58% 222 | --]]------------------------ 223 | ``` 224 | 225 | **结论** 226 | 虽然Lua中强调了对尾递归的优化,但是实际执行结果表明,Lua中的尾递归只是对栈空间的分配进行了优化,速度并没有比普通递归有提升。 227 | 而在LuaJIT中,使用尾递归编写的函数具有与普通循环相接近的性能。 228 | 不过尾递归函数不太容易编写,在实际使用过程中**可以**使用。 229 | 230 | Table 231 | ===== 232 | 233 | table追加 234 | --------------- 235 | 236 | Lua的标准库函数中,并不是所有函数都实现得很好,尤其是table数据结构的实现性能较差,`table.insert`函数就是一个性能较低的函数。 237 | 238 | 239 | **代码和结果** 240 | ```lua 241 | function table_insert() 242 | local t = {} 243 | for i = 1, 1000, 2 do 244 | table.insert(t, i) 245 | end 246 | return t 247 | end 248 | 249 | function table_insertL() 250 | local t, insert = {}, table.insert 251 | for i = 1, 1000, 2 do 252 | insert(t, i) 253 | end 254 | return t 255 | end 256 | 257 | function use_counter() 258 | local t, c = {}, 1 259 | for i = 1, 1000, 2 do 260 | t[c], c = i, c + 1 261 | end 262 | return t 263 | end 264 | 265 | function use_length() 266 | local t = {} 267 | for i = 1, 1000, 2 do 268 | t[#t + 1] = i 269 | end 270 | end 271 | 272 | --[[------------------------ 273 | In Lua (1x) 274 | ID Case name t1 t2 t3 t4 t5 avg. % 275 | 1 table.insert G 2.72 2.73 2.72 2.73 2.72 2.724 100% 276 | 2 table.insert L 2.24 2.24 2.24 2.24 2.24 2.24 82.23% 277 | 3 use counter 1.13 1.13 1.14 1.12 1.13 1.13 41.48% 278 | 4 use length 1.43 1.44 1.43 1.43 1.43 1.432 52.57% 279 | In LuaJIT (5x) 280 | ID Case name t1 t2 t3 t4 t5 avg. % 281 | 1 table.insert G 3.69 3.69 3.68 3.68 3.69 3.686 100% 282 | 2 table.insert L 3.75 3.75 3.75 3.75 3.74 3.748 101.68% 283 | 3 use counter 0.86 0.85 0.85 0.86 0.85 0.854 23.17% 284 | 4 use length 3.71 3.71 3.71 3.71 3.71 3.71 100.65% 285 | --]]------------------------ 286 | ``` 287 | 288 | ### 结论 ### 289 | 当需要生成一个数组,并往数组的尾部添加数据时,应当尽可能的使用计数器,如果没办法使用计数器,也应当使用`#`运算符先求出数组的长度,然后使用计数器插入数组。 290 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Flily Hsu 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-performance 2 | =============== 3 | 4 | [![CI Lua 5.1](https://github.com/flily/lua-performance/actions/workflows/ci_lua51.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua51.yml) 5 | [![CI Lua 5.2](https://github.com/flily/lua-performance/actions/workflows/ci_lua52.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua52.yml) 6 | [![CI Lua 5.3](https://github.com/flily/lua-performance/actions/workflows/ci_lua53.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua53.yml) 7 | [![CI Lua 5.4](https://github.com/flily/lua-performance/actions/workflows/ci_lua54.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lua54.yml) 8 | [![CI LuaJIT 2.0](https://github.com/flily/lua-performance/actions/workflows/ci_lj20.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lj20.yml) 9 | [![CI LuaJIT 2.1 beta](https://github.com/flily/lua-performance/actions/workflows/ci_lj21.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lj21.yml) 10 | [![CI LuaJIT 2 OpenResty Fork](https://github.com/flily/lua-performance/actions/workflows/ci_lj2resty.yml/badge.svg)](https://github.com/flily/lua-performance/actions/workflows/ci_lj2resty.yml) 11 | 12 | 13 | 14 | A Lua performance guide for Lua and LuaJIT. 15 | 16 | TODO: 17 | * Add more cases for performance guide. 18 | * Add test results for different platform, including x86 and ARM. 19 | * Add English edition of performance guide. 20 | 21 | 22 | Usage 23 | ===== 24 | 25 | Performance guide text 26 | ---------------------- 27 | - [Original edition (in Chinese)](https://github.com/flily/lua-performance/blob/master/Guide.zh.md) 28 | - English edition (working) 29 | 30 | 31 | Executable testing scripts 32 | -------------------------- 33 | 34 | ### To see results in your environment 35 | Runs `lua main.lua run` 36 | 37 | ### Sub commands (usage) 38 | - `env`, display lua environment of current interpreter, support official implemented lua and LuaJIT. 39 | - `list`, show all test cases included in code. 40 | - `run`, run test cases specified in arguments, it will run all cases while no cases is specified. 41 | 42 | 43 | How to add new cases 44 | -------------------- 45 | 46 | ### **Step 1**: write codes 47 | Write down your test codes in different functions, with no input argument, in a single module file. 48 | 49 | ### **Step 2**: return info of test suite 50 | At the end of the module file, returns a table including information of test, with 3 fields: 51 | - `name`, `[string]` name of this test suite, it MUST BE different from any other cases. 52 | - `desc`, `[string]` description of test suite, ONLY used in `list` command,. 53 | - `cases`, `[table]` an array of entries of test cases, including **2** fields: 54 | - `name`, `[string]` name of this test case. 55 | - `entry`, `[function]`, entry of test case. 56 | 57 | ### **Step 3**: add index of test suite 58 | Add name of test suite, which is filename of module lua file of test suite, directly pass to require function, 59 | to index array, variable `load_list`, found at the last of file `src/init.lua`. 60 | 61 | ### **Step 4 FINAL**: run your test 62 | Run your test with `main.lua`. 63 | 64 | ### Example 65 | write your test suite. 66 | ```lua 67 | function case_a() 68 | -- some codes 69 | end 70 | 71 | function case_b() 72 | -- some codes 73 | end 74 | 75 | return { 76 | name = "example" 77 | desc = "example of case." 78 | cases = { 79 | { name = "case A", entry = case_a }, 80 | { name = "case B", entry = case_b }, 81 | } 82 | } 83 | ``` 84 | 85 | and add to 'src/init.lua' 86 | ```diff 87 | --- a/src/init.lua 88 | +++ b/src/init.lua 89 | @@ -54,6 +54,7 @@ local load_list = { 90 | "set-default-value", 91 | "table-append", 92 | "tail-recursion", 93 | + "example", 94 | } 95 | ``` 96 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | --[[ 4 | Main entry to launch test cases. 5 | --]] 6 | 7 | 8 | function round(num, idp) 9 | local mult = 10^(idp or 3) 10 | return math.floor(num * mult + 0.5) / mult 11 | end 12 | 13 | 14 | function average(nums) 15 | local sum = 0 16 | for i = 1, #nums do sum = sum + nums[i] end 17 | return sum / #nums 18 | end 19 | 20 | 21 | function runtest(func, times) 22 | local begin = os.clock() 23 | for i = 1, times or 100000 do 24 | func() 25 | end 26 | local endtm = os.clock() 27 | return round(endtm - begin) 28 | end 29 | 30 | 31 | function dotest(cases, times) 32 | for i = 1, #cases do 33 | local case = cases[i] 34 | io.stdout:write(string.format("Case %d: %s ", i, case.name)) 35 | 36 | case.t = {} 37 | for j = 1, 5 do 38 | io.stdout:write(".") 39 | io.stdout:flush() 40 | collectgarbage() 41 | local runtime = runtest(case.entry, times) 42 | case.t[#case.t + 1] = runtime 43 | end 44 | print() 45 | end 46 | 47 | print("ID", "Case name", "t1", "t2", "t3", "t4", "t5", "avg.", "%") 48 | local base = 0 49 | for i = 1, #cases do 50 | local case = cases[i] 51 | local t, avg = case.t, average(case.t) 52 | 53 | if i == 1 then base = avg end 54 | local per = avg / base 55 | 56 | print(i, case.name, t[1], t[2], t[3], t[4], t[5], avg, round(100*per,2) .. "%") 57 | end 58 | print("----------------") 59 | end 60 | 61 | function dotimes(t, jt) 62 | local basetimes = 1000 63 | local times = t or 1 64 | if is_luajit() then times = times * (jt or 100) end 65 | return basetimes * times 66 | end 67 | 68 | 69 | local function check_enviroment() 70 | local is_luajit = not (nil == jit) 71 | local o = { 72 | ["Syntax version"] = _VERSION, 73 | ["Is LuaJIT"] = is_luajit, 74 | ["LuaJIT version"] = is_luajit and jit.version or "No LuaJIT", 75 | } 76 | 77 | return o 78 | end 79 | 80 | 81 | local function load_test_case() 82 | package.path = package.path .. ";./src/?.lua" 83 | local cases = require "src.init" 84 | 85 | return cases 86 | end 87 | 88 | 89 | local function usage(interpreter, executable) 90 | print("Usage:") 91 | print(string.format(" %s %s command [args...]", 92 | interpreter, executable)) 93 | print() 94 | print("Commands:") 95 | print(" env Show lua environment information.") 96 | print(" list List all test cases.") 97 | print(" run [cases ...] Run test cases given. Run all cases if args is missing.") 98 | end 99 | 100 | 101 | local function cmd_do_env() 102 | local env_info = check_enviroment() 103 | 104 | print("Environment information:") 105 | for k, v in pairs(env_info) do 106 | print(string.format(" - %s: %s", k, tostring(v))) 107 | end 108 | end 109 | 110 | 111 | local function cmd_do_list() 112 | local cases = load_test_case() 113 | local title = string.format(" %4s %-20s %-60s %s", 114 | "No.", "Name", "Description", "Count of cases") 115 | local l = string.rep("-", 1 + #title) 116 | print(title) 117 | print(l) 118 | 119 | for i, m in cases.each() do 120 | print(string.format(" %4d %-20s %-60s %d", i, m.name, m.desc, #m.cases)) 121 | end 122 | end 123 | 124 | 125 | local function cmd_do_run(names) 126 | local cases = load_test_case() 127 | local env_info = check_enviroment() 128 | local run_all = false 129 | 130 | if #names <= 0 or names[1] == "all" then 131 | names = cases.all_names 132 | end 133 | 134 | print("Running environment") 135 | print(string.format("Lua syntax version %s, LuaJIT=%s", 136 | tostring(env_info["Syntax version"]), 137 | tostring(env_info["Is LuaJIT"]))) 138 | print(string.rep("=", 60)) 139 | 140 | 141 | for _, name in ipairs(names) do 142 | local c = cases.fetch(name) 143 | if nil == c then 144 | print(string.format("Test case '%s' not found", name)) 145 | 146 | else 147 | local entries = c.cases 148 | print(string.format("Running test '%s' ......", c.name)) 149 | dotest(entries) 150 | end 151 | end 152 | end 153 | 154 | 155 | local function main() 156 | if nil == arg[1] then 157 | usage(arg[-1], arg[0]) 158 | return 159 | end 160 | 161 | local cmd = arg[1] 162 | if "env" == cmd then 163 | cmd_do_env() 164 | elseif "list" == cmd then 165 | cmd_do_list() 166 | elseif "run" == cmd then 167 | local names = {} 168 | for i = 2, #arg, 1 do 169 | names[i - 1] = arg[i] 170 | end 171 | cmd_do_run(names) 172 | end 173 | end 174 | 175 | 176 | main() 177 | -------------------------------------------------------------------------------- /scripts/find-lua.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ############################################################ 4 | # find-lua.sh 5 | ############################################################ 6 | 7 | 8 | DEBUG_MODE="off" 9 | FIND_ALL="off" 10 | 11 | USE_LUA="on" 12 | USE_LUAJIT="off" 13 | 14 | PATH_PREFER_LIST=" 15 | /usr/local/bin 16 | /usr/bin 17 | /bin 18 | ${HOME}/usr/bin 19 | ${HOME}/bin 20 | " 21 | 22 | debug_print() 23 | { 24 | if [ "${DEBUG_MODE}" = "on" ]; then 25 | printf "%s" "$1" >&2 26 | fi 27 | } 28 | 29 | debug_println() 30 | { 31 | if [ "${DEBUG_MODE}" = "on" ]; then 32 | printf "%s\\n" "$1" >&2 33 | fi 34 | } 35 | 36 | usage() 37 | { 38 | echo "usage:" 39 | echo " $0 [options..] [path]" 40 | echo "" 41 | echo "options:" 42 | echo " --help show this message" 43 | echo " --debug run in debug mode." 44 | echo " --lua51 find lua 5.1" 45 | echo " --lua52 find lua 5.2" 46 | echo " --lua53 find lua 5.3" 47 | echo " --with(out)-lua find with or without lua official implement, included by default." 48 | echo " --with(out)-luajit find with or without luajit, excluded by deafult." 49 | } 50 | 51 | check_lua_version() 52 | { 53 | LUA_BIN_PATH="$1" 54 | LUA_VERSION="$2" 55 | if [ -z "${LUA_VERSION}" ]; then 56 | LUA_VERSION="" 57 | fi 58 | 59 | debug_print "checking lua ${LUA_VERSION} binary ${LUA_BIN_PATH}..." 60 | 61 | if [ ! -f "${LUA_BIN_PATH}" ]; then 62 | debug_println "miss" 63 | return 1 64 | fi 65 | 66 | if [ ! -x "${LUA_BIN_PATH}" ]; then 67 | debug_println "non-executable" 68 | return 2 69 | fi 70 | 71 | # VERSION_TAG="$("${LUA_BIN_PATH}" --version 2>&1)" 72 | RELEASE_TAG="$("${LUA_BIN_PATH}" -v 2>&1 | awk '{print $1}')" 73 | VERSION_TAG="$("${LUA_BIN_PATH}" -v 2>&1 | awk '{print $2}')" 74 | 75 | debug_print " ${RELEASE_TAG} ${VERSION_TAG} -> " 76 | 77 | if [ "${RELEASE_TAG}" = "LuaJIT" ]; then 78 | if [ "${USE_LUAJIT}" != "on" ]; then 79 | debug_println "skip LuaJIT" 80 | return 3 81 | fi 82 | 83 | if [ -z "${LUA_VERSION}" ] || [ "${LUA_VERSION}" = "5.1" ]; then 84 | debug_println "ok" 85 | return 0 86 | else 87 | debug_println "error" 88 | return 4 89 | fi 90 | fi 91 | 92 | if [ "${RELEASE_TAG}" = "Lua" ]; then 93 | if [ "${USE_LUA}" != "on" ]; then 94 | debug_println "skip Lua" 95 | return 3 96 | fi 97 | 98 | if [ -z "${LUA_VERSION}" ]; then 99 | debug_println "ok" 100 | return 0 101 | fi 102 | 103 | VERSION_MAJOR="$(echo "${VERSION_TAG}" | cut -c 1-3 )" 104 | debug_print "(${VERSION_MAJOR}) " 105 | 106 | if [ "${VERSION_MAJOR}" = "${LUA_VERSION}" ]; then 107 | debug_println "ok" 108 | return 0 109 | else 110 | debug_println "error" 111 | return 4 112 | fi 113 | fi 114 | 115 | 116 | 117 | } 118 | 119 | 120 | find_probable_lua_name() 121 | { 122 | FIND_PATH="$1" 123 | PROBABLE_NAMES="$(find "${FIND_PATH}" -name 'lua*' 2>/dev/null)" 124 | for x in ${PROBABLE_NAMES}; do 125 | LUA_NAME="$(basename "$x")" 126 | # debug_println "FIND_PATH=${FIND_PATH} x=${x} LUA_NAME=${LUA_NAME}" 127 | 128 | case "${LUA_NAME}" in 129 | "luac"*) 130 | # debug_println "[SKIP] Find luac: ${FIND_PATH}/${LUA_NAME}" 131 | ;; 132 | "lua"*) 133 | # debug_println "[INFO] Find lua interpreter: ${FIND_PATH}/${LUA_NAME}" 134 | echo "${FIND_PATH}/${LUA_NAME}" 135 | ;; 136 | *) 137 | ;; 138 | esac 139 | done 140 | } 141 | 142 | 143 | check_lua_in_path() 144 | { 145 | ALL_PATH_LIST="$1" 146 | LUA_VERSION="$2" 147 | 148 | # debug_println "FIND PATH LIST:" 149 | # debug_println "${ALL_PATH_LIST}" 150 | # debug_println "===============" 151 | 152 | FOUND_LUA_PATH="" 153 | for BASE_PATH in ${ALL_PATH_LIST}; do 154 | debug_println "finding lua ${LUA_VERSION} in ${BASE_PATH}.." 155 | 156 | for LUA_NAME in $(find_probable_lua_name "${BASE_PATH}"); do 157 | LUA_BIN_PATH="${LUA_NAME}" 158 | 159 | if check_lua_version "${LUA_BIN_PATH}" "${LUA_VERSION}"; then 160 | if [ -z "${FOUND_LUA_PATH}" ]; then 161 | FOUND_LUA_PATH="${LUA_BIN_PATH}" 162 | fi 163 | 164 | if [ "${FIND_ALL}" = "off" ]; then 165 | break 166 | fi 167 | fi 168 | done 169 | 170 | if [ -n "${FOUND_LUA_PATH}" ]; then 171 | if [ "${FIND_ALL}" = "off" ]; then 172 | break 173 | fi 174 | fi 175 | done 176 | 177 | if [ -n "${FOUND_LUA_PATH}" ]; then 178 | echo "${FOUND_LUA_PATH}" 179 | fi 180 | } 181 | 182 | 183 | find_lua() 184 | { 185 | CHECK_PATH="" 186 | LUA_VERSION="" 187 | 188 | while [ -n "$1" ]; do 189 | case "$1" in 190 | "/"*) 191 | CHECK_PATH="${CHECK_PATH} $1" 192 | ;; 193 | "--debug") 194 | DEBUG_MODE="on" 195 | ;; 196 | "--all") 197 | FIND_ALL="on" 198 | ;; 199 | "--lua"*) 200 | LUA_VERSION="$(echo "$1" | cut -c 6-)" 201 | ;; 202 | "--with-lua") 203 | USE_LUA="on" 204 | ;; 205 | "--without-lua") 206 | USE_LUA="off" 207 | ;; 208 | "--with-luajit") 209 | USE_LUAJIT="on" 210 | ;; 211 | "--without-luajit") 212 | USE_LUAJIT="off" 213 | ;; 214 | "--help"|"-h"|"-?") 215 | usage 216 | exit 217 | ;; 218 | esac 219 | shift 220 | done 221 | 222 | # check_path is empty, check for all directory listed. 223 | if [ -z "${CHECK_PATH}" ]; then 224 | SYSTEM_LUA_PATH="$(command -v lua 2>/dev/null)" 225 | if [ -n "${SYSTEM_LUA_PATH}" ]; then 226 | PATH_PREFER_LIST="${PATH_PREFER_LIST} $(dirname "${SYSTEM_LUA_PATH}")" 227 | fi 228 | 229 | ALL_PATH_LIST="${PATH_PREFER_LIST} $(echo "$PATH" | tr ':' ' ')" 230 | check_lua_in_path "${ALL_PATH_LIST}" "${LUA_VERSION}" 231 | return $? 232 | fi 233 | 234 | if [ -f "${CHECK_PATH}" ]; then 235 | if check_lua_version "${CHECK_PATH}" "${LUA_VERSION}"; then 236 | echo "ok" 237 | return 0 238 | else 239 | echo "error" 240 | return 1 241 | fi 242 | else 243 | check_lua_in_path "${CHECK_PATH}" "${LUA_VERSION}" 244 | return $? 245 | fi 246 | } 247 | 248 | find_lua "$@" 249 | -------------------------------------------------------------------------------- /src/function-call.lua: -------------------------------------------------------------------------------- 1 | 2 | function direct_compute() 3 | local x = 0 4 | for i = 1, 30 do 5 | local r = 1 6 | for j = 1, i do 7 | r = r * j 8 | end 9 | x = x + r 10 | end 11 | return x 12 | end 13 | 14 | function fact(x) 15 | local result = 1 16 | for i = 1, x do 17 | result = result * i 18 | end 19 | return result 20 | end 21 | 22 | function call_function() 23 | local x = 0 24 | for i = 1, 30 do 25 | x = x + fact(i) 26 | end 27 | return x 28 | end 29 | 30 | return { 31 | name = "function-call", 32 | desc = "function call vs in-line code", 33 | cases = { 34 | { name = "Direct compute", entry = direct_compute }, 35 | { name = "Call function ", entry = call_function }, 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /src/function-localize.lua: -------------------------------------------------------------------------------- 1 | 2 | function nonlocal() 3 | local x = 0 4 | for i = 1, 100 do 5 | x = x + math.sin(i) 6 | end 7 | return x 8 | end 9 | 10 | local sin = math.sin 11 | function localized() 12 | local x = 0 13 | for i = 1, 100 do 14 | x = x + sin(i) 15 | end 16 | return x 17 | end 18 | 19 | return { 20 | name = "function-localize", 21 | desc = "calling local/global functions", 22 | cases = { 23 | { name = "Non-local", entry = nonlocal }, 24 | { name = "Localized", entry = localized }, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /src/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = { 3 | cases = {}, 4 | index = {}, 5 | all_names = {}, 6 | } 7 | 8 | function M.load(name) 9 | local m = require(name) 10 | local l = #M.cases 11 | 12 | if "table" ~= type(m) then 13 | error(string.format("Invalid data type returned from test '%s', a table is required.", name)) 14 | end 15 | 16 | if nil == m.name then 17 | error(string.format("Miss 'name' in test '%s'", name)) 18 | end 19 | 20 | if nil == m.desc then 21 | error(string.format("Miss 'desc' in test '%s'", name)) 22 | end 23 | 24 | if nil == m.cases then 25 | error(string.format("Miss 'desc' in test '%s'", name)) 26 | end 27 | 28 | M.cases[l + 1] = m 29 | M.all_names[l + 1] = m.name 30 | M.index[m.name] = m 31 | end 32 | 33 | function M.each() 34 | local n = 0 35 | local l = #M.cases 36 | return function() 37 | n = n + 1 38 | if n <= l then 39 | return n, M.cases[n] 40 | end 41 | end 42 | end 43 | 44 | function M.fetch(name) 45 | return M.index[name] 46 | end 47 | 48 | local load_list = { 49 | "function-call", 50 | "function-localize", 51 | "meta-function", 52 | "multi-conditions", 53 | "name-length", 54 | "set-default-value", 55 | "table-append", 56 | "tail-recursion", 57 | } 58 | 59 | for _, name in ipairs(load_list) do 60 | M.load(name) 61 | end 62 | 63 | return M 64 | -------------------------------------------------------------------------------- /src/meta-function.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local states = { 4 | "alabama", "alaska", "arizona", "arkansas", 5 | "california", "colorado", "connecticut", "delaware", 6 | "florida", "georgia", "hawaii", "idaho", 7 | "illinois", "indiana", "iowa", "kansas", 8 | "kentucky", "louisiana", "maine", "maryland", 9 | "massachusetts", "michigan", "minnesota", "mississippi", 10 | "missouri", "montana", "nebraska", "nevada", 11 | "new hampshire", "new jersey", "new mexico", "new york", 12 | "north carolina", "north dakota", "ohio", "oklahoa", 13 | "oregon", "pennsylvania", "rhode island", "south carolina", 14 | "south dakota", "tennessee", "texas", "utah", 15 | "vermont", "virginia", "washington", "west virginia", 16 | "wisconsin", "wyoming", 17 | } 18 | 19 | function to_upper(s) 20 | return string.upper(s) 21 | end 22 | 23 | local function to_upper_l(s) 24 | return string.upper(s) 25 | end 26 | 27 | function gloal_func() 28 | local str = "" 29 | for _, s in pairs(states) do 30 | str = str .. string.upper(s) 31 | end 32 | end 33 | 34 | function global_wrap() 35 | local str = "" 36 | for _, s in pairs(states) do 37 | str = str .. to_upper(s) 38 | end 39 | end 40 | 41 | function local_wrap() 42 | local str = "" 43 | for _, s in pairs(states) do 44 | str = str .. to_upper_l(s) 45 | end 46 | end 47 | 48 | function from_meta() 49 | local str = "" 50 | for _, s in pairs(states) do 51 | str = str .. s:upper() 52 | end 53 | end 54 | 55 | function localize() 56 | local str = "" 57 | local upper = string.upper 58 | for _, s in pairs(states) do 59 | str = str .. upper(s) 60 | end 61 | end 62 | 63 | return { 64 | name = "meta-function", 65 | desc = "implement with meta-function", 66 | cases = { 67 | { name = "Global function", entry = gloal_func }, 68 | { name = "Global wrap ", entry = global_wrap }, 69 | { name = "Local wrap ", entry = local_wrap }, 70 | { name = "From meta table", entry = from_meta }, 71 | { name = "Localize ", entry = localize }, 72 | }, 73 | } 74 | -------------------------------------------------------------------------------- /src/multi-conditions.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local states = { 4 | "alabama", "alaska", "arizona", "arkansas", 5 | "california", "colorado", "connecticut", "delaware", 6 | "florida", "georgia", "hawaii", "idaho", 7 | "illinois", "indiana", "iowa", "kansas", 8 | "kentucky", "louisiana", "maine", "maryland", 9 | "massachusetts", "michigan", "minnesota", "mississippi", 10 | "missouri", "montana", "nebraska", "nevada", 11 | "new hampshire", "new jersey", "new mexico", "new york", 12 | "north carolina", "north dakota", "ohio", "oklahoa", 13 | "oregon", "pennsylvania", "rhode island", "south carolina", 14 | "south dakota", "tennessee", "texas", "utah", 15 | "vermont", "virginia", "washington", "west virginia", 16 | "wisconsin", "wyoming", 17 | } 18 | 19 | function use_or() 20 | local x = 0 21 | for _, v in pairs(states) do 22 | if "alabama" == v or 23 | "california" == v or 24 | "missouri" == v or 25 | "virginia" == v or 26 | "wisconsin" == v then 27 | x = x + 1 28 | end 29 | end 30 | return x 31 | end 32 | 33 | function use_table() 34 | local x, vals = 0, {"alabama", "california", "missouri", "virginia", "wisconsin"} 35 | for _, v in pairs(states) do 36 | for _, s in pairs(vals) do 37 | if s == v then x = x + 1 end 38 | end 39 | end 40 | return x 41 | end 42 | 43 | return { 44 | name = "multi-conditions", 45 | desc = "judge with table and inline code", 46 | cases = { 47 | { name = "Use OR ", entry = use_or }, 48 | { name = "Use table", entry = use_table }, 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/name-length.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local states = { 4 | "alabama", "alaska", "arizona", "arkansas", 5 | "california", "colorado", "connecticut", "delaware", 6 | "florida", "georgia", "hawaii", "idaho", 7 | "illinois", "indiana", "iowa", "kansas", 8 | "kentucky", "louisiana", "maine", "maryland", 9 | "massachusetts", "michigan", "minnesota", "mississippi", 10 | "missouri", "montana", "nebraska", "nevada", 11 | "new hampshire", "new jersey", "new mexico", "new york", 12 | "north carolina", "north dakota", "ohio", "oklahoa", 13 | "oregon", "pennsylvania", "rhode island", "south carolina", 14 | "south dakota", "tennessee", "texas", "utah", 15 | "vermont", "virginia", "washington", "west virginia", 16 | "wisconsin", "wyoming", 17 | } 18 | 19 | a_list_of_all_names_of_united_states = "" 20 | all_name = "" 21 | an = "" 22 | 23 | function global_long() 24 | a_list_of_all_names_of_united_states = "" 25 | for _, v in pairs(states) do 26 | a_list_of_all_names_of_united_states = a_list_of_all_names_of_united_states .. v 27 | end 28 | 29 | return a_list_of_all_names_of_united_states 30 | end 31 | 32 | function global_medium() 33 | all_name = "" 34 | for _, v in pairs(states) do 35 | all_name = all_name .. v 36 | end 37 | 38 | return all_name 39 | end 40 | 41 | function global_short() 42 | an = "" 43 | for _, v in pairs(states) do 44 | an = an .. v 45 | end 46 | 47 | return an 48 | end 49 | 50 | function local_long() 51 | local a_list_of_all_names_of_united_states = "" 52 | for _, v in pairs(states) do 53 | a_list_of_all_names_of_united_states = a_list_of_all_names_of_united_states .. v 54 | end 55 | 56 | return a_list_of_all_names_of_united_states 57 | end 58 | 59 | function local_medium() 60 | local all_name = "" 61 | for _, v in pairs(states) do 62 | all_name = all_name .. v 63 | end 64 | 65 | return all_name 66 | end 67 | 68 | function local_short() 69 | local an = "" 70 | for _, v in pairs(states) do 71 | an = an .. v 72 | end 73 | 74 | return an 75 | end 76 | 77 | return { 78 | name = "name-length", 79 | desc = "length of variable name", 80 | cases = { 81 | { name = "Global long ", entry = global_long }, 82 | { name = "Global medium ", entry = global_medium }, 83 | { name = "Global short ", entry = global_short }, 84 | { name = "Local long ", entry = local_long }, 85 | { name = "Local medium ", entry = local_medium }, 86 | { name = "Local short ", entry = local_short }, 87 | }, 88 | } 89 | -------------------------------------------------------------------------------- /src/set-default-value.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | function add_or(a, b) 4 | local c = b or 1 5 | return a + c 6 | end 7 | 8 | function add_if(a, b) 9 | local c = b 10 | if c == nil then 11 | c = 1 12 | end 13 | 14 | return a + c 15 | end 16 | 17 | function test_or() 18 | local sum = 0 19 | for i = 1, 50 do 20 | sum = sum + add_or(i) 21 | end 22 | return sum 23 | end 24 | 25 | function test_if() 26 | local sum = 0 27 | for i = 1, 50 do 28 | sum = sum + add_or(i) 29 | end 30 | return sum 31 | end 32 | 33 | return { 34 | name = "set-default-value", 35 | desc = "set default value with 'if' or 'or'", 36 | cases = { 37 | { name = "Use if ", entry = test_if }, 38 | { name = "Use or ", entry = test_or }, 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /src/table-append.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | function table_insert() 4 | local t = {} 5 | for i = 1, 1000, 2 do 6 | table.insert(t, i) 7 | end 8 | return t 9 | end 10 | 11 | function table_insertL() 12 | local t, insert = {}, table.insert 13 | for i = 1, 1000, 2 do 14 | insert(t, i) 15 | end 16 | return t 17 | end 18 | 19 | function use_counter() 20 | local t, c = {}, 1 21 | for i = 1, 1000, 2 do 22 | t[c], c = i, c + 1 23 | end 24 | return t 25 | end 26 | 27 | function use_length() 28 | local t = {} 29 | for i = 1, 1000, 2 do 30 | t[#t + 1] = i 31 | end 32 | end 33 | 34 | return { 35 | name = "table-append", 36 | desc = "append item to a table", 37 | cases = { 38 | { name = "table.insert G", entry = table_insert }, 39 | { name = "table.insert L", entry = table_insertL }, 40 | { name = "use counter ", entry = use_counter }, 41 | { name = "use length ", entry = use_length }, 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/tail-recursion.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | function fact_normal(x) 4 | local result = 1 5 | for i = 2, x do 6 | result = result * i 7 | end 8 | return result 9 | end 10 | 11 | function fact_recurse(x) 12 | if x <= 1 then return 1 end 13 | return x * fact_recurse(x - 1) 14 | end 15 | 16 | function fact_tail(x, r) 17 | local result = r or 1 18 | if x <= 1 then return result end 19 | return fact_tail(x - 1, result * x) 20 | end 21 | 22 | function normal_recurse() 23 | local sum = 0 24 | for i = 1, 50 do 25 | sum = sum + fact_recurse(i) 26 | end 27 | return sum 28 | end 29 | 30 | function normal_loop() 31 | local sum = 0 32 | for i = 1, 50 do 33 | sum = sum + fact_normal(i) 34 | end 35 | return sum 36 | end 37 | 38 | function tail_recurse() 39 | local sum = 0 40 | for i = 1, 50 do 41 | sum = sum + fact_tail(i) 42 | end 43 | return sum 44 | end 45 | 46 | return { 47 | name = "tail-recursion", 48 | desc = "tail recursion or normal loop", 49 | cases = { 50 | { name = "Normal recurse", entry = normal_recurse }, 51 | { name = "Normal loop ", entry = normal_loop }, 52 | { name = "Tail recurse ", entry = tail_recurse }, 53 | }, 54 | } 55 | 56 | --------------------------------------------------------------------------------