将 ReadOnlySequence<byte> 复制到结构体的更好方法



所以我最近一直在与System.Buffers合作,特别是ReadOnlySequence<T>类。

我有一个原语结构,定义如下:

[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,这是我在阅读源代码时偶然发现的一个技巧。

我找不到一个 API 来干净地将ReadOnlySequence<bytes>的内容提取到messageHeader中,虽然我所拥有的正在工作,但我只是觉得应该存在一个......

编辑 1:

我只是偶然发现了BuffersExtensions.CopyTo<T>(ReadOnlySequence<T>, Span<T>)可以替换foreach循环

的。
ros.CopyTo(workingSpace);

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

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];
}

这可能会使它更快,但你必须检测它,看看它是否真的有帮助。

最新更新