Rust获取SIMD向量中

时间:2018-04-10 17:53:57

标签: 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 matches.extract_unchecked(i) {
            // Do something with the index
        }
    }
}

理想情况下,我只想为设定的索引“做某事”,而不是检查每一个(匹配数量会很少)。

有没有办法使用内在函数来获取匹配的索引,而不是遍历整个向量?以gcc为例,我可以使用_mm_movemask_epi8对数据进行比特包装,然后重复__builtin_clz的应用程序来获取第一个设置位的索引(这对于我将拥有的稀疏数字更有效) 。或者,我可以有一个查找表,在我的比特打包整数中为每个半字节做正确的事情(例如第一个答案here)。

生锈中是否有相当于这些说明?

我正在为英特尔x86-64处理器进行编译,并不需要跨平台支持。

注意:我更喜欢原生(安全)生锈的解决方案,但这并不是一项艰难的要求。我很好写不安全的生锈,甚至使用某种FFI链接到上述方法。

1 个答案:

答案 0 :(得分:1)

std::arch包含一套详尽的内在操作。这可以使用core::archstd::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;
    }
}