2012-01-03 28 views
6

私は数日間今私を悩ませている問題があります。私はGoogleに問題を解決しようとしましたが、これまで同じ問題を抱えている人はいませんでした。C#System.Buffer.BlockCopyメモリの問題?

C#のSystem.Buffer.BlockCopyメソッドは、ある種のメモリゴーストを残しているようです。私は例えばこのメソッドを持っています:

private float[,] readFloatArray2 (byte[] b) { 
    int floatSize = sizeof(float); 
    float[,] v = new float[2, (b.Length/2)/floatSize]; 
    System.Buffer.BlockCopy(b, 0, v, 0, b.Length); 

    return v; 
} 

バイト配列を2D浮動小数点配列に変換します。データはあらかじめストリームから読み込まれています。 問題は、System.Buffer.BlockCopyメソッドに位置しています。

BlockCopyコマンドを削除した場合、アプリケーションで使用されるメモリのサイズは半分になります。これは、バイト配列がまだ生きているという私のせいではないということです。 BlockCopyコマンドを使用しないと、バイト配列が正常に終了しないためです。とにかくフロート配列が作成されます(コピーされた情報の有無にかかわらず)。

BlockCopyコマンドまたはGCの問題であるかどうかはまだ分かりませんが、System.GC.Collect(); BlockCopyの後に、それも完全に機能します(私はあなたがこれをしてはいけないことを知っています...なぜ私がここで助言を求めているのか)。

私はまた質問しても構いませんが、問題には数百メガが含まれます。

メモリの問題の他に、このメソッドは完全に正常に機能します。誰でもメモリの問題の原因を知っていますか?事前に

挨拶と感謝 OLI

PS:私は、Visual Studio 2010 PROとWIN7で.NET4.0を使用していますが...これが関連しているかどうかわかりません。

+2

メモリが 'GC.Collect'によって適切に収集されているとすれば、すべて正常です。 'b' *は最終的に時が来ると通常のGCによって収集されます。 –

+0

オーディオデータを扱っているなら、 'float [len] [channelCount]'の形式のギザギザの配列を使います。そうすることで、チャンネルを別々に扱うことができます。これは便利なこともあります。 – CodesInChaos

+1

"とにかくフロート配列が作成されます。それは必ずしも物理メモリを必要としません。メモリページはすべて0であり、書き込まれていないメモリはWindowsメモリマネージャによって最適化されます。 – CodesInChaos

答えて

1

BlockCopyは、.NETの実装を管理していません。内部的には、外部のwin apiを呼び出します。

[SecuritySafeCritical] 
public static extern void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count); 
+0

ok ...この問題に対処する方法は?何を指示してるんですか? –

+0

何もありません。 GCは余分なメモリが必要なときに最終的に世話をします。 –

1

Buffer.BlockCopyは、インデックスベースではなくバイトベースです。私は基本的に同じことを行うArray.Copyを使うことをお勧めします。 BlockCopyはちょっと速いです。

私はSystem.Buffer.BlockCopy方法であることが問題の場所に位置していること

static float[] ConvertByteArrayToFloat(byte[] bytes) 
{ 
    if(bytes == null) 
     throw new ArgumentNullException("bytes"); 

    if(bytes.Length % 4 != 0) 
     throw new ArgumentException 
       ("bytes does not represent a sequence of floats"); 

    return Enumerable.Range(0, bytes.Length/4) 
        .Select(i => BitConverter.ToSingle(bytes, i * 4)) 
        .ToArray(); 
} 
+0

しかし、Array.Copyは異なるデータ型では動作しません。私はそれをバイト[]からフロート[、]にコピーすることはできません。 –

+0

私は答えを –

+0

yeah ... ok ... thatsクールだが、私は2D float配列(float [、])に1Dのバイト配列を持っています。しかし、あなたの提案はクールです。私はこれを知らなかった。 (C#で4ヶ月間コーディングしています) –

2

については、下記の[] first.Have外観をfloatに[]バイトを変換する必要があります。 BlockCopyコマンドを削除すると、アプリケーションで使用されるメモリのサイズが半分になります。これは、バイト配列がまだ生きているという私のせいではないということです。 BlockCopyコマンドを使用しないと、バイト配列が正しく終了しないためです。

私はその結論に同意しません。

  1. バイト配列が存在し、データ
  2. あなたはfloat配列を割り当てるで満たされているが、それはまだデータで満たされていない:私はいくつかの段階を参照してください。
  3. あなたはデータ
  4. バイト配列は、もはや参照されていませんが、バイト配列が収集されたまだ
  5. 収集されていないとのfloat配列を埋めます。

バイト配列のライブは、BlockCopyの影響を受けません。

ステップ2は仮想メモリを予約し、コミットします。したがって、コミット・サイズはこのステップで大きくなります。しかし、配列の内容は一度も書き込みされておらず、完全に00バイトで構成されているため、Windowsメモリマネージャはそれに対して物理メモリを割り当てません。これらのページはすべて00で構成されています。

フロート配列の物理メモリは手順3でのみ割り当てられます。アレイのすべてのフィールドを初期化するループを追加する場合も同じ効果が得られます。別に実際の問題から


、私はまた、いくつかの設計提案を持っている:

  1. リユース・バッファを。 GCは小さな寿命の短いオブジェクトには適していますが、長い寿命の長いオブジェクトでは非常に悪いです。つまり、大きな配列を割り当てたり返す関数を使用しないでください。代わりにそれらをパラメータとして取るので、既存の配列を再利用することができます。
  2. もしあなたがオーディオデータを使って作業しているのであれば、私はソリッド2D配列を使用しません。代わりに配列の配列を使用したいと思います。内側の配列は単一のバッファ内のサンプルを表し、外側の配列はバッファを表します。これには2つの利点があります。

    • 単一チャネルでしか動作しないコードを簡単に書くことができます。
    • 固体2Dアレイのインデックス作成が遅いため、しばしば高速です。
  3. 実際にすべてのデータを一度に読みたいですか?私は数キロバイトの塊を読みました。
+0

私はバイオインフォメーションですので、オーディオファイルでは動作しません。 MSスペクトルとよく似ています。私は処理のために全データが必要なので、設計上の提案1.と2.はオプションではありません(ステップ1はオプションではありません。読み込まれたバイトの長さが32バイトから10.000.000まで劇的に異なるためです)。 デザインの提案3は、異なる側面を強調するためにデータの異なる部分を複数回処理する必要があるため、オプションでもありません。したがって、処理の最後まですべての情報が必要であるため、オンザフライですべてをリロードすることは不可能です。 しかし、とにかく感謝:) –

+0

ああ...と提案3に、私は一度にすべてを読んでいない。私はスペクトルを読みました。また、1つのスペクトルサイズは32Bから10MBを超えることがあります。そして私は約80,000のスペクトルを読んでいます。それらのすべてが処理に必要です。 –

1

私はまたSystem.GC.Collect()を呼び出すようにしようとしたので、これはBlockCopyコマンドの問題やGCであればかなりわかりません。 BlockCopyの後に、それも完全に機能します(私はあなたがこれをしてはいけないことを知っています...なぜ私がここで助言を求めているのか)。私たちが話している数冊のMBほどではないかと尋ねることもありません。

特定の世代のためにさらに多くのメモリが必要な場合、またはLOHからガーベジコレクションが実行されます。原則として、収集するゴミがあるだけでガベージコレクションは実行されません。これは大変良いことです(実際には使用していないギガバイトのメモリを公式に "使用中" GCが必要なときにGCが取得できる限り)。

実際のプログラムではGC.Collect()を呼び出すことは意味がありますが、これもその1つである可能性があります。その場合、「完全に機能する」とすれば、コードの99.9%を練習してください。それが「ベストプラクティス」である理由は、厳密ではなく、時には0.1%のケースにあることが多いため、通常はベストプラクティスはもはやベストプラクティスではありません。

また、配列の最大サイズ(または元のバイト配列のみのもの)を事前に予測できる場合は、CodeInChaosの最初のアプローチが有効です。実際には10,000,000バイトを使用している間は、10,000,000バイトを使用して32を処理することに実際に傷つくことはありません。その10,000,000を再利用することは、プロセスの寿命にわたって非常に実質的な節約になります。