https://docs.microsoft.com/en-us/dotnet/standard/numerics#simd-enabled-vector-types

ел несколько статей, описывающих, какVector<T> поддерживает SIMD и реализован с использованием встроенных функций JIT, поэтому компилятор будет правильно выводить инструкции AVS / SSE / ... при его использовании, что позволяет выполнять код намного быстрее, чем классические линейные циклы (примерВот).

Я решил попробовать переписать метод, который мне нужен, чтобы увидеть, удалось ли мне добиться некоторого ускорения, но пока что мне это не удалось, и векторизованный код работает в 3 раза медленнее, чем исходный, и я не совсем уверен, почему. Вот две версии метода проверки, если дваSpan<float> экземпляры имеют все пары элементов в одной и той же позиции, которые имеют одинаковую позицию относительно порогового значения.

// Classic implementation
public static unsafe bool MatchElementwiseThreshold(this Span<float> x1, Span<float> x2, float threshold)
{
    fixed (float* px1 = &x1.DangerousGetPinnableReference(), px2 = &x2.DangerousGetPinnableReference())
        for (int i = 0; i < x1.Length; i++)
            if (px1[i] > threshold != px2[i] > threshold)
                return false;
    return true;
}

// Vectorized
public static unsafe bool MatchElementwiseThresholdSIMD(this Span<float> x1, Span<float> x2, float threshold)
{
    // Setup the test vector
    int l = Vector<float>.Count;
    float* arr = stackalloc float[l];
    for (int i = 0; i < l; i++)
        arr[i] = threshold;
    Vector<float> cmp = Unsafe.Read<Vector<float>>(arr);
    fixed (float* px1 = &x1.DangerousGetPinnableReference(), px2 = &x2.DangerousGetPinnableReference())
    {
        // Iterate in chunks
        int
            div = x1.Length / l,
            mod = x1.Length % l,
            i = 0,
            offset = 0;
        for (; i < div; i += 1, offset += l)
        {
            Vector<float>
                v1 = Unsafe.Read<Vector<float>>(px1 + offset),
                v1cmp = Vector.GreaterThan<float>(v1, cmp),
                v2 = Unsafe.Read<Vector<float>>(px2 + offset),
                v2cmp = Vector.GreaterThan<float>(v2, cmp);
            float*
                pcmp1 = (float*)Unsafe.AsPointer(ref v1cmp),
                pcmp2 = (float*)Unsafe.AsPointer(ref v2cmp);
            for (int j = 0; j < l; j++)
                if (pcmp1[j] == 0 != (pcmp2[j] == 0))
                    return false;
        }

        // Test the remaining items, if any
        if (mod == 0) return true;
        for (i = x1.Length - mod; i < x1.Length; i++)
            if (px1[i] > threshold != px2[i] > threshold)
                return false;
    }
    return true;
}

Как я уже сказал, я протестировал обе версии, используя BenchmarkDotNet, и ту, которая используетVector<T> работает примерно в 3 раза медленнее, чем другой. Я попытался запустить тесты с разной длиной (от 100 до 2000), но векторизованный метод продолжает работать намного медленнее, чем другой.

Я что-то упускаю здесь очевидное?

Спасибо!

РЕДАКТИРОВАТЬ: причина, по которой я использую небезопасный код и пытаюсь максимально оптимизировать этот код без его распараллеливания, заключается в том, что этот метод уже вызывается изнутриParallel.For итерация.

Кроме того, возможность распараллеливания кода в нескольких потоках, как правило, не является хорошей причиной для того, чтобы не оптимизировать отдельные параллельные задачи.

Ответы на вопрос(2)

Ваш ответ на вопрос