rust - 为什么我们使用掩码来切割字节?
问题描述
所以我想这个很愚蠢,但我自己想不出答案。
几天前,我不得不创建一个返回无符号整数的强字节的函数。在网上查了一些资料后,我想到了这个:
fn strong_byte(num: u32) -> u8 {
((num >> 24) & 0xFF) as u8
}
但是又找了一会儿,我也找到了这个:
fn strong_byte(num: u32) -> u8 {
(num << 24 >> 24) as u8
}
所以我想知道哪种形式的性能更高?我试图找到显示左移与位掩码性能的基准,但没有找到任何东西......
我知道第一种语法是迄今为止最常用的,但我不明白为什么第二种不是......
解决方案
积分也适用于其他人,请参阅问题下方的评论。
术语
什么是强字节?您应该使用最低有效字节(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 和返回_2
LSB。如果你想比较它们,你必须修复它们。
实现也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是一个很好的工具,用于检查编译器输出是什么。
一般来说,你应该完成你的程序,让它工作,然后优化。你的程序会做很多其他的事情,它会包含更多的瓶颈。测量、优化、回滚、优化、测量、提交……但要使用最终产品,而不仅仅是简单的例程。您可以在过早的(因此无用的)优化上花费大量时间。然后你会发现你优化了一个例程,但是你的程序正在等待其他东西(网络,...),在其他地方有问题,...