这是一篇关于在 Rust 中使用实习(interning)模式压缩巴黎公共交通网络开放数据的博客文章,主要内容总结如下:
- 背景与目标:作者浏览巴黎公共交通网络的开放数据仓库,发现 RATP 状态网站利用该数据提供了历史中断的可视化界面。数据存储约 10GB,作者希望通过实习模式压缩数据,减少存储空间。
- 导入数据(Importing the data):使用 Rust 的
serde
库将 JSON 数据导入程序,定义数据模式为 Rust 结构体和枚举,并实现反序列化。通过std::mem::size_of()
函数估计对象在内存中的占用空间,发现导入 2024 年 5 月的文件使内存使用量增加了 35%。 实习模式(Interning):
- 字符串实习(Strings):受matklad的博客启发,作者实现了一个字符串实习器,将重复的字符串存储为索引,以减少内存使用。通过将字符串包装在
Rc
中避免内存重复,并解决了引用与内部可变性的冲突。实验表明,字符串实习使内存使用量减少了约 54%。 - 任意类型实习(Arbitrary types):将实习技术扩展到任意实现
Eq
和Hash
trait 的类型,为不同类型创建各自的实习器,并在数据结构中使用实习后的类型。这使得内存使用量进一步减少约 87%。 - 删除引用(Dropping the reference):简化实习对象的设计,去除对实习器的引用,通过比较索引实现比较和哈希操作。将实习索引从
usize
改为u32
,使内存使用量减少约 69%。
- 字符串实习(Strings):受matklad的博客启发,作者实现了一个字符串实习器,将重复的字符串存储为索引,以减少内存使用。通过将字符串包装在
调整数据模式(Tuning the schema):
- 排序集合(Sorting sets):由于 Rust 的
Vec
集合类型是有序的,而某些集合中的元素顺序无关紧要,导致内存使用增加。通过手动实现PartialOrd
和Ord
trait 对集合进行排序,减少了内存使用约 67%。 - 使用枚举(Using enums):将
Data
根结构体中的可选字段改为枚举类型,减少了内存使用约 4%。 - 拆分结构体(Splitting structs):将
ImpactedObject
结构体中的固定字段和变化字段分离,创建新的Object
结构体,并对其进行实习,减少了内存使用约 2%。 - 专门化类型(Specializing types):对于字符串实习器中的不同类型,如 UUID 和时间戳,使用更紧凑的表示形式,进一步减少了内存使用约 28%。
- 排序集合(Sorting sets):由于 Rust 的
序列化(Serialization):
- 使用 Serde 编写自定义序列化器(Writing custom (de)serializers with Serde):为实习器类型编写自定义的序列化和反序列化函数,以更高效地处理实习数据。比较不同序列化格式的编码大小和时间,发现 Postcard 格式在压缩后最小。
- 压缩与处理 Rust 借用检查器(Compression and fighting the Rust borrow checker):通过线程处理压缩过程,避免 Linux 管道的死锁问题。压缩后,不同格式的文件大小差异减小,但更优化的格式在序列化和反序列化速度上更快。
- 元组编码(Tuple encoding):使用
serde_tuple
crate 将结构体序列化为元组,减少了序列化后的数据大小,尤其是对于包含PhantomData
的结构体。 - 重新优化集合(Optimizing sets revisited):对集合进行差量编码和行程长度编码,减少了序列化后的数据大小,尤其是对于连续的 ID 序列。
- 最终结果:轻量级追加数据库(Final result: a lightweight append-only database):通过实习和各种优化技术,将 1.1GB 的 JSON 数据压缩到 530KB 左右,实现了约 2000 倍的大小减少。文章还讨论了实习模式的应用和注意事项,以及如何根据具体需求选择是否使用现有的实习库或自行实现。
总的来说,实习模式在压缩 Rust 中的数据方面非常有效,可以显著减少内存使用和文件大小。通过对不同数据类型的实习和各种优化技术的组合,可以实现更高效的数据存储和处理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。