Rust 将8个连续字节转换为半字节(以32位整数编码)的最快方法

Rust 将8个连续字节转换为半字节(以32位整数编码)的最快方法,rust,bit-manipulation,Rust,Bit Manipulation,字节是无符号的,并且都小于16,因此可以放入半字节。 我当前正在循环中移动字节,并使用0xf将它们移动到&: pub fn compress(offsets: [u8; 8]) -> u32 { let mut co: u32 = 0; for (i, o) in offsets.iter().enumerate() { co |= ((*o as u32) & 0xf ) << (i * 4); } co } pub

字节是无符号的,并且都小于16,因此可以放入半字节。 我当前正在循环中移动字节,并使用
0xf
将它们移动到
&

pub fn compress(offsets: [u8; 8]) -> u32 {
    let mut co: u32 = 0;

    for (i, o) in offsets.iter().enumerate() {
        co |= ((*o as u32) & 0xf ) << (i * 4);
    }
    co
}
pub-fn-compress(偏移量:[u8;8])->u32{
设mut-co:u32=0;
对于偏移量中的(i,o)。iter()枚举(){

co |=(*o作为u32)和0xf)使用
bitintr
板条箱,您可以使用
pext

bitintr::bmi2::pext(x, 0x0f0f0f0f0f0f0f0f)
然而,这只在英特尔处理器上运行得很快。AMD Ryzen实现了BMI2,但它的
pext
非常慢

以下是一个仅使用普通代码的替代方案:

pub fn compress(offsets: [u8; 8]) -> u32 {
    let mut x = u64::from_le_bytes(offsets);
    x = (x | (x >> 4)) & 0x00FF00FF00FF00FF;
    x = (x | (x >> 8)) & 0x0000FFFF0000FFFF;
    x = (x | (x >> 16));
    x as u32
}
步骤如下:

start:         0x0a0b0c0d0e0f0g0h
x | (x >> 4):  0x0aabbccddeeffggh
& mask:        0x00ab00cd00ef00gh
x | (x >> 8):  0x00ababcdcdefefgh
& mask:        0x0000abcd0000efgh
x | (x >> 16): 0x0000abcdabcdefgh
as u32:                0xabcdefgh

你的普通代码很漂亮,我喜欢看到比特从一个位置流向另一个位置的步骤的说明。这两个步骤中有一个比OP提供的更快吗?这个问题要求最快的解决方案,所以我希望看到一些基准测试来证明隐式断言,这些是快的(或者至少更快)@Shepmaster
pext
是Intel上的一条3周期指令,在出现更专门的指令之前基本上是无法匹敌的。但它对AMD来说是不好的。这也证明了存在最快的方法:各种方法的相对速度取决于硬件。因此,如果我们从字面上理解OPs标题,就没有答案了。谢谢,解决方案至少要短很多(关于操作码指令的数量)@PhilippMildenberger根据测试,它看起来像是一个基于掩码位的微码循环,每集位产生8µops,每集位额外花费约4.5个周期(也有一些开销,所以mask=0已经很慢了,可能会发生小数周期,因为这是一个倒数吞吐量)。我也不能测试它,但通过估计设置32位应该在145-150个周期左右。这比原始版本差十多倍。半字节可以交错
y=(x&0x0f0f);y |=(y>>28);
谢谢,很有趣,据我所知,结果字节顺序如下(大端):73625140不幸的是,我必须保留这些字节的顺序。这基本上是从未打包BCD转换为打包BCD。相关:,