2011-01-07 9 views
7

Mi código utiliza MemoryStream para serializar/deserializar objetos a/desde la red. Me gustaría volver a utilizar un solo MemoryStream en mi clase, en lugar de crear uno nuevo cada vez que necesito para enviar algo por cable.desea volver a utilizar MemoryStream

¿Alguien sabe cómo hacer esto?

Fragmento de código:

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

Gracias!

+7

¿Cuál es el motivo para reutilizar las transmisiones? Suena como una forma bastante fácil de introducir memoryleaks y código difícil de depurar. –

+1

+1 en el comentario de @Richard. No haga esto a menos que haya perfilado el código y haya encontrado que se trata de un problema de rendimiento o memoria. –

+0

Gracias, chicos.Supongo que olvidé lo que alguien dijo una vez sobre la optimización prematura ...... – Jacko

Respuesta

8

En primer lugar el método de serialización tiene un error:

Tenga en cuenta que el búfer contiene bytes asignados que podrían no utilizarse. Por ejemplo, si la cadena "prueba" está escrita en el objeto MemoryStream, la longitud del buffer devuelto por GetBuffer es 256, no 4, con 252 bytes sin usar. Para obtener solo los datos en el búfer, use el método ToArray; sin embargo, ToArray crea una copia de los datos en la memoria.

es decir, los rendimientos de la matriz es mayor que los datos serializados

Para deserializar se puede construir una secuencia de memoria que utiliza la pasó en matriz, por lo que no serán asignar memorias intermedias internas. Pero a menos que tenga puntos de referencia que demuestren que la asignación de flujo de memoria es realmente un cuello de botella, no me molestaría.

Si realmente desea optimizar sus asignaciones de memoria necesitará reutilizar los buffers byte[]. Esto en particular significa modificar la API para que funcione con subsecciones de matrices, por lo que el tamaño del mensaje y el tamaño de la matriz no necesitan ser idénticos.

Los siguientes son detalles de implementación que pueden cambiar en cualquier momento (y ya podrían haber cambiado desde que leí sobre él):
Sin duda no vale la pena molestarse si los buffers no terminan en el montón de objetos grandes. Si los objetos son pequeños, se recogerán de forma económica en la próxima colección Gen0. El gran montón de objetos, por otro lado, termina directamente en Gen2. Los objetos AFAIR> 250kB se asignan allí.

Y, por supuesto, reutilizar los almacenamientos intermedios sin contraerlos puede ser una pérdida de memoria.

+0

+1 para la gran observación. – Aliostad

+0

¡Guau, gracias, CodeInChaos! Me preguntaba por qué estaba obteniendo arreglos de 256 bytes. – Jacko

12

Reutilizar el mismo MemoryStream no le da ningún beneficio de rendimiento.

Hay una razón por la cual MemoryStream no tiene una clara. Porque sería más costoso limpiarlo que crear uno nuevo.

Si observa las partes internas de la clase, puede ver que asigna un búfer, y al escribir, si su búfer se llena, asigna un búfer nuevo y copia los bytes existentes y luego continúa. Entonces, de alguna manera, el buffer es inmutable.

Esto se puede ver aquí en el ajuste de la capacidad que es llamada por EnsureCapacity() en el momento de la escritura:

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; 
     } 
    } 
} 
+3

'SetLength (0)' es efectivamente claro. Y supongo que ofrece beneficios de rendimiento (para buffers grandes) reutilizando el búfer. Pero probablemente aún no valga la pena. – CodesInChaos

+0

Gracias Aliostad. Tiene mucho sentido. – Jacko

Cuestiones relacionadas