хочу повторно использовать MemoryStream - PullRequest
7 голосов
/ 07 января 2011

Мой код использует MemoryStream для сериализации / десериализации объектов в / из сети. Я хотел бы повторно использовать один MemoryStream в своем классе, а не создавать новый каждый раз, когда мне нужно отправить что-то по телеграфу.

Кто-нибудь знает, как это сделать?

Фрагмент кода:

    // Serialize object to buffer
    public  byte[] Serialize(object value)
    {
        if (value == null)
            return null;
      MemoryStream _memoryStream = new MemoryStream();

        _memoryStream.Seek(0, 0);
        _bf.Serialize(_memoryStream, value);
        return _memoryStream.GetBuffer();
    }

    // Deserialize buffer to object
    public  object Deserialize(byte[] someBytes)
    {         
        if (someBytes == null)
            return null;
        MemoryStream _memoryStream = new MemoryStream();
        _memoryStream.Write(someBytes, 0, someBytes.Length);
        _memoryStream.Seek(0, 0);
        var de = _bf.Deserialize(_memoryStream);
        return de;
    }

Спасибо!

Ответы [ 2 ]

10 голосов
/ 07 января 2011

Повторное использование того же MemoryStream не дает вам никакого преимущества в производительности.

Существует причина, по которой MemoryStream не имеет четкого определения. Потому что очистить его будет дороже, чем создать новый.

Если вы посмотрите на внутренние компоненты класса, вы увидите, что он выделяет буфер, а при записи, если его буфер заполнен, он выделяет новый буфер и копирует существующие байты, а затем продолжает работу. Таким образом, в некотором смысле буфер является неизменным.

Это можно увидеть здесь при настройке емкости, которая вызывается EnsureCapacity() во время написания:

public virtual int Capacity
{
    get
    {
        if (!this._isOpen)
        {
            __Error.StreamIsClosed();
        }
        return (this._capacity - this._origin);
    }
    [SecuritySafeCritical]
    set
    {
        if (value < this.Length)
        {
            throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
        }
        if (!this._isOpen)
        {
            __Error.StreamIsClosed();
        }
        if (!this._expandable && (value != this.Capacity))
        {
            __Error.MemoryStreamNotExpandable();
        }
        if (this._expandable && (value != this._capacity))
        {
            if (value > 0)
            {
                byte[] dst = new byte[value];
                if (this._length > 0)
                {
                    Buffer.InternalBlockCopy(this._buffer, 0, dst, 0, this._length);
                }
                this._buffer = dst;
            }
            else
            {
                this._buffer = null;
            }
            this._capacity = value;
        }
    }
}
8 голосов
/ 07 января 2011

Прежде всего ваш метод сериализации содержит ошибку:

Обратите внимание, что буфер содержит выделенные байты, которые могут быть не использованы. Например, если строка «test» записана в объект MemoryStream, длина буфера, возвращаемого из GetBuffer, составляет 256, а не 4, при этом 252 байта не используются. Чтобы получить только данные в буфере, используйте метод ToArray; однако ToArray создает копию данных в памяти.

т.е. возвращаемые массивы больше, чем сериализованные данные

Для десериализации вы можете создать поток памяти, который использует переданный массив, поэтому он не будет выделять внутренние буферы. Но если у вас нет тестов, которые показывают, что распределение потока памяти является узким местом, я бы не стал беспокоиться.

Если вы действительно хотите оптимизировать распределение памяти, вам необходимо повторно использовать буферы byte[]. Это, в частности, означает изменение API для работы с подразделами массивов, чтобы размер сообщения и размер массива не обязательно были одинаковыми.

Ниже приведены подробности реализации, которые могут измениться в любое время (и, возможно, уже изменились с тех пор, как я об этом прочитал):
Конечно, не стоит беспокоиться, если буферы не окажутся в куче больших объектов. Если объекты маленькие, они будут дешево собраны в следующей коллекции Gen0. С другой стороны, куча больших объектов напрямую попадает в Gen2. Здесь размещены объекты AFAIR> 250 КБ.

И, конечно, повторное использование буферов без их сжатия может привести к утечке памяти.

...