我知道 Rust 结构中的字段会自动重新排序以节省内存(除非#[repr(C)]
指定了eg)。
我假设枚举也是如此。今天我正在创建一个简单的辅助类型(示例的语义并不重要,这是关于内存布局的):
pub enum LazyRwLockGuardVersionA<'a, T> {
Unlocked(&'a RwLock<T>),
Read {
lock: &'a RwLock<T>, // still neccessary for promoting to write lock
guard: RwLockReadGuard<'a, T>,
},
Write(RwLockWriteGuard<'a, T>),
}
原始读/写防护使用16 个字节,而此类型使用32 个字节。为了让编译器使用利基优化,我尝试了这个版本:
pub enum LazyRwLockWriteGuard<'a, T> {
Unlocked(&'a RwLock<T>),
Write(RwLockWriteGuard<'a, T>),
}
pub enum LazyRwLockGuardVersionB<'a, T> {
Read {
guard: RwLockReadGuard<'a, T>,
lock: &'a RwLock<T>,
},
NonRead(LazyRwLockWriteGuard<'a, T>),
}
这成功地将内存使用量降低到24字节。
但是,我注意到一些奇怪的事情:当反转变体中的字段顺序时Read
:
pub enum LazyRwLockGuardVersionC<'a, T> {
Read {
lock: &'a RwLock<T>, // order of fields reversed
guard: RwLockReadGuard<'a, T>,
},
NonRead(LazyRwLockWriteGuard<'a, T>),
}
这个类型突然又使用了32字节。
我的 Rust 版本是 1.77,这里是 godbolt 重现:https://godbolt.org/z/svE5v6Tr8
- 规范不允许枚举字段重新排序,还是编译器在这里只是“懒惰”?
- 对于编译器当前用于布局枚举的算法是否有一个粗略的直觉,以便我可以理解这个结果,并防止将来我的代码意外悲观?
默认/
Rust
类型布局完全未指定,超出了健全性保证。编译器可以自由地做任何它想做的事情,事实上,这确实在编译器版本之间发生了变化。如果想要布局有保证,就必须选择不同的表示方式。