将 ReadOnlySequence<byte> 复制到 Struct 的更好方法

A better way to copy a ReadOnlySequence<byte> to a Struct

所以我最近一直在使用 System.Buffers,特别是 ReadOnlySequence<T> class。

我有一个基元结构,定义如下:

[StructLayout(LayoutKind.Sequential,Pack =1,CharSet=CharSet.Unicode)]
public partial struct MessageHeader
{
    public MessageVersion Version;
    ...

我可以毫无问题地在网络上来回传递这个信息,我正在利用 System.IO.Pipelines 为此。

ReadOnlySequence<byte> 转换回结构引起了一些头痛。

我从这个开始:

    private void ExtractMessageHeaderOld(ReadOnlySequence<byte> ros, out MessageHeader messageHeader)
    {
        var x = ros.ToArray<byte>();

        ReadOnlySpan<MessageHeader> mhSpan = MemoryMarshal.Cast<byte, MessageHeader>(x);

        messageHeader = mhSpan[0];

    }

在服务器的整个生命周期中创建了数千个小字节[]数组,(这本身不是问题),但是系统正在尝试做的所有其他事情都会增加一些压力在 GC 上。

所以我开始使用:

    private void ExtractMessageHeader(ReadOnlySequence<byte> ros, out MessageHeader messageHeader, ref byte[] workingSpace)
    {
        var i = 0;
        foreach (var rom in ros)
            foreach (var b in rom.Span)
            {
                workingSpace[i] = b;
                i++;
            }

        ReadOnlySpan<MessageHeader> mhSpan = MemoryMarshal.Cast<byte, MessageHeader>(workingSpace);

        messageHeader = mhSpan[0];
    }

这不会进行任何内存分配,但我只是觉得应该有更好的方法。

foreach 循环,MemoryMarshal.Cast()messageHeader[1] 这样我就可以提取元素 0 是我在阅读源代码时偶然发现的一个 hack。

我找不到一个 API 来将 ReadOnlySequence<bytes> 的内容干净地提取到 messageHeader 中,而当我拥有的东西在工作时,我只是觉得应该存在...

编辑 1:

我刚刚偶然发现 BuffersExtensions.CopyTo<T>(ReadOnlySequence<T>, Span<T>) 可以用

替换 foreach 循环
ros.CopyTo(workingSpace);

您可以通过使用 stackallocSpan<byte> 在第一种方法中缓冲数据来稍微降低 GC 压力,如下所示:

public static void ExtractMessageHeaderOld(ReadOnlySequence<byte> ros, out MessageHeader messageHeader)
{
    Span<byte> stackSpan = stackalloc byte[(int)ros.Length];
    ros.CopyTo(stackSpan);

    ReadOnlySpan<MessageHeader> mhSpan = MemoryMarshal.Cast<byte, MessageHeader>(stackSpan);

    messageHeader = mhSpan[0];
}

这可能会使速度更快,但您必须对其进行检测以查看它是否真的有帮助。