用于替换值的内在 SIMD 指令

Intrinsics SIMD instruction to replace values

我想知道如何替换 Vector128<byte>

中的字节值

我认为假设下面的代码我们有一个 resultvector 是可以的 这些值: <0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0>

这里我想创建一个新向量,其中所有“0”都将替换为“2” 并且所有“1”将被替换为“0”,如下所示: <2,2,2,2,0,0,0,0,2,2,2,2,2,2,2,2>

我不确定这是否有内在函数或如何实现?

谢谢!

        //Create array
        byte[] array = new byte[16];
        for (int i = 0; i < 4; i++) { array[i] = 0; }
        for (int i = 4; i < 8; i++) { array[i] = 1; }
        for (int i = 8; i < 16; i++) { array[i] = 0; }


        fixed (byte* ptr = array)
        {
            byte* pointarray = &*((byte*)(ptr + 0)); 
            System.Runtime.Intrinsics.Vector128<byte> resultvector = System.Runtime.Intrinsics.X86.Avx.LoadVector128(&pointarray[0]);

            //<0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0>
            //resultvector
        }

该指令是 pshufb,在现代 .NET 中可作为 16 字节版本的 Avx2.Shuffle, and Ssse3.Shuffle 使用。两者都非常快,在现代 CPU 上有 1 个周期的延迟。

将您的源数据传递到洗牌控制掩码参数中,并将第一个参数的特殊值传递给正在洗牌的字节,如下所示:

// Create AVX vector with all zeros except the first byte in each 16-byte lane which is 2
static Vector256<byte> makeShufflingVector()
{
    Vector128<byte> res = Vector128<byte>.Zero;
    res = Sse2.Insert( res.AsInt16(), 2, 0 ).AsByte();
    return Vector256.Create( res, res );
}

有关详细信息,请参阅 this article 第 18 页的 _mm_shuffle_epi8 部分。

更新:如果你没有SSSE3,你可以在SSE2中做同样的事情,用2条指令代替1条指令:

static Vector128<byte> replaceZeros( Vector128<byte> src )
{
    src = Sse2.CompareEqual( src, Vector128<byte>.Zero );
    return Sse2.And( src, Vector128.Create( (byte)2 ) );
}

顺便说一下,.NET 中有一个 performance problem 可以防止编译器在循环外加载常量。如果您要在循环中调用该方法并希望最大限度地提高性能,请考虑传递两个常量向量(0 和 2)作为方法参数。