2

所以我最近一直在System.BuffersReadOnlySequence<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循环和to MemoryMarshal.Cast()amessageHeader[1]这样我就可以提取元素 0 有点像我在阅读源代码时偶然发现的黑客行为。

我找不到一个 API 来干净地将内容提取ReadOnlySequence<bytes>messageHeader.

编辑1:

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

ros.CopyTo(workingSpace);
4

1 回答 1

3

stackalloc您可以通过在第一种方法中使用和Span<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];
}

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

于 2020-01-10T09:34:04.770 回答