如果你有下面的布尔结构,Rust Analyzer说它占用了3个字节,因为有三个字段。
struct Mask {
field_1: bool,
field_2: bool,
field_3: bool,
} // size = 3, align = 1
字符串
*为什么编译器不将其优化为1字节的位掩码?
*如何使结构体成为真正的位掩码?
据我所知,API都是相同的(例如,获取/设置字段+方法)。
0000_0000
^^^ field_1
|L_ field_2
L__ field_3
型
P.S.我知道modular_bitfield crate可以将Mask
结构体变成一个真正的位掩码,但是对于这样简单的东西使用外部crate似乎有点矫枉过正。
2条答案
按热度按时间dvtswwa31#
为什么编译器不将其优化为1字节的位掩码?
它不能,因为每个变量和字段的地址都可以被获取,并且它们的值可以通过这个引用被观察和变化。所以假设我们引用
field_2
,它将在内存中与field_1
重叠,编译器不知道从哪一个读取。这可以通过在引用中使用额外的数据来解决(基本上,同时使用地址和位的胖引用)。但是这打开了另一个蠕虫,因为现在
&bool
引用与任何其他引用都不兼容,这意味着我们不能将bool
s的切片视为u8
s的切片,这是一个常见的用例。这也并不总是更快。它节省内存,但速度可能会受到负面影响(也可能不会;这是一个复杂的情况)。
我怎样才能使结构成为真正的位掩码?
使用
bitflags
crate。它几乎是标准的:字符串
ijnw1ujt2#
因为位掩码实际上比字节级操作更慢,也更复杂。没有“从内存中的位置X读取第三个字节”指令。如果你的结构体存储为三个字节,从位置X开始,你想访问
field_2
,那么你只需要访问内存中的位置X+1。这很容易,这是现代系统上的一条指令。如果你的结构体存储为三位,并且你想访问
field_2
,那么你必须访问整个位掩码(在位置X),将其加载到寄存器中,然后按位执行my_value & 2
以获得所需的值。(0和1,而不是0和“一些不确定的非零值”),你也必须对它进行位移位。每次访问需要两到三条指令,修改也是如此。这是隐藏的复杂性。即使我们愿意付出代价,它也会完全重新定义Rust的引用语义。你只能有指向字节的指针。所以你不能引用
field_2
或field_3
,因为它们不是字节对齐的(对field_1
的引用将是对整个结构体的引用)。因此Rust的引用必须是某种形式的“胖指针”,存储OS指针和一些偏移数据,这将给Rust已经复杂的借用机制增加大量复杂性。我不知道Rust中有什么内置的方法可以做到这一点。Zig支持
packed struct
,但在Rust中,我相信你最终会拉入一个外部库。如果它真的是一个3字节的结构体,(与您为我们提炼的更复杂的示例相反),那么我会说让Rust做它自己的事情,忘记位打包。但是如果你已经对它进行了基准测试,它确实是一个瓶颈,那么就看看一些外部的板条箱。