所以我最近一直在与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);
您可以通过在第一种方法中使用stackalloc
和Span<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];
}
这可能会使它更快,但你必须检测它,看看它是否真的有帮助。