Самый быстрый способ узнать, присутствует ли «ushort» в пределах диапазона <ushort>с SIMD? - PullRequest
0 голосов
/ 16 ноября 2018

В C # на .NET Core я ищу самый быстрый способ проверить, присутствует ли заданное значение ushort в диапазоне Span<ushort>. Наивный вариант состоит в перечислении диапазона, но я сильно подозреваю, что намного более быстрый вариант с одним ядром существует через SIMD (то есть SSE или AVX).

Какой самый быстрый вариант здесь? (небезопасный код в порядке)

1 Ответ

0 голосов
/ 17 ноября 2018

Базовая реализация (до применения оптимизаций, таких как описанные Питером в комментариях) может работать следующим образом:

static unsafe bool ContainsUshort(Span<ushort> data, ushort val)
{
    int vecSize = Vector<ushort>.Count;
    var value = new Vector<ushort>(val);
    int i;
    fixed (ushort* ptr = &data[0])
    {
        int limit = data.Length - vecSize;
        for (i = 0; i <= limit; i += vecSize)
        {
            var d = Unsafe.ReadUnaligned<Vector<ushort>>(ptr + i);
            if (Vector.EqualsAny(d, value))
                return true;
        }
    }
    for (; i < data.Length; i++)
    {
        if (data[i] == val)
            return true;
    }
    return false;
}

Для этого требуется пакет System.Runtime.CompilerServices.Unsafe для небезопасного чтения, без созданиявектор из диапазона (или массива тоже) гораздо менее эффективен.Кстати, встроенная EqualsAny реализована с (v)ptest вместо (v)pmovmskb, ptest обычно стоит больше мопов, поэтому сравнительно важнее минимизировать его влияние - но поскольку прямого доступа к ptest нет илиpmovmskb окончательный "вектор к условию" AFAIK все еще должен быть сделан с Vector.EqualsAny (с вектором, заполненным 0xFFFF), что немного глупо ... тем не менее это было немного быстрее на моей машине (проверено так, что возвращениезначение будет false, поэтому более ранний выход не развернутой версии не вступил в игру)

var allSet = new Vector<ushort>(0xFFFF);
int limit = data.Length - vecSize * 2;
for (i = 0; i <= limit; i += vecSize * 2)
{
    var d0 = Unsafe.ReadUnaligned<Vector<ushort>>(ptr + i);
    var d1 = Unsafe.ReadUnaligned<Vector<ushort>>(ptr + i + vecSize);
    var eq = Vector.Equals(d0, value) | Vector.Equals(d1, value);
    if (Vector.EqualsAny(eq, allSet))
        return true;
}
...