首页 > 解决方案 > 将 8 个连续字节转换为半字节的最快方法(以 32 位整数编码)

问题描述

这些字节是无符号的,并且都小于 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
}

编译器已经对此做了一些很好的优化:

https://godbolt.org/z/NEpC64

但也许可以做一些小操作或使用带有 a 的 SIMD 命令u64来减少操作量?

标签: rustbit-manipulation

解决方案


使用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

推荐阅读