首页 > 解决方案 > 为什么我们使用掩码来切割字节?

问题描述

所以我想这个很愚蠢,但我自己想不出答案。

几天前,我不得不创建一个返回无符号整数的强字节的函数。在网上查了一些资料后,我想到了这个:

fn strong_byte(num: u32) -> u8 {
  ((num >> 24) & 0xFF) as u8
}

但是又找了一会儿,我也找到了这个:

fn strong_byte(num: u32) -> u8 {
  (num << 24 >> 24) as u8
}

所以我想知道哪种形式的性能更高?我试图找到显示左移与位掩码性能的基准,但没有找到任何东西......

我知道第一种语法是迄今为止最常用的,但我不明白为什么第二种不是......

标签: rust

解决方案


积分也适用于其他人,请参阅问题下方的评论。

术语

什么是强字节?您应该使用最低有效字节(LSB) 或最高有效字节(MSB)。

你的功能

我在名称中添加了_1_2后缀,只是为了区分它们。

fn strong_byte_1(num: u32) -> u8 {
  ((num >> 24) & 0xFF) as u8
}
fn strong_byte_2(num: u32) -> u8 {
  (num << 24 >> 24) as u8
}

这两个函数做不同的事情。猜猜输出是什么...

fn main() {
    println!("{}", strong_byte_1(255));
    println!("{}", strong_byte_2(255));
}

... 是。_1返回0_2返回255_1返回 MSB 和返回_2LSB。如果你想比较它们,你必须修复它们。

实现也strong_byte_1包含不必要的位掩码。((num >> 24) & 0xFF) as u8等于(num >> 24) as u8。在此处查看 Shepmaster 的答案。该>>操作包含脚注:

有符号整数类型的算术右移,无符号整数类型的逻辑右移。

u32是无符号 -> 逻辑右移(请参阅逻辑移位),这意味着:

0b11111111000000000000000000000000 >> 1 == 0b01111111100000000000000000000000
0b01111111100000000000000000000000 >> 1 == 0b00111111110000000000000000000000
0b00111111110000000000000000000000 >> 1 == 0b00011111111000000000000000000000
...
0b00000000000000000000000111111110 >> 1 == 0b00000000000000000000000011111111

最低位

让我们重写它们,所以两者都返回 LSB。

fn lsb_1(num: u32) -> u8 {
  (num & 0xFF) as u8
}
fn lsb_2(num: u32) -> u8 {
  (num << 24 >> 24) as u8
}

什么性能更高?

生锈 1.36 & opt-level=0

example::lsb_1:
        and     edi, 255
        mov     al, dil
        ret

example::lsb_2:
        push    rax
        shl     edi, 24
        mov     dword ptr [rsp + 4], edi
        mov     eax, dword ptr [rsp + 4]
        shr     eax, 24
        mov     dword ptr [rsp], eax
        mov     eax, dword ptr [rsp]
        mov     cl, al
        mov     al, cl
        pop     rcx
        ret

生锈 1.36.0 & opt-level=1

example::lsb_1:
        mov     eax, edi
        ret

example::lsb_2:
        mov     eax, edi
        ret

MSB

fn msb(num: u32) -> u8 {
  (num >> 24) as u8
}

生锈 1.36.0 & opt-level=0

example::msb:
        sub     rsp, 4
        shr     edi, 24
        mov     dword ptr [rsp], edi
        mov     eax, dword ptr [rsp]
        mov     cl, al
        mov     al, cl
        add     rsp, 4
        ret

生锈 1.36.0 & opt-level=1

example::msb:
        mov     eax, edi
        shr     eax, 24
        ret

结论

什么性能更高?查看编译器输出(程序集),查阅目标架构文档等。您使用的是什么编译器?哪个版本?你的目标架构是什么?换句话说 - 你的问题太宽泛了。

上面提到的Compiler Explorer是一个很好的工具,用于检查编译器输出是什么。

一般来说,你应该完成你的程序,让它工作,然后优化。你的程序会做很多其他的事情,它会包含更多的瓶颈。测量、优化、回滚、优化、测量、提交……但要使用最终产品,而不仅仅是简单的例程。您可以在过早的(因此无用的)优化上花费大量时间。然后你会发现你优化了一个例程,但是你的程序正在等待其他东西(网络,...),在其他地方有问题,...


推荐阅读