这是课程"做你自己的套利机器人"的辅助课。第 13 课把扫描器的热点循环搬到 Rust 里。你不需要成为 Rust 程序员,只要能读一小段程序并运行它。
直接的办法:继续用 Python
第一个扫描器用 Python 没问题。麻烦从一个函数每秒要运行数百万次时开始:解释器每一步都要多做工作。像 Rust 这样的编译型语言,在运行前一次性把程序变成机器码,之后循环的每一步几乎不花什么。代价是要多写一点,而且编译器很严格。
一个小程序
还是上一篇预备课里的那四个价格。保存为 r.rs:
fn mean(v: &Vec<f64>) -> f64 {
let mut sum = 0.0;
for x in v {
sum += x;
}
sum / v.len() as f64
}
fn main() {
let prices: Vec<f64> = vec![100.0, 101.5, 99.8, 102.0];
let mut best = 0.0;
for i in 0..prices.len() - 1 {
let ratio = prices[i + 1] / prices[i];
println!("step {}: ratio {:.4}", i, ratio);
if ratio > best {
best = ratio;
}
}
println!("best {:.4}, mean price {:.3}", best, mean(&prices));
}
编译并运行:rustc -O r.rs -o r && ./r。
step 0: ratio 1.0150
step 1: ratio 0.9833
step 2: ratio 1.0220
best 1.0220, mean price 100.825
怎么读
fn name(...) -> type { ... }定义一个函数。最后一个不带分号的表达式就是它的返回值:sum / v.len() as f64返回平均值。let创建变量。除非写成let mut,否则它不能改变。所以sum和best带了mut。Vec<f64>是一列小数(向量)。vec![...]创建它。v.len()是它的长度。for i in 0..n让 i 从 0 循环到 n 减 1。for x in v遍历每一项。&prices的意思是"把向量借给函数,不要送出去"。细节现在可以先跳过:如果编译器抱怨所有权问题,把提示信息给你的智能体看。println!用来打印。{}是放值的位置,{:.4}是保留小数点后 4 位的小数。- 类型是明确写出来的(
f64),类型对不上编译器就拒绝这个程序。这感觉很严格,但能在程序运行前抓出错误。
为什么标志 -O 很重要
不加 -O,Rust 会构建一个用于调试的慢版本。加上它,编译器会做优化。在一台机器上跑十亿步的循环:
without -O: 1.18 s
with -O: 0.39 s
(你的数字会不同。)一定要测量发布版本,也就是用 -O 构建的版本;如果用 cargo,就用 cargo build --release。
你的练习
- 安装 Rust(rustup.rs),保存程序并运行。对照输出。
- 加上第五个价格,让程序也打印最差的比值。
- 写一个函数
max_of(v: &Vec<f64>) -> f64并打印它的结果。 - 用
-O和不用-O分别构建一个 3 亿步的循环,比较耗时。 - 让你的智能体用通俗的话解释你遇到的第一个编译器错误。
下一步
现在可以读第 13 课的 Rust 代码了。