即使在有限的范围数字情况下,是否建议使用`u32` /`i32`?

时间:2016-10-09 02:29:20

标签: rust

我们应该使用u32 / i32或它的低级变体(u8 / i8u16 / i16)在处理有限范围的数字时,例如"月内的情况"其范围为1-30或"主题得分"范围从0到100?或者为什么我们不应该?

较低的变体是否有任何优化或好处(即内存效率)?

3 个答案:

答案 0 :(得分:6)

摘要

正确性应该优先于性能和正确性(对于像1-100这样的范围),所有解决方案(u8u32,......)都同样糟糕。最好的解决方案是创建一个新类型,以便从强类型中受益。

我的答案的其余部分试图证明这种说法是正确的,并讨论了创建新类型的不同方法。

更多解释

让我们来看看“主题得分”的例子:唯一的合法值是0-100。我认为正确性,使用u8u32同样糟糕:在这两种情况下,您的变量都可以在您的语义上下文中保存合法的值;那很糟!

并且认为u8更好,因为有更少的非法价值观,就像争辩说摔跤熊比走过纽约更好,因为你只有一种可能性死亡(熊的失血)与纽约的死亡(车祸,刀袭,溺水......)的许多可能性相反。

所以我们想要的是一种保证只保留合法价值的类型。我们想要创建一个完全符合这一要求的新类型。但是,有多种方法可以继续;各有利弊。

(A)将内部值公开

struct ScoreOfSubject(pub u8);

优势:至少API更容易理解,因为参数已经由类型解释。更容易理解的是:

  • add_record("peter", 75, 47)
  • add_record("peter", StudentId(75), ScoreOfSubject(47))

我会说后者; - )

缺点:我们实际上没有进行任何范围检查,仍然可能发生非法值;糟糕!

(B)将内部值设为私有并提供范围检查构造函数

struct ScoreOfSubject(pub u8);

impl ScoreOfSubject {
    pub fn new(value: u8) -> Self {
        assert!(value <= 100);
        ScoreOfSubject(value)
    }
    pub fn get(&self) -> u8 { self.0 }
}

优势:我们使用非常少的代码强制执行合法值,是的:)

缺点:使用该类型可能很烦人。几乎每个操作都需要程序员打包和安装。解包价值。

(C)添加一堆实现(除了(B))

(代码会impl Add<_>impl Display等等)

优势:程序员可以直接使用该类型并对其执行所有有用的操作 - 进行范围检查!这非常理想。

请看看Matthieu M.的评论:

  

[...]通常将分数相乘或除以它们不会产生分数!强类型不仅强制执行有效值,还强制执行有效操作,因此您实际上不会将两个分数分开以获得另一个分数。

我认为这是一个非常重要的一点,我以前没有说清楚。强类型可以防止程序员对值执行非法操作(没有任何意义的操作)。一个很好的例子是crate cgmath,它区分了point和direction向量,因为它们都支持不同的操作。您可以找到其他解释here

缺点:很多代码:(

幸运的是,Rust宏/编译器插件系统可以减少这个缺点。有像newtype_derivebounded_integer这样的包装箱为你做这种代码生成(免责声明:我从来没有使用它们)。

但是现在你说:“你不能认真吗?我应该花时间写新类型吗?”。

不一定,但是如果你正在处理生产代码(==至少有些重要),那么我的回答是:是的,你应该

答案 1 :(得分:3)

无答案答案:我怀疑你会发现基准测试有任何不同,除非你很多算术或过程巨大数组。

你可能应该选择更有意义的类型(没有理由使用底片或者每月有数百万的上限)并提供你需要的方法(例如你不能执行{ {1}}直接在无符号整数上。)

答案 2 :(得分:1)

使用较小的类型可能会有很大的好处,但您必须在目标平台上对应用程序进行基准测试才能确定。

较低内存占用的第一个也是最容易实现的好处是更好的缓存。您的数据不仅更有可能适合缓存,而且还不太可能丢弃缓存中的其他数据,从而可能改善应用程序的完全不同的部分。是否触发此操作取决于应用程序接触的内存和顺序。做基准吧!

使用较小的类型,网络数据传输有明显的好处。

较小的数据允许“更大”的指令。 128位SIMD单元可以处理4个32位数据或16个8位数据,使某些操作速度提高4倍。在基准测试中,我使这些指令的执行速度确实提高了4倍,但整个应用程序的改进提高了不到1%,并且代码变得更加混乱。将程序简化为更好地使用SIMD可能会非常棘手。

截至签名/未签名讨论时,unsigned的属性稍好一些,编译器可能会或可能不会利用这些属性。