Лучший способ скопировать ReadOnlySequence <byte>в Struct - PullRequest
1 голос
/ 10 января 2020

В последнее время я работаю с System.Buffers и, в частности, с классом * 1002. *

У меня есть структура примитивов, определенная следующим образом:

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

    }

, который создал тысячи маленьких байтовых [] массивов в течение жизни сервера (что само по себе не является проблемой), но со всем остальным система пытается Это немного увеличивает давление на G C.

Так что я перешел к использованию:

    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);

1 Ответ

2 голосов
/ 10 января 2020

Вы можете несколько уменьшить давление G C, используя 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];
}

Это может сделать его быстрее, но вам нужно будет применить его, чтобы увидеть, действительно ли это помогает.

...