我目前正在考虑移植我的metro hash implementon以使用C#7功能,因为有几个部分可能会从参考本地人那里获益以提高性能。
哈希在ulong[4]
数组上进行计算,但结果是16 byte
数组。目前我正在将ulong
数组复制到结果byte
缓冲区,但这需要一些时间。
所以我想知道System.Runtime.CompilerServices.Unsafe
在这里使用是否安全:
var result = new byte[16];
ulong[] state = Unsafe.As<byte[], ulong[]>(ref result);
ref var firstState = ref state[0];
ref var secondState = ref state[1];
ulong thirdState = 0;
ulong fourthState = 0;
上面的代码片段意味着我也使用结果缓冲区来进行状态计算的部分,而不仅仅是最终输出。
我的单元测试成功,根据benchmarkdotnet跳过块复制会导致 20%性能提升,这足以让我发现它是否正确使用它。< / p>
答案 0 :(得分:5)
按照当前的.NET术语,这非常适合Span<T>
:
Span<byte> result = new byte[16];
Span<ulong> state = MemoryMarshal.Cast<byte, ulong>(result);
这会强制执行长度等操作,同时具有良好的JIT行为,和不需要unsafe
。您甚至可以stackalloc
原始缓冲区(从C#7.2起):
Span<byte> result = stackalloc byte[16];
Span<ulong> state = MemoryMarshal.Cast<byte, ulong>(result);
请注意,Span<T>
的长度更改正确;如果要使用SIMD进行硬件加速,将其转换为Span<Vector<T>>
也很简单。
答案 1 :(得分:1)
C#支持&#34;固定缓冲区&#34;,这是我们可以做的事情:
public unsafe struct Bytes
{
public fixed byte bytes[16];
}
然后
public unsafe static Bytes Convert (long[] longs)
{
fixed (long * longs_ptr = longs)
return *((Bytes*)(longs_ptr));
}
试试吧。 (C#中基本类型的1D数组总是存储为连续的内存块,这就是为什么获取(托管)数组的地址很好)。
你甚至可以返回指针以获得更快的速度:
public unsafe static Bytes * Convert (long[] longs)
{
fixed (long * longs_ptr = longs)
return ((Bytes*)(longs_ptr));
}
并根据需要操作/访问字节。
var s = Convert(longs);
var b = s->bytes[0];
答案 2 :(得分:1)
您正在做的事情看起来还不错,请小心,因为没有什么可以阻止您这样做:
byte[] x = new byte[16];
long[] y = Unsafe.As<byte[], long[]>(ref x);
Console.WriteLine(y.Length); // still 16
for (int i = 0; i < y.Length; i++)
Console.WriteLine(y[i]); // reads random memory from your program, could cause crash