【问题标题】:Can I use the "null pointer optimization" for my own non-pointer types?我可以对自己的非指针类型使用“空指针优化”吗?
【发布时间】:2015-08-05 12:02:35
【问题描述】:

当您有Option<&T> 时,编译器知道NULL 绝不可能是&Tencodes the None variant as NULL instead 的值。这样可以节省空间:

use std::mem;

fn main() {
    assert_eq!(mem::size_of::<&u8>(), mem::size_of::<Option<&u8>>());
}

但是,如果您对非指针类型执行相同操作,则不需要额外的位来存储该值,并且需要额外的空间:

use std::mem;

fn main() {
    // fails because left is 1 and right is 2
    assert_eq!(mem::size_of::<u8>(), mem::size_of::<Option<u8>>()); 
}

一般来说,这是正确的。但是,我想选择优化,因为我知道我的类型具有某些不可能的值。作为一个虚构的例子,我可能有一个有年龄的玩家角色。年龄可能不详,但绝不会高到255

struct Age(u8);

struct Player {
    age: Option<Age>,
}

我希望能够将此约束通知优化器 - Age 永远不会是 255,因此使用该位模式作为 None 是安全的。这可能吗?

【问题讨论】:

    标签: optimization rust


    【解决方案1】:

    从 Rust 1.28 开始,您可以使用 std::num::NonZeroU8(和朋友)。这充当了一个包装器,告诉编译器一个数字的内容将永远包含文字零。这也是为什么Option&lt;Box&lt;T&gt;&gt; 是指针大小的原因。

    这是一个示例,展示了如何创建 Age 并读取其有效负载。

    use std::num::NonZeroU8;
    
    struct Age(NonZeroU8);
    
    impl Age {
        pub fn new(age: u8) -> Age {
            let age = NonZeroU8::new(age).expect("Age cannot be zero!");
            Age(age)
        }
    
        pub fn age(&self) -> u8 {
            self.0.get()
        }
    }
    
    struct Player {
        age: Option<Age>,
    }
    
    fn main() {
        println!("size: {}", std::mem::size_of::<Player>());
        // Output: size: 1
    }
    

    【讨论】:

    • 顾名思义,我假设NonZero 不允许零值。零以外的值呢?在我的示例中,0 可能有效,但 255 无效。
    • @Shepmaster:它被硬编码为非 0,我想有一个特征和相关的常量可以扩展它......但是现在你必须满足于数学。如果 255 是您的魔法值,那么将 +1 应用于存储和来自存储的 -1(使用包装算法)将足以使 NonZero 魔法起作用:)
    • @MatthieuM。当然,我只需要做更多的分析,看看它是否真的值得。使用更少的字节似乎是一个明显的胜利。在任何地方使用更少的字节强制数学不太可靠。
    • @Shepmaster:当然;虽然 0 是一个经常使用的魔法值,但大量的分支指令专门用于它(jzjnz 浮现在脑海中),因此尚不清楚关联的常量是否能胜过它。
    • BTW NonZeroU* 将在 1.28 稳定。
    猜你喜欢
    • 2011-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多