Я использую Vector128<byte>
в C# для подсчета совпадений из байтового массива с индексом 16.
Это часть реализации байтовой версии Микро Оптимизация гистограммы из 4 блоков большого массива или списка , используя технику из Как подсчитывать вхождения символов, используя SIMD расширения 8-битных счетчиков до 64 внутри внешнего l oop (hsum_epu8_epu64
вспомогательная функция), а затем после всех циклов суммирования этого вектора счетчиков до одного скаляра (hsum_epu64_scalar
).
Так что C ++ со встроенными функциями Intel должен быть перенесен на C# , А без AVX2 мы используем 128-битные целочисленные векторы, а не 256.
Массив байтов состоит из чисел 0
и 1
, где встречается 5 0
.
Теперь задача состоит в том, чтобы сосчитать те 5 0
, где мы можем видеть, что 2 из 0
находятся в верхней полосе Vector128<byte>
, а 3 из 0
- в нижней полосе Vector128<byte>
.
Я успешно прошел весь код до того места, где я Sse2.SumAbsoluteDifferences
, и могу извлечь число 0
для sumHigh
и sumLow
, показывающее 3 и 2 соответственно.
проблема начинается сейчас, когда мне нужно перетасовать так, чтобы верхний и нижний диапазоны поменялись местами, чтобы позже я мог извлечь противоположности в: sumHigh
и sumLow
для sum64b
Я поместил много комментариев в код, так что я думаю, что можно следовать коду и увидеть там также, как я пытаюсь перемешать и завершить код.
(Код также показывает, что мой процессор AMD K10 поддерживает: Sse, Sse2, Sse3)
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
private void button2_Click(object sender, EventArgs e)
{
//This shows what is supported on my processor. However it seems that I could use something from "Avx" anyway
bool avx = Avx.IsSupported; //false
bool avx2 = Avx2.IsSupported; //false
bool sse = Sse.IsSupported; //true
bool sse2 = Sse2.IsSupported; //true
bool sse3 = Sse3.IsSupported; //true
bool ssse3 = Ssse3.IsSupported; //false
bool sse41 = Sse41.IsSupported; //false
bool sse42 = Sse42.IsSupported; //false
//Create a bytearray of 16 indexes. As seen: '0' occur 2 times in the upper band and 3 times in the lower band
//We want to count those "0" in the below code
byte[] v1 = new byte[16];
v1[0] = 0; v1[1] = 0; v1[2] = 1; v1[3] = 1; v1[4] = 1; v1[5] = 1; v1[6] = 1; v1[7] = 1;
v1[8] = 1; v1[9] = 0; v1[10] = 0; v1[11] = 0; v1[12] = 1; v1[13] = 1; v1[14] = 1; v1[15] = 1;
Vector128<byte> counts = Vector128<byte>.Zero;
unsafe
{
fixed (byte* fixedInput = v1)
{
//Load byte Vector with 16 indexes
var v = Avx.LoadVector128(&fixedInput[0]);
//Now match how many "0" we can find in "Vector128: v". 'counts' show the result string where: '1' tells where we found: "0".
//As seen it happened as expected total times: 5 (2 times in the upper band and 3 times in the lower band of the Vector)
byte val = 0;
var match = Avx.CompareEqual(v, Vector128.Create(val));
counts = Avx.Subtract(counts, match); //counts: <1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0>
//Extract high/low bands
//So we use "SumAbsoluteDifferences" to "Separately sum the 8 low differences and 8 high differences to produce two unsigned word integer results."
//We can see on index 0: 2 and on index 4: 3
Vector128<ushort> sum64 = Vector128<ushort>.Zero;
sum64 = Sse2.Add(sum64, Sse2.SumAbsoluteDifferences(counts, Vector128<byte>.Zero)); //sum64: <2,0,0,0,3,0,0,0>
//I AM NOT SURE OF THE CODE BELOW HOW TO DO IT PROPERLY!
//Now I need to shuffle the above: "<2,0,0,0,3,0,0,0>" but are not sure of how the complete process is to do this correctly?
//Below is a start of an "attempt" but are not sure how to do this all the way correctly?
Vector128<uint> result = Sse2.Shuffle(sum64.AsUInt32(), 0xB1);
//Extract high/low bands from ther shuffle above?
//Vector128<uint> sum64b = Vector128<uint>.Zero;
//sum64b = Sse2.Add(sum64b, result);
//sumHigh = Sse2.Extract(sum64b, 1); //0
//sumLow = Sse2.Extract(sum64b, 0); //
}
}
}
Использование 16-битных извлечений будет возможно, но не пригодно для больших количеств.
var sumHigh = Sse2.Extract(sum64, 4); // pextrw
var sumLow = Sse2.Extract(sum64, 0); //sumHigh == 3 and sumLow == 2
var sumScalar = SumLow + sumHigh;
Примечание от @PeterCordes: реальный вариант использования будет l oop добавить до 255 векторов в counts
, затем во внешнем l oop накапливается в широкие элементы в sum64
с Sse2.SumAbsoluteDifferences
и Sse2.Add
и сбрасывается counts
. Эта часть выглядит правильно в этом порту C#, за исключением того, что sum64
не должен использовать ushort
элементов.
Часть, о которой этот вопрос задает, является горизонтальной суммой двух 64-битных векторов элементы вплоть до одного скалярного целого числа. (Реальный сценарий использования имеет три вектора отсчетов, из 3 сегментов гистограммы; транспонирование и сумма могут работать, но просто делать отдельные горизонтальные суммы для каждого вектора хорошо.)