2011-01-07 22 views
7

私のコードはMemoryStreamを使用して、ネットワークとの間でオブジェクトをシリアル化/逆シリアル化します。私は、ワイヤで何かを送るために が必要なたびに新しいクラスを作成するのではなく、クラス内で1つの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; 
    } 

感謝を!

+7

ストリームを再利用する理由は何ですか?これは、メモリリークを導入してコードをデバッグするのは非常に簡単な方法です。 @Richardのコメントに+1 –

+1

+1コードをプロファイリングして、これがパフォーマンスやメモリの問題であることが判明していない限り、これを行わないでください。 –

+0

ありがとう、みんな。私は誰かが早すぎる最適化について言ったことを忘れてしまったと思います...... – Jacko

答えて

8

最初にバグがあります。

バッファには未使用の割り当て済みバイトが含まれていることに注意してください。たとえば、文字列 "test"がMemoryStreamオブジェクトに書き込まれている場合、GetBufferから返されるバッファの長さは、4バイトではなく256バイトで、252バイトは未使用です。バッファ内のデータのみを取得するには、ToArrayメソッドを使用します。ただし、ToArrayはメモリ内にデータのコピーを作成します。

すなわちアレイ戻り、それが内部バッファを割り当てないようにあなたは、アレイ内の通過を使用するメモリストリームを構築することができるデシリアライズするためにシリアル化されたデータ

より大きい。しかし、メモリストリーム割り当てが本当にボトルネックであることを示すベンチマークがない限り、私は気にしません。

メモリ割り当てを本当に最適化したい場合は、byte[]のバッファを再利用する必要があります。これは、配列のサブセクションで動作するようにAPIを変更することを意味し、メッセージのサイズと配列のサイズが同じである必要はありません。

次はいつでも変更することができます実装の詳細です(と私はそれについて読んで以来、すでに変更されている場合があります):
バッファが大きなオブジェクトヒープ上に終了しない場合、それは確かに悩ま価値はありません。オブジェクトが小さい場合は、次のGen0コレクションで安価に収集されます。一方、大きなオブジェクトヒープはGen2で直接終了します。 AFAIRオブジェクト> 250kBがそこに割り当てられます。

もちろん、バッファを縮めずに再利用するとメモリリークが発生する可能性があります。

+0

+1大きな観察のために。 – Aliostad

+0

うわー、ありがとう、CodeInChaos !!なぜ256バイトの配列が得られたのだろうと思っていました。 – Jacko

12

再利用同じMemoryStreamを使用しても、パフォーマンス上の利点はありません。

MemoryStreamには明確な理由がありません。それは新しいものを作るよりも、それをクリアする方が高価になるからです。

クラスの内部を見ると、バッファが割り当てられていることがわかります。書き込み時にバッファがいっぱいになると、新しいバッファが割り当てられ、既存のバイトがコピーされてから実行されます。 したがって、バッファは不変です。

これは、執筆時点でEnsureCapacity()によって呼び出される容量の設定でここで見ることができます:すべてのSerializeメソッドの

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)'は事実上クリアです。バッファーを再利用することでパフォーマンスが向上する(大きなバッファーの場合)ことがわかります。しかし、おそらくまだ問題の価値がない。 – CodesInChaos

+0

Aliostadありがとうございました。多くの意味があります。 – Jacko

関連する問題