X86 Rust获取SIMD向量中的真字节索引

X86 Rust获取SIMD向量中的真字节索引,x86,rust,simd,intrinsics,X86,Rust,Simd,Intrinsics,我想比较两个16字节的向量,得到每个匹配的索引。一个小例子来说明我想要什么: fn get_matching_idx(arr1: &[u8], arr2: &[u8]) { let vec1 = u8x16::load_aligned(arr1); let vec2 = u8x16::load_aligned(arr2); let matches = vec1.eq(vec2); for i in 0..16 { if ma

我想比较两个16字节的向量,得到每个匹配的索引。一个小例子来说明我想要什么:

fn get_matching_idx(arr1: &[u8], arr2: &[u8]) {
    let vec1 = u8x16::load_aligned(arr1);    
    let vec2 = u8x16::load_aligned(arr2);
    let matches = vec1.eq(vec2);
    for i in 0..16 {
        if matches.extract_unchecked(i) {
            // Do something with the index
        }
    }
}
理想情况下,我只想对集合索引“做点什么”,而不是检查每一个索引(匹配的数量会很低)

有没有一种方法可以使用内部函数来获得匹配索引,而不是遍历整个向量?以gcc为例,我可以使用对向量进行位打包,然后重复应用
\uu builtin\u clz
,以获得第一个设置位的索引(这对于稀疏数字更有效,我会这样做)。或者,我可以有一个查找表,它为我的位压缩整数中的每个半字节做了正确的事情(例如,第一个答案)

是否有与这些防锈说明相同的说明

我编译的是英特尔x86-64处理器,不需要跨平台支持


注意:我更喜欢天然(安全)防锈的解决方案,但这不是一个硬性要求。我可以编写不安全的rust,甚至可以使用某种FFI链接到上述方法。

std::arch
包含一组详尽的内部操作。这可以使用
core::arch
std::simd
完成,如下所示:

use std::arch::x86_64::{self, __m128i};
use std::simd::{u8x16, FromBits};

unsafe fn get_matching_idx(arr1: &[u8], arr2: &[u8]) -> u32 {
    let vec1 = __m128i::from_bits(u8x16::load_aligned_unchecked(arr1));
    let vec2 = __m128i::from_bits(u8x16::load_aligned_unchecked(arr2));
    return x86_64::_mm_movemask_epi8(x86_64::_mm_cmpeq_epi8(vec1, vec2)) as u32;
}

fn main() {
    // let arr1 = ...
    // let arr2 = ...

    unsafe {
        let mut mask = get_matching_idx(arr1, arr2);
    }
    let mut delta_i = 0;
    // This assumes a little endian machine (note it counts trailing 0s)
    while group_mask > 0 {
        let tz = x86_64::_mm_tzcnt_32(mask);
        let i = tz + delta_i;
        // Do something...
        group_mask >>= tz + 1;
        delta_i += tz + 1;
    }
}

您可以通过
std::arch
:---在Rust中使用相同的内在特性。请注意,这是一个仅限夜间使用的API,但计划很快稳定下来。如果您需要在稳定的Rust上执行此操作,那么最简单的方法可能是在C中更正SIMD例程。为什么
core::arch
而不是
std::arch
?另外,为什么
\u-mm\u-tzcount\u 32
而不是呢?看起来,\u-mmtzcount\u 32只是调用汇编指令tzcntl,而u32::training\u-zeros做得更多(但如果我错了,请随意更正):更新为使用std::arch
training\u-zeros
检查了0参数;我不确定0参数的内在行为,我的经验是使用gcc intrinsic,它指定0参数导致未定义的行为。@MatthieuM
_mm_tzcnt_32
是(通过与
tzcnt
指令等效)为零输入定义的。GCC内置可能未指定0,因此它可以使用断开为零的指令(序列),例如最简单的使用
bsf
(输入为零时不会写入其目标)