2011-01-07 16 views
7

Mój kod wykorzystuje MemoryStream do serializowania/deserializowania obiektów do/z sieci. Chciałbym ponownie użyć pojedynczego MemoryStream w mojej klasie, zamiast tworzyć nowy za każdym razem, gdy potrzebuję , aby wysłać coś przez przewód.Chcę ponownie użyć MemoryStream

Czy ktoś wie, jak to zrobić?

Fragment kodu:

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

Dzięki!

+7

Jaki jest powód ponownego wykorzystywania strumieni? To brzmi jak całkiem prosty sposób na wprowadzenie pamięci i trudny do debugowania kod. –

+1

+1 z komentarza @Richard. Nie rób tego, chyba że sprofilowałeś kod i okazało się, że jest to problem z wydajnością lub pamięcią. –

+0

Dzięki, chłopaki.Przypuszczam, że zapomniałem, co ktoś powiedział o przedwczesnej optymalizacji ...... – Jacko

Odpowiedz

8

przede wszystkim metodę serializacji ma błąd:

Zauważ, że bufor zawiera przydzielone bajty, które mogą być nieużywane. Na przykład, jeśli ciąg "test" jest zapisywany w obiekcie MemoryStream, długość bufora zwróconego przez GetBuffer wynosi 256, a nie 4, z 252 bajtami nieużywanymi. Aby uzyskać tylko dane w buforze, użyj metody ToArray; jednak ToArray tworzy kopię danych w pamięci.

to znaczy powraca tablicy jest większe niż dane odcinkach

Na deserializacji można tworzyć strumień pamięci, która wykorzystuje przekazany w szereg tak, że nie będzie przeznaczyć buforów wewnętrznych. Ale jeśli nie masz benchmarków, które pokazują, że przydzielanie strumieni pamięci jest naprawdę wąskie gardło, nie zawracałbym sobie głowy.

Jeśli naprawdę chcesz zoptymalizować przydzielanie pamięci, musisz ponownie użyć buforów byte[]. W szczególności oznacza to modyfikowanie api do pracy z podsekami tablic, więc rozmiar wiadomości i rozmiar tablicy nie muszą być identyczne.

Poniżej znajdują się szczegóły dotyczące implementacji, które mogą ulec zmianie w dowolnym momencie (i mogły już ulec zmianie, ponieważ czytałem o tym):
Z pewnością nie warto przejmować się, jeśli bufory nie znajdą się na stosie dużych obiektów. Jeśli obiekty są małe, zostaną one tanio zebrane w następnej kolekcji Gen0. Z drugiej strony stertę dużych obiektów trafia bezpośrednio do Gen2. Obiekty AFAIR> 250kB są tam przydzielane.

Oczywiście ponowne wykorzystanie buforów bez ich zmniejszania może być przeciekiem pamięci.

+0

+1 za wspaniałą obserwację. – Aliostad

+0

Wow, dzięki, CodeInChaos !! Zastanawiam się, dlaczego dostaję 256 tablic bajtowych. – Jacko

12

Ponowne użycie tego samego MemoryStream nie daje żadnych korzyści z wydajności.

Jest powód, dla którego MemoryStream nie ma wyraźnego. Ponieważ wyczyszczenie go byłoby droższe niż stworzenie nowego.

Jeśli przyjrzeć się obiektom wewnętrznym klasy, można zauważyć, że przydziela bufor, a podczas zapisywania, jeśli jego bufor jest pełny, przydziela nowy bufor i kopiuje istniejące bajty, a następnie wykonuje. W pewnym sensie bufor jest niezmienny.

Widać tutaj, w otoczeniu pojemności który jest nazywany przez EnsureCapacity() w czasie pisania:

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)' jest wyraźnie jasne. I przypuszczam, że oferuje korzyści w zakresie wydajności (w przypadku dużych buforów) poprzez ponowne użycie bufora. Ale prawdopodobnie nadal nie warte problemów. – CodesInChaos

+0

Dziękuję Aliostad. ma wiele sensu. – Jacko

Powiązane problemy