不安全。从字节数组到ulong数组

时间:2017-03-09 12:10:52

标签: c# c#-7.0

我目前正在考虑移植我的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>

3 个答案:

答案 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