代码之家  ›  专栏  ›  技术社区  ›  senseiwa

用另一个字节替换一个字节

  •  0
  • senseiwa  · 技术社区  · 7 年前

    我发现为这个看似简单的问题创建代码有困难。

    给定一个压缩的8位整数,用另一个字节替换一个字节(如果存在)。

    例如,我想用 0x06 具有 0x01 ,所以我可以用 res 作为输入来查找 0x06 :

    // Bytes to be manipulated
    res = _mm_set_epi8(0x00, 0x03, 0x02, 0x06, 0x0F, 0x02, 0x02, 0x06, 0x0A, 0x03, 0x02, 0x06, 0x00, 0x00, 0x02, 0x06);
    
    // Target value and substitution
    val = _mm_set1_epi8(0x06);
    sub = _mm_set1_epi8(0x01);
    
    // Find the target
    sse = _mm_cmpeq_epi8(res, val);
    
    // Isolate target
    sse = _mm_and_si128(res, sse);
    
    // Isolate remaining bytes
    adj = _mm_andnot_si128(sse, res);
    

    现在我不知道如何继续 or 这两部分,我需要删除目标并用替换的字节替换它。

    我这里缺少什么SIMD指令?

    和其他问题一样,我只限于AVX,没有更好的处理器。

    1 回复  |  直到 7 年前
        1
  •  6
  •   chtz    7 年前

    您基本上需要做的是将(输入的)所有字节设置为零。然后将替换的所有其他字节设置为零和或结果。你已经有了一个面具 _mm_cmpeq_epi8 . 总的来说,可以这样做:

    __m128i mask = _mm_cmpeq_epi8(inp, val);
    return _mm_or_si128(_mm_and_si128(mask, sub), _mm_andnot_si128(mask, inp));
    

    由于and/andnot/or的最后一个组合非常常见,SSE4.1引入了一个指令,它(本质上)将这些组合成一个:

    __m128i mask = _mm_cmpeq_epi8(inp, val);
    return _mm_blendv_epi8(inp, sub, mask);
    

    事实上,clang5.0和更高版本在用优化编译时足够智能,可以用第二个变量替换第一个变量: https://godbolt.org/z/P-tcik


    注意:如果替换值实际上是 0x01 你可以利用这样一个事实:蒙版(比较的结果)是 0x00 0xff (这是 -0x01 ,也就是说,可以将要替换的值归零,然后减去遮罩:

    __m128i val = _mm_set1_epi8(0x06);
    __m128i mask = _mm_cmpeq_epi8(inp, val);
    return _mm_sub_epi8(_mm_andnot_si128(mask, inp), mask);
    

    这可以节省加载 0x01 来自内存的向量或为它浪费一个寄存器。根据您的体系结构,它的吞吐量可能稍好一些。

    推荐文章