为什么 Rust 不重新排序枚举中的字段以进行内存布局?

问题描述 投票:0回答:1

我知道 Rust 结构中的字段会自动重新排序以节省内存(除非指定了

#[repr(C)]
)。

我假设枚举也是如此。 今天我正在创建一个简单的辅助类型(示例的语义并不重要,这是关于内存布局的):

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 enums language-lawyer compiler-optimization memory-layout
1个回答
0
投票

规范不允许枚举字段重新排序,还是编译器在这里只是“懒惰”?

默认/

Rust
类型布局完全未指定,超出了健全性保证。否则,编译器可以自由地做任何它想做的事情,事实上,这确实在编译器版本之间发生了变化。

对于编译器当前用于布局枚举的算法是否有一个粗略的直觉,以便我可以理解这个结果,并防止将来我的代码意外悲观?

如果想要布局有保证,就必须使用

#[repr(C)]
。其他任何事情都将由编译器随意决定。

© www.soinside.com 2019 - 2024. All rights reserved.