调优 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 尤其有帮助。
参见:
compiler示例, 其中就使用了 interning。
取消(Cancellation)
当并发写入发生,或者依赖关系变化导致某些查询结果已经不再需要时, Salsa 会取消这些查询。 每一次访问中间查询,都是一个潜在的取消点。 取消是通过 panic 实现的,而 Salsa 内部的设计目标之一就是保证 panic-safe。
如果你的某个查询里有一个很长的循环,
并且这个循环期间没有执行任何中间查询,
那么 Salsa 就无法自动取消它。
这时你可能希望自行调用 db.unwind_if_cancelled() 来检查是否已被取消。
关于取消的更多细节,可以参考 Salsa 仓库里那些测试取消行为的测试用例。