【发布时间】:2019-09-25 23:06:32
【问题描述】:
我正在使用 System.Runtime.Intrinsics 命名空间中的 .NET Core 3.0 对硬件内在函数的新支持。
我有一些代码在循环中执行 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)。另外,我认为必须像这样存储。这种元素加载和存储的开销很大。 -
@usr 我的目的是保持可读性,专注于手头的问题,但我可以看到细节如何相关 - 我已经更新了代码示例以更好地匹配现实
-
@usr 我刚刚尝试使用
Avx2.LoadVector256((ulong*)(ptr + p));在循环中加载向量——这确实比使用Vector256.Create快得多!这使我与标量版本的性能大致相当。当然它可以做得更快...... -
请始终发布您拥有的最佳代码。确保也将结果直接存储到内存中。另外,请张贴拆解清单。你肯定是在没有附加调试器的情况下以发布模式运行的,对吧?
-
使用
Avx.Store非常成功 - SIMD 版本现在比标量版本快 3.75 倍!我会用更新的代码写一个答案 - 感谢@usr 的帮助 :)
标签: c# .net-core simd intrinsics .net-core-3.0