强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Lua 从入门到精通 / 16 - 性能优化 / Performance

性能优化 / Performance

Lua 本身已经很快,但不当的使用方式会导致性能问题。掌握这些技巧,让你的 Lua 代码飞起来。

Lua is fast by nature, but misuse causes slowdowns. Master these techniques to make your Lua code fly.


🟢 基础 / Basics — 常见优化

1. 局部变量 vs 全局变量

-- 全局变量访问很慢(哈希表查找)
-- 局部变量访问很快(寄存器/栈操作)

-- 慢
for i = 1, 1000000 do
    math.floor(i / 3)    -- 每次都要查找 _G["math"] -> math["floor"]
end

-- 快
local floor = math.floor
for i = 1, 1000000 do
    floor(i / 3)    -- 直接访问局部变量
end

-- 经验法则:在循环内频繁使用的全局函数,缓存为局部变量
local insert = table.insert
local concat = table.concat
local format = string.format

2. 避免不必要的表创建

-- 慢:每次循环都创建新表
local function slow()
    local results = {}
    for i = 1, 100000 do
        results[#results + 1] = {x = i, y = i * 2}
    end
    return results
end

-- 快:重用表
local function fast()
    local results = {}
    local tmp = {}
    for i = 1, 100000 do
        tmp.x = i
        tmp.y = i * 2
        results[#results + 1] = tmp
        tmp = {}    -- 只在需要时创建
    end
    return results
end

-- 更快:预分配数组大小
local function fastest(n)
    local results = {}
    for i = 1, n do
        results[i] = i * 2
    end
    return results
end

3. 字符串连接优化

-- 极慢:O(n^2)
local s = ""
for i = 1, 10000 do
    s = s .. "x"
end

-- 快:O(n)
local parts = {}
for i = 1, 10000 do
    parts[i] = "x"
end
local s = table.concat(parts)

-- 也可以指定分隔符
local s = table.concat(parts, ",")

4. ipairs vs pairs vs for i=1,#t

local t = {10, 20, 30, 40, 50}

-- ipairs:使用迭代器,稍慢
for i, v in ipairs(t) do end

-- 数字 for:最快(直接索引)
for i = 1, #t do
    local v = t[i]
end

-- pairs:哈希遍历,无序,最慢(对于数组部分)
for k, v in pairs(t) do end

🟡 进阶 / Intermediate

1. 表的高效使用 / Efficient Table Usage

-- table.insert vs 直接赋值
-- table.insert 有额外的函数调用开销
local t = {}
for i = 1, 100000 do
    t[#t + 1] = i        -- 比 table.insert(t, i) 快
    -- 或 t[i] = i        -- 如果 i 是连续的
end

-- table.remove 的开销(需要移动元素)
-- 删除数组末尾元素:O(1)
-- 删除数组中间元素:O(n)
table.remove(t)           -- 删除最后一个,O(1)
table.remove(t, 5)        -- 删除第5个,O(n)

-- 如果需要频繁删除中间元素,考虑用链表或标记删除

-- table.sort 比手写排序快(C 实现)
table.sort(t, function(a, b) return a > b end)

2. 避免过度使用闭包

-- 每次调用都创建新闭包(有开销)
local function makeFilter(threshold)
    return function(x) return x > threshold end
end

-- 如果在同一循环中频繁创建,考虑缓存
local filters = {}
local function getFilter(threshold)
    if not filters[threshold] then
        filters[threshold] = function(x) return x > threshold end
    end
    return filters[threshold]
end

3. 使用位运算代替取模 / Bitwise vs Modulo

-- Lua 5.3+ 位运算
-- x % 2 == 0 可以用 x & 1 == 0 替代(对 2 的幂)
-- x % 4 == 0 可以用 x & 3 == 0 替代

local function isEven(n)
    return n & 1 == 0    -- 比 n % 2 == 0 快
end

-- 位移代替乘除
local x = 1 << 4    -- x = 16 (比 1 * 16 快)
local y = 100 >> 2  -- y = 25  (比 100 / 4 快)

🔴 高级 / Advanced — LuaJIT

1. LuaJIT 简介 / Introduction to LuaJIT

LuaJIT 是 Mike Pall 开发的 Lua 5.1 兼容的 JIT 编译器
LuaJIT is a JIT compiler compatible with Lua 5.1

┌───────────────────────────────────────────┐
│              LuaJIT 架构                   │
├───────────────────────────────────────────┤
│  Lua 源码                                  │
│     │                                      │
│     ▼                                      │
│  Bytecode Compiler(字节码编译器)          │
│     │                                      │
│     ▼                                      │
│  Interpreter(解释器)                     │
│     │                                      │
│     ├── Hot loop detected?                │
│     │      │                              │
│     │      ▼                              │
│     │   Trace Compiler(追踪编译器)        │
│     │      │                              │
│     │      ▼                              │
│     │   Machine Code(机器码)             │
│     │                                      │
│     ▼                                      │
│  Native Performance(原生性能)            │
└───────────────────────────────────────────┘

性能对比 / Performance comparison:
- 纯 Lua 解释器: 1x
- LuaJIT 解释器: ~3-5x
- LuaJIT JIT 编译: ~30-100x(接近 C 性能)

2. FFI 库 — 直接调用 C / FFI Library

-- LuaJIT 的 FFI 允许直接调用 C 函数和操作 C 数据结构
-- 比 Lua C API 快得多(零开销调用)

local ffi = require("ffi")

-- 声明 C 函数
ffi.cdef[[
    int printf(const char *fmt, ...);
    double sqrt(double x);
    void *malloc(size_t size);
    void free(void *ptr);
]]

-- 调用 C 函数
ffi.C.printf("Hello from C! %d\n", 42)
local result = ffi.C.sqrt(16.0)
print(result)    -- 4.0

-- 创建 C 类型
local arr = ffi.new("double[10]")
for i = 0, 9 do
    arr[i] = i * 1.5
end
print(arr[5])    -- 7.5

-- FFI vs Lua C API 性能:
-- FFI 调用 C 函数: ~纳秒级
-- Lua C API 调用: ~微秒级(栈操作开销)

3. Trace 编译原理 / Trace Compilation

-- LuaJIT 通过"追踪"执行路径来编译热代码

-- 1. 解释器正常执行字节码
-- 2. 检测到"热循环"(执行次数超过阈值)
-- 3. 开始录制 trace(记录执行路径)
-- 4. 如果 trace 是线性的(没有分叉),直接编译为机器码
-- 5. 如果 trace 分叉,可能需要"guard"(检查点)

-- 有利于 trace 编译的代码:
local sum = 0
for i = 1, 1000000 do
    sum = sum + i    -- 简单循环,容易被 JIT
end

-- 不利于 trace 编译的代码:
for i = 1, 1000000 do
    if type(x) == "number" then    -- 类型不稳定会打断 trace
        -- ...
    end
end

-- 类型稳定性非常重要!
-- LuaJIT 为每种类型生成专门的机器码
-- 类型变化会导致 trace 失效,回退到解释器

4. LuaJIT vs Lua 5.4 兼容性

-- LuaJIT 基于 Lua 5.1,缺少 5.2-5.4 的特性
-- 需要注意的差异:

-- Lua 5.2+:
-- - goto 语句(LuaJIT 支持)
-- - 无 __len 对 table(LuaJIT 支持)
-- - bit32 库(LuaJIT 有 bit 库)

-- Lua 5.3+:
-- - 整数/浮点数分离(LuaJIT 无,全用 double)
-- - // 整除(LuaJIT 无)
-- - 位运算(LuaJIT 用 bit 库)
-- - utf8 库(LuaJIT 无)

-- Lua 5.4:
-- - 分代 GC(LuaJIT 无)
-- - to-be-closed 变量(LuaJIT 无)
-- - 常量变量(LuaJIT 无)

-- 选择建议:
-- - 需要极致性能 → LuaJIT
-- - 需要最新语言特性 → Lua 5.4
-- - 嵌入式环境 → Lua 5.4(更小)
-- - OpenResty / Kong → LuaJIT(强制)

小结 / Summary

层级你需要知道的 / What You Need to Know
🟢 基础局部变量缓存全局函数、table.concat 替代 ..、避免不必要创建
🟡 进阶数组直接索引 vs ipairs、table.insert vs 直接赋值、位运算
🔴 高级LuaJIT 架构、FFI 零开销调用 C、trace 编译、类型稳定性

下一章:C API / Lua C API