Я использую Vector128<byte>
в C # для подсчета совпадений из массива байтов с индексом 16.
Это часть реализации байтовой версии Микрооптимизация гистограммы с 4 сегментами большого массива или списка с использованием метода из Как подсчитать количество символов с помощью SIMD расширения 8-битных счетчиков до 64 во внешнем цикле (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: реальный вариант использования будет включать в цикл до 255 векторов в counts
, затем во внешнем цикле накапливаться в широкие элементы в sum64
с Sse2.SumAbsoluteDifferences
и Sse2.Add
и сбрасывать counts
. Эта часть выглядит правильно в этом порте C #, за исключением того, что sum64
не должен использовать ushort
элементы.
Часть, о которой задается этот вопрос, - это горизонтальная сумма двух 64-битных векторных элементов до одного скалярного целого числа. (В реальном варианте использования есть три вектора счетчиков из трех сегментов гистограммы; транспонирование и сумма может работать, но просто выполнение отдельных горизонтальных сумм для каждого вектора нормально.)
sum64
должен бытьVector128<uint>
илиVector128<Uint64>
. ИспользованиеSse2.Extract(sum64, 4);
(pextrw
) для добавления 16-битного фрагмента не сработает для вашего реального варианта использования, потому что количество может быть больше 65535. - person Peter Cordes   schedule 13.04.2020Vector128<uint> srcAsUInt32 = counts.AsUInt32();
- это должно бытьsum64.AsUInt32();
Вы использовали 8-битные сегменты счетчика, а не результатpsadbw
. - person Peter Cordes   schedule 13.04.2020Sse2.Extract(
может возвращать толькоushort
, а эта функция также может принимать толькоVector128<ushort>
в качестве параметра. Я не уверен, но кажется, что я застрял там, потому что мне нравится использовать, например,<uint>
, поэтому переполнение не может произойти. Из кода: отSse2.Shuffle(
доSse2.Extract(
Я даже не могу найти комбинацию типов, которая будет работать, потому что они принимают разные типы. - person Andreas   schedule 13.04.2020sum64.AsUInt32();
Да, конечно, это правда. Это должно быть то, что нужно пройти туда. Я тоже сделал ошибку. - person Andreas   schedule 13.04.2020Extract
. Используйте вектор, чтобы перемешать вектор и добавить. Тогда, конечно, есть какой-нибудь способ получить низкий элемент, кромеExtract
? В C ++ есть специальные встроенные функции для получения нижнего элемента в виде скаляра, кроме извлечения. В противном случае, в худшем случае, вы могли бы транспонировать и добавить дваsum64
вектора (из двух корзин) с помощью unpacklo / unpackhi (punpcklqdq
) для хранилища векторов в массивUint64
. Или, что еще хуже, просто сохраните в массиве tmp и перезагрузите как скаляр. - person Peter Cordes   schedule 13.04.2020Sse2.SumAbsoluteDifferences
должен возвращатьushort
, поэтомуsum64
- этоushort
. Но затем я использовалUnpack
вот так, и в messageBox я мог преобразовать в Scalar и получить числа3
и2
: ....Vector128<ushort> upper = Sse2.UnpackHigh(sum64, sum64); Vector128<ushort> lower = Sse2.UnpackLow(sum64, sum64); MessageBox.Show(upper.ToScalar() + ":" + lower.ToScalar());
Может ли это быть допустимое решение. Затем я передам3
и2
вInt64
счетчики, чтобы они не переполнялись, а затем сбрасывать их после каждой255
итерации? - person Andreas   schedule 13.04.2020Vector128<Uint64>
, а не для чередования 16-битныхushort
элементов. А можно сделать SIMD Sse2.Add вместо 2-х отдельныхToScalar()
. Но да,.ToScalar()
предположительно является внутренним элементом дляmovd / movq
, так что да, это правильный строительный блок вместо.Extract
(pextrw
). - person Peter Cordes   schedule 13.04.2020Sse2.UnpackLow(sum64, sum64)
бессмысленно. Низкий элемент уже находится внизу, где вы хотите. Иushort
распаковка просто создаст(2<<16) | 2
в младших 32 битах, когда она чередуетushort
элементы из младших половин двух векторов. Чтобы получить правильное перемешивание, вы должны использовать правильный метод для правильного вектора типа элемента с C #, в отличие от C ++, где все равно__m128i
, а размер элемента перемешивания является частью внутреннего имени. - person Peter Cordes   schedule 13.04.2020SumAbsoluteDifferences
к.AsUInt64()
, чтобы я мог полностью отказаться от нее и изменить все типы наUInt64
- person Andreas   schedule 13.04.2020Sse2.UnpackLow(sum64, sum64)
бессмысленно. Единственное, что я могу понять, это то, что я использую этот метод для получения значения из нижней полосы? (Я не уверен, изменился ли сценарий при использовании толькоUInt64
сейчас) - person Andreas   schedule 13.04.2020punpcklbw
- обратите внимание, что нижний элемент остается нижним элементом. Для 64-битных элементов вы просто дублируете нижний элемент в верхний при распаковке, но вы никогда не читаете этот высокий элемент SIMD. officedaytime.com/simd512e также содержит несколько диаграмм и категоризацию инструкций по их назначению. - person Peter Cordes   schedule 13.04.2020unpack
в ссылке там, и красный / низкий цвет элемента с самого начала остается низким. Но я не уверен, как вернуть меньшее значение без использования:Sse2.UnpackLow
. Должен ли я использовать какой-либо другой синтаксис дляsum64
, чтобы вернуть это значение? Я думаю, что здесь что-то не хватает, как это сделать. Возможно, мне следует использовать только:sum64.ToScalar()
, поскольку он возвращает2
, какой именно элемент является нижним? - person Andreas   schedule 13.04.2020Uint64
, чтобы не путаться с разными типами. - person Andreas   schedule 13.04.2020vec.ToScalar()
возвращает младший элемент вектора. docs.microsoft. com / en-us / dotnet / api / Для вектора Uint64 это младшие 64 бита. IDK, почему вы думали, что UnpackLow потребуется для использования ToScalar или получения желаемого значения, но это не так. - person Peter Cordes   schedule 13.04.2020