Eu sei que os campos nas estruturas Rust são reordenados automaticamente para economizar memória (a menos que, por exemplo, #[repr(C)]
seja especificado).
Eu estava supondo que o mesmo se aplica a enums. Hoje eu estava criando um tipo auxiliar simples (a semântica do exemplo não importa, trata-se de layout de memória):
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>),
}
Enquanto um Read/Write Guard bruto usa 16 bytes, esse tipo usa 32 . Para fazer com que o compilador use a otimização de nicho, tentei esta versão:
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>),
}
Isso reduz com sucesso o uso de memória para 24 bytes.
Porém, notei algo estranho: Ao inverter a ordem dos campos na Read
variante:
pub enum LazyRwLockGuardVersionC<'a, T> {
Read {
lock: &'a RwLock<T>, // order of fields reversed
guard: RwLockReadGuard<'a, T>,
},
NonRead(LazyRwLockWriteGuard<'a, T>),
}
Esse tipo usa repentinamente 32 bytes novamente.
Minha versão Rust é 1.77, aqui está uma reprodução do godbolt: https://godbolt.org/z/svE5v6Tr8
- Os campos enum não podem ser reordenados pelas especificações ou o compilador é apenas 'preguiçoso' aqui?
- Existe uma intuição aproximada para o algoritmo que o compilador usa atualmente para o layout de enums, para que eu possa entender esse resultado e evitar pessimizações acidentais do meu código no futuro?
O layout padrão/
Rust
tipo é completamente inespecificado além das garantias de solidez. Caso contrário, o compilador é livre para fazer o que quiser e, de fato, isso muda entre as versões do compilador.Se quiser ter garantia do layout, você deve selecionar uma representação diferente .