2017-02-09 11 views
2

私はこのテストに私の最初の問題を単純化しました。このクラスを使用してParallel forの内部を通常のforループより遅くします。どうして?

public class Unmanaged : IDisposable 
{ 
    private IntPtr unmanagedResource; 

    public Unmanaged() 
    { 
     this.unmanagedResource = Marshal.AllocHGlobal(10 * 1024 * 1024); 
    } 
    public void DoSomethingWithThisClass() 
    { 
     Console.WriteLine($"{DateTime.Now} - {this.unmanagedResource.ToInt64()}"); 
    } 

    private bool disposedValue = false; // To detect redundant calls 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposedValue) 
     { 
      Marshal.FreeHGlobal(unmanagedResource); 
      disposedValue = true; 
     } 
    } 

    ~Unmanaged() { 
     Dispose(false); 
    } 

    void IDisposable.Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

は、私はこれらの二つの試験があります。

public class UnitTest1 
{ 
    const int Runs = 100000; 

    [TestMethod] 
    public void UsingFor() 
    { 
     for (var i = 0; i <= Runs; i++) 
     { 
      using (var unman = new Unmanaged()) 
      { 
       unman.DoSomethingWithThisClass(); 
      } 
     } 
    } 

    [TestMethod] 
    public void UsingParallelFor() 
    { 
     Parallel.For(0, Runs, new ParallelOptions() { MaxDegreeOfParallelism = 10}, 
      index => { 
       using (var unman = new Unmanaged()) 
       { 
        unman.DoSomethingWithThisClass(); 
       } 
      }); 
    } 
} 

をParallelForは一般限りの正規の約2倍になります。プロファイラによると、実行時間の62%〜65%がParallelForのためにFreeHGlobal内部で費やされています。 FreeHGlobal内では、通常のためにわずか52%-53%が費やされています。

私は近代的なRAMシステムではこれであまり差がないと思っていました。複数のプロセスで大量の管理されていないメモリを処理する方法はありますか?これを変更してマルチスレッド化できる方法はありますか?

私が各プロセスで使用したRAMを廃棄しないでください(悪い考えでテストするだけです)、Parallel Forは2倍速いですが、その後約4〜5回しか開きません(大量ですあなたが推測したように、RAMの例外が発生している)と同時に、アプリケーションがクラッシュする前に同時に(つまり、画像データの)

別々のオブジェクトに複数のDisposeアクションを実行すると、処理が遅くなるのはなぜですか?

これが唯一のオプションであれば、私はそれらを1つのスレッドにしておくことができますが、これをスピードアップしたいと考えていました。

ありがとうございます。

+0

'Unmanaged'を' sealed'クラスにすると、 'Dispose()'を書くのは 'virtual Dispose(bool)'の必要がないので簡単です。 –

+1

AllocHGlobal()にはロックが組み込まれており、ヒープをスレッドセーフに保ちます。だからあなたが測定しているのは、ロックが保持されている時間です。他のスレッドもメモリを割り当てている間に必然的に時間がかかります。 –

+0

多くの管理されていないリソース( '10 * 1024 * 1024'はそのためのプロキシになります)を扱う場合、P/InvokeではなくC++/CLIを検討するかもしれません。 C++では、メモリを管理するためのツールをいくつか追加することもできます。 –

答えて

0

FreeHGlobalはほぼ確実にブロックします。これは、プロセス内の1つのスレッドだけが一度に実行できることを意味します。彼らは並んで待ちます。それにはオーバーヘッドがあるので、遅いです。

単一の大きなブロックのアンマネージメモリを作成し、ロックされていないアロケータを実行すると、高速化できます。

+1

私は内部のロックがあることを認識しませんでした。それで私は問題に別の方法で取り組まなければなりません。 私は約4個を一度にRAMに入れるのに十分な大きさのメモリブロックを作成するというあなたの考えが好きです。次に、私は待ち行列で処理する必要があるジョブをセットアップし、コントローラにRAMワークエリアの異なるチャンクにプロセスを分割させることができます。キューから別のタスクを開始する前に、前のプロセスで使用したセクションをクリアすることができます。これはもう少しコーディングのオーバーヘッドですが、これらで実行する必要がある処理量に時間を節約するはずです。 –

+0

あなたは確かに私の経験の中で正しい道をたどっています。あなたがロックせずにそれを行うことができるかもしれない方法について考えてみるか、代わりに素晴らしいメモリプールを見てください。 – hoodaticus

関連する問題