将8个连续字节转换为半字节的最快方法(以32位整数编码) [英] Fastest way to convert 8 consecutive bytes into nibbles (encoded in 32 bit integer)

查看:44
本文介绍了将8个连续字节转换为半字节的最快方法(以32位整数编码)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这些字节是无符号的,并且都小于16,因此可以将它们放入半字节.我目前正在循环移动字节,并使用 0xf :

The bytes are unsigned and are all less than 16 so they can be fit into a nibble. I'm currently shifting the bytes in a loop and & them with 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
}

编译器已经对此进行了一些优化:

The compiler does already some good optimization on that:

https://godbolt.org/z/NEpC64

但是也许可以进行一些调整或将SIMD命令与 u64 一起使用以减少操作量?

But maybe it is possible to do some bit twiddling or use SIMD commands with a u64 to reduce the amount of operations?

推荐答案

使用 bitintr 条板箱,您可以使用 pext :

With the bitintr crate you can use pext:

bitintr::bmi2::pext(x, 0x0f0f0f0f0f0f0f0f)

但是,这仅在Intel处理器上是快速的.AMD Ryzen实现了BMI2,但是它的 pext 非常慢.

However, that is only fast on Intel processors. AMD Ryzen implements BMI2, but its pext is very slow.

这里是仅普通代码的替代方法:

Here is an alternative with only normal code:

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

这篇关于将8个连续字节转换为半字节的最快方法(以32位整数编码)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆