https://blog.yasking.org/a/rust-create-dll-using-by-python-nodejs.html
在处理一些计算密集型,或者系统交互较多的时候,使用编译后的程序,动态库效率会高不少,而且把相应功能封装成动态库可以便于复用,隐藏脚本语言的实现细节
一般制作DLL都是用C/C++等语言来写,不过现在又多了一种选择——Rust
创建项目:
cargo new toolib --lib
修改lib.rs文件,计算斐波那契数列:
fn fib(n: i32) -> i32 {
match n {
0 => 0,
1 => 1,
_ => fib(n - 1) + fib(n - 2),
}
}
#[no_mangle]
pub extern fn fibonacci(n: i32) -> i32 {
let r: i32 = fib(n);
return r
}
pub关键字说明可以在模块外部调用该函数,extern让这个函数符合 C 调用函数的约束,另外,rust在编译时会改变函数的名称,为了让外部能够访问到这个函数,所以使用#[no_mangle]属性使编译器不修改函数的名称
在Cargo.toml 中添加一个属性:
[lib]
name = "orz"
crate-type = ["cdylib"]
编译DLL
cargo build --release
使用 Python 调用:
import time
from ctypes import cdll
lib = cdll.LoadLibrary("target/release/toolib.dll")
t1 = time.time()
result = lib.fibonacci(35)
t2 = time.time()
print("====== Rust ======")
print("use time: " + str(round(t2 - t1, 2)) + "s")
print("result is: " + str(result))
def F(n):
if n == 0: return 0
elif n == 1: return 1
else: return F(n-1)+F(n-2)
t1 = time.time()
result = F(35)
t2 = time.time()
print("====== Python ======")
print("use time: " + str(round(t2 - t1, 2)) + "s")
print("result is: " + str(result))
运行:
λ python load.py
====== Rust ======
use time: 0.08s
result is: 9227465
====== Python ======
use time: 6.59s
result is: 9227465
λ node load.js
====== Rust ======
use time: 0.07s
result is: 9227465
done!
====== Node.js ======
use time: 0.18s
result is: 9227465
等等,Node.js 怎么比 Python 快这么多,这有点不科学,搜索后知道是 JIT compiler 的原因,Node.js运行时发现重复循环的代码块,会把它编译成汇编代码,Python默认解释器老老实实的运行代码,所以有很大的速度差异
换用Pypy编译试试:
====== Rust ======
use time: 0.09s
result is: 9227465
====== Python ======
use time: 0.29s
result is: 9227465
嗯,还差一丢丢,不过已经在一个量级了
后注:
crate_type 可选值有好几个,如:bin, lib, dylib, staticlib, cdylib等,具体解释可以参考Linkage
我在看的时候,比较疑惑dylib与cdylib,因为文档上都标注了可以被外部程序或库调用,而且编译出的dll文件,经过测试,都是可以使用的,那么区别是什么呢
文末参考2的rfcs链接中有详细的介绍,如下罗列一下区别:
Metadata: dylib保留有一些元数据,cdylib没有
Symbol visibility: 符号能见度,dylib打出的dll文件里有很多ABI函数,cdylib则只导出用户指定的函数
LTO:“LTO - this will disallowed for rdylibs, but enabled for cdylibs.” (注: 不懂)
Linkage: dylib默认会自动链接标准库,cdylib默认会自动链接所有依赖
使用Dependency Walker工具查看符号能见度
rust dll
另外,使用编译的cdylib风格的DLL明显比dylib风格的DLL小很多,本例中,dylib大小为885kb,cdylib的大小为10kb,体积有大幅的减小
参考:
Rust Inside Other Languages
rust-lang/rfcs
Why python is much slower than node.js on recursion