Rust中的空指针优化是什么?

53
使用过多链表的 Rust 学习指南中,作者提到:

然而,如果我们有一种特殊类型的枚举:

enum Foo {
    A,
    B(ContainsANonNullPtr),
}
如果空指针优化启动,将会省去标签所需的空间。如果变量是A,则整个枚举将被设置为所有0。否则,该变量将是B。这起作用的原因是B永远不可能是全0,因为它包含一个非零指针。
let test = Foo::A

内存布局是

0000 0000

但是

let test = Foo::B

内存布局是

some 8 bit non 0 value

这里优化了什么?这两种表示方式不都是8位吗?当作者声称什么时,这是什么意思?

这意味着在 Rust 中,&&mutBoxRcArcVec 和其他几个重要类型在放入 Option 时没有额外开销。

3个回答

78
空指针优化基本上意味着,如果你有一个枚举类型,其中一个变体没有关联的数据,而另一个变体有关联的数据,其中所有零位模式都不是有效值,那么枚举本身将占用与该关联值完全相同的空间,使用全零位模式来表示它是另一个变体。
换句话说,这意味着 Option<&T>&T 的大小完全相同,而不需要额外的字。

我理解这一点。但编译器如何知道全零是无效值?我猜测优化只针对特定的内置类型才会启动。如果是这样,那么是哪些类型呢? - Noel Widmer
13
编译器内置了各种类型的内存布局知识。例如,它知道& 引用永远不可能为空。它还知道StringVec永远不可能是全零;深入实现后,StringVec支持,VecRawVec支持,RawVecUnique支持,其中包含一个 *const T ,但具有编译器属性声明其不能为null。类似地,标准库中有一个NonNull<T>类型,作为一个*mut T,永远不可能为空指针。 - Lily Ballard
9
具体来说,空字符串和向量不指向null,它们指向一个固定的非空地址,容量为零。其他容器如HashMap也是同样的情况,可以廉价地创建。Rust标准库尽力避免使用指针为空的情况,以便将全零值保留用于诸如空指针优化等事项。 - Lily Ballard

23

enum 是一个带标签的联合体。在没有优化的情况下,它看起来像是:

Foo::A;    // tag 0x00 data 0xXX
Foo::B(2); // tag 0x01 data 0x02
空指针优化会去除独立的标签字段。
Foo::A;    // tag+data 0x00
Foo::B(2); // tag+data 0x02

16
第二个例子似乎有一点不准确,因为0x00是一个整数的有效位模式,所以它的含义是含糊的,既可以是Foo::A也可以是Foo::B(0) - mvlabat

9

我也在学习太多的链表,也许这段代码能加深你的理解

pub enum WithNullPtrOptimization{
    A,
    B(String),
}

pub enum WithoutNullPtrOptimization{
    A,
    B(u32),
}

fn main()  {
    println!("{} {}", std::mem::size_of::<WithNullPtrOptimization>(), std::mem::size_of::<String>()); // 24 24
    println!("{} {}", std::mem::size_of::<WithoutNullPtrOptimization>(), std::mem::size_of::<u32>()); // 8 4
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接