前段时间想把一些常用的 Rust 库绑定到 Lua 上,以便在 Lua 里复用 Rust 的各种库。
相比 Python,这样做的好处很近似 Go,成品只是一个可执行文件 + Lua 脚本,不需要解决环境的各种依赖安装,并且生成的 binary 很小;而相比 Go,这样又近似 Python,打开个记事本写完就跑。
于是我着手开始绑定,尽管我第一步就是将一些模板代码写成宏,尽量降低重复劳动,但实际要写的代码还是太多了,自虐了几天开始寻找其他解决方案,看了一些由 Rust 实现的脚本语言,选择了 Rhai 作为备选方案。
将 Rust 里的类型传递给 Rhai 很方便,可以少写很多代码,但是 Rhai 只是相当于一个语法翻译器,Github 上主要贡献者也谈到了性能大概在 Python 的水平,只适合做插件,所以我做了个简单的性能测试。
看了很多易与 Rust 集成的脚本语言,作为资料,列举我排除的理由。我需要的是一个小型的、易用的、可嵌入的语言。
测试很简单,计算斐波那契数列,测试了 5 种情况。
由于 Lua 速度太快 😂,而 Lua 自带的时间戳只能精确到毫秒,所以我的测试方式是笔记本在完全节能模式下跑 3 次,每次循环 5 次 fibonacci(32)
(代码里函数命名简写为 fib
)。
fib_rs
命名以区分。
fn fib_rs(n: i64) -> i64 {
if n < 2 { return n; }
fib_rs(n - 2) + fib_rs(n - 1)
}
function fib(n)
if n < 2 then return n end
return fib(n - 2) + fib(n - 1)
end
local start = os.clock()
for i = 1, 5 do
fib(32)
end
io.write(string.format("elapsed: %f\n", os.clock() - start))
local start2 = os.clock()
for i = 1, 5 do
fib_rs(32)
end
io.write(string.format("elapsed: %f\n", os.clock() - start2))
fn fib(n) {
if n < 2 { return n; }
fib(n - 2) + fib(n - 1)
}
let now = timestamp();
for i in range(0, 5) {
fib(32);
}
print(now.elapsed());
let now2 = timestamp();
for i in range(0, 5) {
fib_rs(32);
}
print(now2.elapsed());
from __future__ import print_function
import time
def fib(n):
if n < 2: return n
return fib(n - 1) + fib(n - 2)
start = time.process_time()
for i in range(0, 5):
fib(32)
print("elapsed: " + str(time.process_time() - start))
# 尾递归版,仅几十毫秒。
import time
def fib(n, acc1, acc2):
if n < 1: return acc1
return fib(n - 1, acc1 + acc2, acc1)
def loop_fib(n):
for i in range(0, n):
fib(90, 0, 1)
start = time.process_time()
loop_fib(2000)
print("elapsed: " + str(time.process_time() - start))
elapsed: 1.081000
elapsed: 0.038000
elapsed: 1.085000
elapsed: 0.040000
elapsed: 1.203000
elapsed: 0.047000
elapsed: 6.34375
elapsed: 5.0
elapsed: 4.984375
79.4438942
0.1073184
111.5409529
0.1158327
46.1241106
0.0424334
Rhai 还是只适合作为插件系统调用 Rust 实现的 API,当成一个解释器用还是不太行。尽管绑定 Lua 代码超多,但似乎别无选择。