1 个不稳定版本

0.1.0-dev.12023年12月15日

内存管理 中排名第 558

Download history 601/week @ 2024-04-03 597/week @ 2024-04-10 565/week @ 2024-04-17 385/week @ 2024-04-24 425/week @ 2024-05-01 416/week @ 2024-05-08 487/week @ 2024-05-15 582/week @ 2024-05-22 627/week @ 2024-05-29 505/week @ 2024-06-05 545/week @ 2024-06-12 546/week @ 2024-06-19 540/week @ 2024-06-26 319/week @ 2024-07-03 737/week @ 2024-07-10 695/week @ 2024-07-17

每月下载量 2,380
用于 4 个 crates (通过 timely-container-master)

MIT 许可证

42KB
643

列化

一个实验性的列式竞技场

列化借用了来自 Abomonation 的名字,这是一个非常快且非常不安全的 Rust 序列化框架。在 Abomonation 的不安全性中,它托管了由 [u8] 切片支持的类型数据,这引起了关注对对齐、填充字节可见性以及可能我尚未了解的其他事物的担忧。

列化稍微好一些,因为它只维护类型分配 Vec<T>,并且不会调用 mem::transmute 来更改类型。这并不意味着它是 安全的,只是意味着代码中有更少的地方可能是不安全的,一旦 Rust 的不安全性故事明朗化。

相反,列化通过将类型可能拥有的所有者分配(例如,一个 String 或一个 Vec<T> 用于其他类型 T)合并成相对较少且较大的分配,并将这些类型中的指针重写为指向更大的分配来工作。这使得这些类型不适用于任何其他用途,而只能通过不可变引用使用。这也意味着列化非常不安全,目前只是一个实验。

示例用法

某些类型实现了Columnation,这是一个Rust特质,它没有任何方法,但允许实例化一个ColumnStack,你可以将类型的实例复制到其中。然后,通过栈的Deref实现,可以访问这些实例,该实现将栈呈现为一个&[Type]。索引元素呈现为&Type,原则上你可以使用这些引用执行所有常规操作,尽管实际上并没有实际的Type作为它们的后盾。

// Some data are suitable for translation into columnar form.
let my_data = vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32];

// Such data can be copied in to a columnar region.
let mut my_region = ColumnStack::default();
for _ in 0 .. 1024 {
    my_region.copy(&my_data);
}

// The copying above is substantially faster than cloning the
// data, 21ms versus 198ms, when cloned like so:
let mut my_vec = Vec::with_capacity(1024);
for _ in 0 .. 1024 {
    my_vec.push(my_data.clone());
}

// At this point, `my_region` has just tens of allocations,
// despite presenting as if a thousand records which would
// normally have a thousand allocations behind each of them.
assert_eq!(&my_region[..], &my_vec[..]);

测量

我选取了各种类型的记录,通常包含大约一千次分配,并将它们在容器中复制或克隆1024次,就像上面一样。以下是Rust的cargo bench工具提供的基准时间,其中_clone是克隆到向量中,而_copy是复制到基于区域的容器中。

running 16 tests
test empty_clone      ... bench:         835 ns/iter (+/- 114)
test empty_copy       ... bench:       3,103 ns/iter (+/- 346)
test string10_clone   ... bench: 108,914,118 ns/iter (+/- 7,864,343)
test string10_copy    ... bench:   4,654,702 ns/iter (+/- 312,409)
test string20_clone   ... bench:  59,302,789 ns/iter (+/- 8,014,183)
test string20_copy    ... bench:   2,818,970 ns/iter (+/- 191,415)
test u32x2_clone      ... bench:   1,920,494 ns/iter (+/- 198,815)
test u32x2_copy       ... bench:     282,235 ns/iter (+/- 40,851)
test u64_clone        ... bench:   1,951,842 ns/iter (+/- 129,288)
test u64_copy         ... bench:     234,412 ns/iter (+/- 25,186)
test u8_u64_clone     ... bench:   1,931,056 ns/iter (+/- 162,882)
test u8_u64_copy      ... bench:     266,326 ns/iter (+/- 35,203)
test vec_u_s_clone    ... bench: 120,642,691 ns/iter (+/- 9,124,488)
test vec_u_s_copy     ... bench:   5,801,229 ns/iter (+/- 522,024)
test vec_u_vn_s_clone ... bench: 134,171,625 ns/iter (+/- 18,599,137)
test vec_u_vn_s_copy  ... bench:   8,580,739 ns/iter (+/- 451,180)

在每种情况下,除了故意简单的empty情况外,_copy版本都比_clone版本明显更快。这在某种程度上是有道理的,因为在_copy情况下,我们可以跨运行重用所有的分配,而在_clone情况下,只有向量的脊柱被重用(我们可以尝试更复杂的缓冲池,但我们没有这样做)。

empty情况有一个有趣的故事。当我们把多个Vec<()>分配合并到一个Vec<()>中时,我们引入了维护该向量的长度和容量的成本。它应该是一个“无界”的零大小类型,但实际上我们需要验证它不会达到usize::MAX。而empty_clone情况不需要这样做,并优化为memcpy,而empty_copy情况必须在每次插入时检查容量。

描述

Vec<String>这样的类型将使用类似于以下内存使用方式编码在Columnation中

struct Roughly {
    /// Where each `Vec<String>` record is put.
    records: Vec<Vec<String>>,
    /// All of the `String` in all of the records.
    strings: Vec<String>
    /// All of the bytes in all of the strings in all of the records.
    bytes: Vec<u8>,
}

事实上,每个成员都是一个分配序列,因为我们无法重新调整我们的任何分配(除非进行大量工作),我们而是创建新的几何级增加的分配。该结构具有特殊的Drop实现,它释放了三个分配包,但不递归调用其成员的Drop实现(以避免尝试释放指向分配内部指针,我们知道这些指针是无效的)。

更通用的类型需要描述如何安全地存储它们可能拥有的所有权分配。它们实现的Columnation特质允许它们指定一个关联类型,InnerRegion,这是吸收类型所拥有的分配。

与Abomonation的关系

Columnation的意图与Abomonation相似,即在不需要实际所有类型存在的情况下,向Rust类型提供不可变引用,但它们的内存布局不同。

Abomonation会将每个记录连续序列化,而Columnation“序列化”每个记录后面的分配,分别进入不同的静态类型缓冲区。当您想调查整个记录时,Abomonation具有引用局部性的优势,而Columnation在您经常只需要记录背后分配的子集时,具有内存紧凑性的优势。

安全性

推动代码的一部分目标是让一些人来看看。它可能是安全的,但我对确保安全所需的指针别名规则并不清楚(也不清楚它们在哪里记录)。此外,可能完全存在错误,但我应该修复它们。

依赖关系