调优 Salsa

缓存淘汰(LRU)

Salsa 支持为 tracked 函数启用 LRU(Least Recently Used,最近最少使用)缓存淘汰。 默认情况下,memoized 值永远不会被淘汰,也就是无界缓存。 你可以在编译期通过指定容量来启用 LRU:

#![allow(unused)]
fn main() {
#[salsa::tracked(lru = 128)]
fn parse(db: &dyn Db, input: SourceFile) -> Ast {
    // ...
}
}

当设置 lru = 128 时, Salsa 最多会为这个函数保留 128 个缓存结果。 当缓存超过这个容量后, 每次进入新修订时,就会把最久未使用的值淘汰掉。

关闭时零成本

如果没有指定 lru 容量(也就是默认情况), Salsa 会使用一个空操作的淘汰策略, 编译器会把它完全优化掉。 这意味着那些不需要缓存淘汰的函数不会承担任何运行时开销。

运行时调整容量

对于启用了 LRU 的函数,你还可以在运行时调整容量:

#![allow(unused)]
fn main() {
#[salsa::tracked(lru = 128)]
fn my_query(db: &dyn Db, input: MyInput) -> Output {
    // ...
}

// 之后再调整容量:
my_query::set_lru_capacity(db, 256);
}

注意: set_lru_capacity 方法只会为带有 lru 属性的函数生成。 没有启用 LRU 的函数并不会生成这个方法。

内存管理

旧查询的键和值目前都不会被垃圾回收, 因此对于基于 Salsa 的长时间运行程序来说, LRU 缓存是目前避免内存无限增长的主要机制。

Intern 查询

Intern 查询可以让键查找更便宜、节省内存, 并避免依赖 Arc

对于那些涉及嵌套树状数据结构的查询, interning 尤其有帮助。

参见:

取消(Cancellation)

当并发写入发生,或者依赖关系变化导致某些查询结果已经不再需要时, Salsa 会取消这些查询。 每一次访问中间查询,都是一个潜在的取消点。 取消是通过 panic 实现的,而 Salsa 内部的设计目标之一就是保证 panic-safe。

如果你的某个查询里有一个很长的循环, 并且这个循环期间没有执行任何中间查询, 那么 Salsa 就无法自动取消它。 这时你可能希望自行调用 db.unwind_if_cancelled() 来检查是否已被取消。

关于取消的更多细节,可以参考 Salsa 仓库里那些测试取消行为的测试用例。