|
1 | | -# LuaRS 性能报告 |
| 1 | +# Lua-RS 性能报告 |
2 | 2 |
|
3 | | -## 概述 |
4 | | - |
5 | | -本报告比较 luars(Rust 实现)与官方 Lua 5.4.6 的性能表现。 |
6 | | - |
7 | | -**测试环境:** |
8 | | -- OS: Windows |
9 | | -- Lua 5.4.6: 从源码编译 (CMake + MSVC) |
10 | | -- luars: `cargo build --release` |
11 | | - |
12 | | -**总体结果:** |
13 | | -| 运行时 | 总时间 | 相对性能 | |
14 | | -|--------|--------|----------| |
15 | | -| Lua 5.4.6 | ~10.1 秒 | 100% | |
16 | | -| luars | ~15.3 秒 | 66% | |
17 | | - |
18 | | -luars 在基准测试套件中达到 Lua 5.4 约 **66%** 的性能。 |
| 3 | +**测试日期**: 2024-12-08 |
| 4 | +**对比版本**: Lua-RS (Rust 实现) vs Lua 5.4 (原生 C) |
| 5 | +**平台**: Windows x64, Release 模式 |
19 | 6 |
|
20 | 7 | --- |
21 | 8 |
|
22 | | -## 详细对比 |
23 | | - |
24 | | -### 算术运算 |
25 | | - |
26 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
27 | | -|--------|-------|---------|------| |
28 | | -| 整数加法 | 139 M/s | 233 M/s | 60% | |
29 | | -| 浮点乘法 | 127 M/s | 208 M/s | 61% | |
30 | | -| 混合运算 | 74 M/s | 123 M/s | 60% | |
31 | | - |
32 | | -### 控制流 |
33 | | - |
34 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
35 | | -|--------|-------|---------|------| |
36 | | -| If-else | 56 M/s | 52 M/s | 108% ✓ | |
37 | | -| While 循环 | 80 M/s | 118 M/s | 68% | |
38 | | -| Repeat-until | 83 M/s | 147 M/s | 56% | |
39 | | -| 嵌套循环 | 145 M/s | 250 M/s | 58% | |
40 | | - |
41 | | -### 变量访问 |
42 | | - |
43 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
44 | | -|--------|-------|---------|------| |
45 | | -| 全局变量 | 32 M/s | 74 M/s | 43% | |
46 | | -| 局部变量 | 121 M/s | 233 M/s | 52% | |
47 | | -| Upvalue | 53 M/s | 133 M/s | 40% | |
48 | | - |
49 | | -### 函数调用 |
50 | | - |
51 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
52 | | -|--------|-------|---------|------| |
53 | | -| 简单调用 | 19 M/s | 53 M/s | 36% | |
54 | | -| 变长参数 | 1.7 M/s | 2.5 M/s | 68% | |
55 | | - |
56 | | -### 闭包 |
57 | | - |
58 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
59 | | -|--------|-------|---------|------| |
60 | | -| 闭包创建 | 11.6 M/s | 7.1 M/s | 163% ✓ | |
61 | | -| Upvalue 读写 | 18.5 M/s | 43.5 M/s | 43% | |
62 | | - |
63 | | -### 表操作 |
64 | | - |
65 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
66 | | -|--------|-------|---------|------| |
67 | | -| 表插入 | 43 M/s | 34 M/s | 126% ✓ | |
68 | | -| 表访问 | 84 M/s | 125 M/s | 67% | |
69 | | - |
70 | | -### 元表 (fasttm 优化后) |
71 | | - |
72 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
73 | | -|--------|-------|---------|------| |
74 | | -| __index (函数) | 4.9 M/s | 22 M/s | 22% | |
75 | | -| __index (表) | 23 M/s | 26 M/s | 88% ✓ | |
76 | | -| __newindex | 6.1 M/s | 17 M/s | 36% | |
77 | | -| __call | 15.7 M/s | 25 M/s | 63% | |
78 | | -| __len | 5.3 M/s | 28 M/s | 19% | |
79 | | -| rawget | 21 M/s | 21 M/s | 100% ✓ | |
80 | | - |
81 | | -### fasttm 优化效果 |
82 | | - |
83 | | -当表有元表但**没有特定元方法**时的访问性能: |
84 | | - |
85 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
86 | | -|--------|-------|---------|------| |
87 | | -| 缺失键 (有元表, 无__index) | 100-113 M/s | 100 M/s | 100-113% ✓ | |
88 | | -| #table (有元表, 无__len) | 83-105 M/s | 100 M/s | 83-105% ✓ | |
89 | | - |
90 | | -### 协程 |
91 | | - |
92 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
93 | | -|--------|-------|---------|------| |
94 | | -| 创建/恢复/让出 | 319 K/s | 521 K/s | 61% | |
95 | | -| 重复 yield | 2.7 M/s | 735 K/s | 367% ✓ | |
96 | | -| coroutine.wrap | 405 K/s | 2.5 M/s | 16% | |
97 | | - |
98 | | -### 错误处理 |
99 | | - |
100 | | -| 测试项 | luars | Lua 5.4 | 比率 | |
101 | | -|--------|-------|---------|------| |
102 | | -| pcall (成功) | 3.2 M/s | 17 M/s | 19% | |
103 | | -| pcall (失败) | 3.4 M/s | 493 K/s | 690% ✓ | |
104 | | -| xpcall (失败) | 1.2 M/s | 459 K/s | 261% ✓ | |
| 9 | +## 📊 性能概览 |
| 10 | + |
| 11 | +### 🟢 优势领域 (Lua-RS 更快) |
| 12 | + |
| 13 | +| 测试项 | Lua-RS | 原生 Lua | 优势比 | |
| 14 | +|--------|--------|----------|--------| |
| 15 | +| Closure 创建 | 11.92 M/s | 7.46 M/s | **+60%** | |
| 16 | +| table.insert (end) | 20.5 K/s | 12.5 K/s | **+64%** | |
| 17 | +| table.insert (middle) | 11.2 K/s | 3.3 K/s | **+239%** | |
| 18 | +| table.remove (end) | 25.8 K/s | 8.3 K/s | **+211%** | |
| 19 | +| table.concat | 12.2 K/s | 39.5 K/s | -67% (误差?需复查) | |
| 20 | +| table.sort (sorted) | 195.6 K/s | 6.4 K/s | **+30x** | |
| 21 | +| table.sort (reversed) | 47.1 K/s | 4.2 K/s | **+11x** | |
| 22 | +| table.sort (random) | 7.9 K/s | 3.5 K/s | **+2.2x** | |
| 23 | +| table.sort (custom cmp) | 3.1 K/s | 1.9 K/s | **+63%** | |
| 24 | +| Hash table 插入 (100k) | 0.037s | 0.066s | **+78%** | |
| 25 | +| pcall (error) | 4.62 M/s | 0.51 M/s | **+9x** | |
| 26 | +| xpcall (error) | 1.22 M/s | 0.43 M/s | **+2.8x** | |
| 27 | +| coroutine yield | 2.07 M/s | 0.67 M/s | **+3.1x** | |
| 28 | +| producer-consumer | 1.83 M/s | 0.63 M/s | **+2.9x** | |
| 29 | +| # operator (length) | 168.5 M/s | 100 M/s | **+68%** | |
| 30 | +| Integer division (//) | 138.8 M/s | 78.1 M/s | **+78%** | |
| 31 | +| Integer mul/add/mod | 76.6 M/s | 58.8 M/s | **+30%** | |
| 32 | +| If-else 分支 | 65.8 M/s | 55.9 M/s | **+18%** | |
| 33 | +| string.upper/lower | 6.3 K/s | 4.5 K/s | **+40%** | |
| 34 | +| string.gsub | 0.137s | 0.203s | **+48%** | |
| 35 | +| string.format (complex) | 725 K/s | 1389 K/s | -48% | |
| 36 | + |
| 37 | +### 🔴 需要优化的领域 (Lua-RS 较慢) |
| 38 | + |
| 39 | +| 测试项 | Lua-RS | 原生 Lua | 差距 | 优先级 | |
| 40 | +|--------|--------|----------|------|--------| |
| 41 | +| **ipairs 迭代** | 14.7 K/s | 33.0 K/s | **-55%** | 🔥 高 | |
| 42 | +| **pairs 迭代** | 13.2 K/s | 32.0 K/s | **-59%** | 🔥 高 | |
| 43 | +| **next() 迭代** | 10.4 K/s | 20.8 K/s | **-50%** | 🔥 高 | |
| 44 | +| **Upvalue 读写** | 22.9 M/s | 43.5 M/s | **-47%** | 🔥 高 | |
| 45 | +| **Method call (colon)** | 3.6 M/s | 7.2 M/s | **-50%** | 🔥 高 | |
| 46 | +| **Method call (dot)** | 3.9 M/s | 8.9 M/s | **-56%** | 🔥 高 | |
| 47 | +| Inherited method call | 3.4 M/s | 9.4 M/s | **-64%** | 🔥 高 | |
| 48 | +| Prototype chain (3级) | 8.7 M/s | 17.9 M/s | **-51%** | 高 | |
| 49 | +| Global var access | 42.0 M/s | 71.9 M/s | **-42%** | 中 | |
| 50 | +| Local table field | 45.5 M/s | 74.1 M/s | **-39%** | 中 | |
| 51 | +| Vararg function | 1.33 M/s | 2.11 M/s | **-37%** | 中 | |
| 52 | +| select('#'/n, ...) | 4.2 M/s | 14.5 M/s | **-71%** | 中 | |
| 53 | +| Vararg passthrough | 14.6 M/s | 25.6 M/s | **-43%** | 中 | |
| 54 | +| coroutine.wrap | 0.64 M/s | 1.67 M/s | **-62%** | 中 | |
| 55 | +| While/Repeat 循环 | 93-97 M/s | 123-143 M/s | **-30%** | 低 | |
| 56 | +| Bitwise 操作 | 67.8 M/s | 102 M/s | **-34%** | 低 | |
| 57 | +| Power (^2) | 20.5 M/s | 53.8 M/s | **-62%** | 低 | |
| 58 | +| Float mul/add/div | 64.3 M/s | 102 M/s | **-37%** | 低 | |
| 59 | +| string.match | 0.9 M/s | 8.3 M/s | **-89%** | 中 | |
| 60 | +| string.char | 4.8 M/s | 12.5 M/s | **-62%** | 低 | |
| 61 | +| Concat (4 parts) | 8.5 M/s | 20.0 M/s | **-58%** | 中 | |
105 | 62 |
|
106 | 63 | --- |
107 | 64 |
|
108 | | -## 优化实现 |
109 | | - |
110 | | -### fasttm 优化 (Lua 5.4 风格) |
111 | | - |
112 | | -参考 Lua 5.4 的 `ltm.h` 中的 `fasttm` 宏,实现了元方法缺失的快速检测: |
113 | | - |
114 | | -```rust |
115 | | -// lua_table.rs |
116 | | -pub mod TmFlags { |
117 | | - pub const TM_INDEX: u8 = 1 << 0; |
118 | | - pub const TM_NEWINDEX: u8 = 1 << 1; |
119 | | - pub const TM_GC: u8 = 1 << 2; |
120 | | - pub const TM_MODE: u8 = 1 << 3; |
121 | | - pub const TM_LEN: u8 = 1 << 4; |
122 | | - pub const TM_EQ: u8 = 1 << 5; |
123 | | - pub const TM_CALL: u8 = 1 << 6; |
124 | | -} |
125 | | - |
126 | | -pub struct LuaTable { |
127 | | - pub tm_flags: u8, // 缓存元方法不存在的位标志 |
128 | | - // ... |
129 | | -} |
130 | | - |
131 | | -impl LuaTable { |
132 | | - /// 检查元方法是否已知不存在 |
133 | | - pub fn tm_absent(&self, flag: u8) -> bool { |
134 | | - (self.tm_flags & flag) != 0 |
135 | | - } |
136 | | - |
137 | | - /// 标记元方法不存在 |
138 | | - pub fn set_tm_absent(&mut self, flag: u8) { |
139 | | - self.tm_flags |= flag; |
140 | | - } |
141 | | -} |
142 | | -``` |
143 | | - |
144 | | -**工作原理:** |
145 | | -1. 每个表的元表有一个 `tm_flags` 位域 |
146 | | -2. 当查找元方法(如 `__index`)失败时,设置对应位 |
147 | | -3. 下次访问时先检查位,如果已设置则跳过哈希查找 |
148 | | -4. 当元表被修改时,清除所有标志 |
149 | | - |
150 | | -**效果:** |
151 | | -- "缺失键 (有元表, 无__index)" 场景:从 55 M/s 提升到 100+ M/s |
152 | | -- 达到 Lua 5.4 同等水平 |
| 65 | +## 🎯 优化建议 (按优先级排序) |
| 66 | + |
| 67 | +### 🔥 P0: 最高优先级 (核心热点) |
| 68 | + |
| 69 | +#### 1. **迭代器性能 (ipairs/pairs/next)** |
| 70 | +- **问题**: 比原生 Lua 慢 50-60% |
| 71 | +- **影响**: 迭代是 Lua 最常用的操作之一 |
| 72 | +- **建议**: |
| 73 | + - 优化 `TFORLOOP` / `TFORCALL` 指令实现 |
| 74 | + - 减少每次迭代的边界检查 |
| 75 | + - 考虑特化 ipairs/pairs 的快速路径 |
| 76 | + - 内联 next() 函数调用 |
| 77 | + |
| 78 | +#### 2. **Upvalue 访问** |
| 79 | +- **问题**: 比原生 Lua 慢 47% |
| 80 | +- **影响**: 闭包内访问外部变量是常见模式 |
| 81 | +- **建议**: |
| 82 | + - 优化 `GETUPVAL` / `SETUPVAL` 指令 |
| 83 | + - 减少 upvalue 间接访问层次 |
| 84 | + - 考虑 upvalue 的内联缓存 |
| 85 | + |
| 86 | +#### 3. **方法调用 (OOP 模式)** |
| 87 | +- **问题**: 方法调用慢 50-64% |
| 88 | +- **影响**: Lua 的 OOP 依赖元表和 __index |
| 89 | +- **建议**: |
| 90 | + - 优化 `SELF` 指令 |
| 91 | + - 缓存 __index 元方法查找 |
| 92 | + - 考虑内联缓存 (Inline Cache) 机制 |
| 93 | + |
| 94 | +### 🔶 P1: 高优先级 |
| 95 | + |
| 96 | +#### 4. **全局变量访问** |
| 97 | +- **问题**: 比原生 Lua 慢 42% |
| 98 | +- **建议**: |
| 99 | + - 优化 `GETTABUP` / `SETTABUP` 指令 |
| 100 | + - 考虑 _ENV 表的特殊优化路径 |
| 101 | + |
| 102 | +#### 5. **Vararg 处理** |
| 103 | +- **问题**: vararg 函数和 select() 慢 37-71% |
| 104 | +- **建议**: |
| 105 | + - 优化 `VARARG` 指令 |
| 106 | + - 减少 vararg 参数的复制开销 |
| 107 | + - select() 可以特化处理 |
| 108 | + |
| 109 | +#### 6. **字符串模式匹配** |
| 110 | +- **问题**: string.match 慢 89% |
| 111 | +- **建议**: |
| 112 | + - 优化 pattern matching 引擎 |
| 113 | + - 考虑 JIT 编译简单模式 |
| 114 | + |
| 115 | +### 🔷 P2: 中等优先级 |
| 116 | + |
| 117 | +#### 7. **循环指令** |
| 118 | +- While/Repeat 循环慢 30% |
| 119 | +- 优化 `LOOP` 相关指令 |
| 120 | + |
| 121 | +#### 8. **位运算** |
| 122 | +- 比原生慢 34% |
| 123 | +- 检查位运算指令实现 |
| 124 | + |
| 125 | +#### 9. **浮点运算** |
| 126 | +- 比原生慢 37% |
| 127 | +- 检查是否有不必要的类型检查 |
153 | 128 |
|
154 | 129 | --- |
155 | 130 |
|
156 | | -## 性能优势领域 |
| 131 | +## ✅ 已优化的领域 |
157 | 132 |
|
158 | | -luars 在以下方面**超越** Lua 5.4: |
| 133 | +以下领域 Lua-RS 表现优秀,无需额外优化: |
159 | 134 |
|
160 | | -1. **闭包创建**: 163% (11.6 M/s vs 7.1 M/s) |
161 | | -2. **表插入**: 126% (43 M/s vs 34 M/s) |
162 | | -3. **重复 yield**: 367% (2.7 M/s vs 735 K/s) |
163 | | -4. **pcall 失败**: 690% (luars 的错误处理更快) |
164 | | -5. **If-else 分支**: 108% |
165 | | -6. **fasttm 优化场景**: 100-113% |
| 135 | +1. **错误处理** - pcall/xpcall 在处理错误时比原生快 3-9 倍 |
| 136 | +2. **协程 yield** - yield/resume 性能优异 |
| 137 | +3. **表操作** - insert/remove/sort 等表库函数性能出色 |
| 138 | +4. **闭包创建** - 创建闭包比原生快 60% |
| 139 | +5. **整数运算** - 整数除法和混合运算较快 |
| 140 | +6. **哈希表插入** - 比原生快 78% |
166 | 141 |
|
167 | 142 | --- |
168 | 143 |
|
169 | | -## 需要改进的领域 |
| 144 | +## 📈 下一步行动 |
170 | 145 |
|
171 | | -1. **函数调用开销**: 36% - 需要优化调用栈管理 |
172 | | -2. **全局变量访问**: 43% - `_ENV` 查找较慢 |
173 | | -3. **元方法调用**: 19-36% - 元方法调用本身的开销较大 |
174 | | -4. **pcall 成功路径**: 19% - 每次 pcall 有较大开销 |
175 | | -5. **coroutine.wrap**: 16% - 包装函数创建较慢 |
| 146 | +1. **Profiling**: 使用 flamegraph 或 perf 分析 ipairs 热点 |
| 147 | +2. **迭代器重构**: 实现 TFORLOOP 的快速路径 |
| 148 | +3. **内联缓存**: 为方法调用实现简单的 IC 机制 |
| 149 | +4. **Upvalue 优化**: 减少间接访问层次 |
| 150 | +5. **Benchmark 细化**: 添加更细粒度的微基准测试 |
176 | 151 |
|
177 | 152 | --- |
178 | 153 |
|
179 | | -## 结论 |
| 154 | +## 📝 测试方法 |
180 | 155 |
|
181 | | -luars 作为 Rust 实现的 Lua 解释器,在整体性能上达到官方 Lua 5.4 的 66%。部分操作(闭包创建、表插入、协程 yield、错误处理)已经超越 Lua 5.4。 |
| 156 | +```bash |
| 157 | +# 编译 release 版本 |
| 158 | +cargo build --release |
182 | 159 |
|
183 | | -实现的 fasttm 优化有效解决了"有元表但无元方法"场景的性能问题,使该场景达到 Lua 5.4 水平。 |
| 160 | +# 运行性能测试 |
| 161 | +./run_benchmarks.ps1 |
| 162 | +``` |
184 | 163 |
|
185 | | -未来优化方向: |
186 | | -1. 优化函数调用栈管理 |
187 | | -2. 改进全局变量 `_ENV` 查找路径 |
188 | | -3. 优化元方法调用本身的开销 |
189 | | -4. 改进 pcall 成功路径的性能 |
| 164 | +各测试文件位于 `benchmarks/` 目录下。 |
0 commit comments