Я играю с новой поддержкой аппаратных встроенных функций в .NET Core 3.0 в пространстве имен System.Runtime.Intrinsics.
У меня есть код, в котором я выполняю 4 операции XOR в цикле — ниже приведен упрощенный пример (я не писал это в IDE, поэтому не обращайте внимания на любые синтаксические ошибки:
private static unsafe ulong WyHashCore(byte[] array)
{
fixed (byte* pData = array)
{
byte* ptr = pData;
// Consume 32-byte chunks
for (int i = 0; i < array.Length; i += 32)
{
ulong a = Read64(ptr, i);
ulong b = Read64(ptr, i + 8);
ulong c = Read64(ptr, i + 16);
ulong d = Read64(ptr, i + 24);
// XOR them with some constants
ulong xor1 = a ^ SOME_CONSTANT1;
ulong xor2 = b ^ SOME_CONSTANT2;
ulong xor3 = c ^ SOME_CONSTANT3;
ulong xor4 = d ^ SOME_CONSTANT4;
// Use the resulting values
}
}
}
Метод Read64
выглядит так:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe ulong Read64(byte* ptr, int start)
=> *(ulong*)(ptr + start);
Я попытался заменить 4 строки XOR на:
byte[] array; // An array from elsewhere
private static unsafe ulong WyHashCore(byte[] array)
{
var bVector = Vector256.Create(SOME_CONSTANT1, SOME_CONSTANT2, SOME_CONSTANT3, SOME_CONSTANT4);
fixed (byte* pData = array)
{
byte* ptr = pData;
// Consume 32-byte chunks
for (int i = 0; i < array.Length; i += 32)
{
ulong a = Read64(ptr, i);
ulong b = Read64(ptr, i + 8);
ulong c = Read64(ptr, i + 16);
ulong d = Read64(ptr, i + 24);
// Create a 256-bit vector from the 4 64-bit integers
var aVector = Vector256.Create(a, b, c, d);
// XOR the 2 vectors
var res = Avx2.Xor(aVector, bVector);
// Get the resulting values out of the result vector
ulong xor1 = res.GetElement(0);
ulong xor2 = res.GetElement(1);
ulong xor3 = res.GetElement(2);
ulong xor4 = res.GetElement(3);
// Use the resulting values
}
}
}
Это дает ожидаемые результаты, но в 15 раз медленнее, чем простое умножение скаляров!
Я где-то ошибаюсь или неправильно использую SIMD?
** Обновление ** Я обновил код, чтобы использовать «правильные» способы загрузки и выгрузки данных в/из вектора, и теперь он примерно в 3,75 раза быстрее, чем скалярный код!
byte[] array; // An array from elsewhere
private static readonly Vector256<ulong> PrimeVector = Vector256.Create(SOME_CONSTANT1, SOME_CONSTANT2, SOME_CONSTANT3, SOME_CONSTANT4);
private static unsafe ulong WyHashCore(byte[] array)
{
// Create space on the stack to hold XOR results
var xorResult = stackalloc ulong[4];
fixed (byte* pData = array)
{
byte* ptr = pData;
// Consume 32-byte chunks
for (int i = 0; i < array.Length; i += 32)
{
// Create a 256-bit vector from the 4 64-bit integers
var vector = Avx.LoadVector256((ulong*)(ptr + i));
// XOR the 2 vectors
var res = Avx2.Xor(vector, PrimeVector);
// Store the resulting vector in memory
Avx2.Store(xorResult, res);
// Get the resulting values out of the result vector
var xor1 = *xorResult;
var xor2 = *(xorResult + 1);
var xor3 = *(xorResult + 2);
var xor4 = *(xorResult + 3);
// Use the resulting values
}
}
}
Vector256.Create(a, b, c, d)
. Тоже надо так хранить, я думаю. Эта поэлементная загрузка и сохранение имеют высокие накладные расходы. - person usr   schedule 08.05.2019Avx2.LoadVector256((ulong*)(ptr + p));
- это действительно намного быстрее, чем использованиеVector256.Create
! Это дает мне примерно паритет производительности со скалярной версией. Наверняка можно быстрее... - person Cocowalla   schedule 08.05.2019Vector
). Я создал суть, показывающую C#, IL и ASM, сгенерированные JIT. - person Cocowalla   schedule 08.05.2019ref
и, возможно,Unsafe.Add
для арифметики указателей. Таким образом, вы используете управляемые указатели, которые проще в использовании, чем неуправляемые указатели. Может быть, они также генерируют лучший код, кто знает. - person usr   schedule 08.05.2019Unsafe.Add
в основном так же с точки зрения производительности, как и небезопасные указатели. Я лично нахожу указатели менее подробными и более удобными для чтения, чем использованиеUnsafe
, но я думаю, что это дело вкуса. Есть методAvx.Store
- я его изучу! - person Cocowalla   schedule 08.05.2019Avx.Store
оказалось очень успешным — версия SIMD теперь в 3,75 раза быстрее, чем скалярная версия! Я напишу ответ с обновленным кодом - спасибо за помощь @usr :) - person Cocowalla   schedule 08.05.2019