rust 作为1字节位掩码的布尔结构

nhjlsmyf  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(147)

如果你有下面的布尔结构,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似乎有点矫枉过正。

dvtswwa3

dvtswwa31#

为什么编译器不将其优化为1字节的位掩码?
它不能,因为每个变量和字段的地址都可以被获取,并且它们的值可以通过这个引用被观察和变化。所以假设我们引用field_2,它将在内存中与field_1重叠,编译器不知道从哪一个读取。
这可以通过在引用中使用额外的数据来解决(基本上,同时使用地址和位的胖引用)。但是这打开了另一个蠕虫,因为现在&bool引用与任何其他引用都不兼容,这意味着我们不能将bool s的切片视为u8 s的切片,这是一个常见的用例。
这也并不总是更快。它节省内存,但速度可能会受到负面影响(也可能不会;这是一个复杂的情况)。
我怎样才能使结构成为真正的位掩码?
使用bitflags crate。它几乎是标准的:

bitflags::bitflags! {
    struct Mask : u8 {
        const FLAG1 = 0b001;
        const FLAG2 = 0b010;
        const FLAG3 = 0b100;
    }
}

字符串

ijnw1ujt

ijnw1ujt2#

因为位掩码实际上比字节级操作更慢,也更复杂。没有“从内存中的位置X读取第三个字节”指令。如果你的结构体存储为三个字节,从位置X开始,你想访问field_2,那么你只需要访问内存中的位置X+1。这很容易,这是现代系统上的一条指令。
如果你的结构体存储为三位,并且你想访问field_2,那么你必须访问整个位掩码(在位置X),将其加载到寄存器中,然后按位执行my_value & 2以获得所需的值。(0和1,而不是0和“一些不确定的非零值”),你也必须对它进行位移位。每次访问需要两到三条指令,修改也是如此。这是隐藏的复杂性。
即使我们愿意付出代价,它也会完全重新定义Rust的引用语义。你只能有指向字节的指针。所以你不能引用field_2field_3,因为它们不是字节对齐的(对field_1的引用将是对整个结构体的引用)。因此Rust的引用必须是某种形式的“胖指针”,存储OS指针和一些偏移数据,这将给Rust已经复杂的借用机制增加大量复杂性。
我不知道Rust中有什么内置的方法可以做到这一点。Zig支持packed struct,但在Rust中,我相信你最终会拉入一个外部库。如果它真的是一个3字节的结构体,(与您为我们提炼的更复杂的示例相反),那么我会说让Rust做它自己的事情,忘记位打包。但是如果你已经对它进行了基准测试,它确实是一个瓶颈,那么就看看一些外部的板条箱。

相关问题