Внутренняя инструкция SIMD для замены значений - PullRequest
1 голос
/ 01 августа 2020

Интересно, как можно было бы заменить байтовые значения в 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
        }

1 Ответ

2 голосов
/ 01 августа 2020

Для этого используется инструкция pshufb, доступная в современной версии. NET как Avx2.Shuffle и Ssse3.Shuffle для 16-байтовой версии. Оба они действительно быстрые, задержка в 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 );
}

См. Раздел _mm_shuffle_epi8 на странице 18 из этой статьи для получения подробной информации.

Обновление: если у вас нет 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 ) );
}

Кстати, есть проблема с производительностью in. NET, которая не позволяет компилятору загружать константы вне циклов . Если вы собираетесь вызвать этот метод в al oop и хотите максимизировать производительность, подумайте о передаче обоих постоянных векторов с нулем и 2 в качестве параметров метода.

...