8

タスク並列ライブラリを使い始めたばかりで、興味深い問題に遭遇しました。私は何が起こっているのかについての一般的な考えを持っていますが、何が起こっているのかを理解するのを助けるために私より有能な人々からのコメントを聞きたいです。やや長いコードについてお詫びします。

ランダムウォークの非並列シミュレーションから始めました。

 var random = new Random();
 Stopwatch stopwatch = new Stopwatch();

 stopwatch.Start();

 var simulations = new List<int>();
 for (var run = 0; run < 20; run++)
 {
    var position = 0;
    for (var step = 0; step < 10000000; step++)
    {
       if (random.Next(0, 2) == 0)
       {
          position--;
       }
       else
       {
          position++;
       }
    }

    Console.WriteLine(string.Format("Terminated run {0} at position {1}.", run, position));
    simulations.Add(position);
 }

 Console.WriteLine(string.Format("Average position: {0} .", simulations.Average()));
 stopwatch.Stop();

 Console.WriteLine(string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds));
 Console.ReadLine();

次に、並列ループでの最初の試みを書きました。

 var localRandom = new Random();

 stopwatch.Reset();
 stopwatch.Start();

 var parallelSimulations = new List<int>();
 Parallel.For(0, 20, run =>
 {
    var position = 0;
    for (var step = 0; step < 10000000; step++)
    {
       if (localRandom.Next(0, 2) == 0)
       {
          position--;
       }
       else
       {
          position++;
       }
    }

    Console.WriteLine(string.Format("Terminated run {0} at position {1}.", run, position));
    parallelSimulations.Add(position);
 });


 Console.WriteLine(string.Format("Average position: {0} .", parallelSimulations.Average()));
 stopwatch.Stop();

 Console.WriteLine(string.Format("Time elapsed: {0}", stopwatch.ElapsedMilliseconds));

 Console.ReadLine();

1コアのみを使用するように設定された仮想マシンで実行した場合、同様の期間が観察されましたが、実行は順番に処理されなくなりました。当然のことです。

デュアルコアマシンで実行したとき、状況は奇妙になりました。時間の改善は見られず、実行ごとに非常に奇妙な結果が見られました。ほとんどの実行の結果は-1,000,000(または非常に近い)になります。これは、Random.Nextが常に0準を返していることを示しています。

ランダムを各ループにローカルにすると、すべてが正常に機能し、期待される期間の改善が得られます。

Parallel.For(0, 20, run =>
         {
            var localRandom = new Random();
            var position = 0; 

私の推測では、問題はRandomオブジェクトがループ間で共有されており、何らかの状態があるという事実に関係していると思います。「失敗した並列」バージョンで期間が改善されていないのは、ランダムへの呼び出しが並列で処理されないためだと思います(並列バージョンは両方のコアを使用しますが、元のバージョンは使用しません) 。私が実際に得られない部分は、シミュレーション結果がそれらが何であるかという理由です。

私が抱えているもう1つの懸念は、各ループにローカルなランダムインスタンスを使用すると、同じシードで始まる複数のループが発生する可能性があることです(複数のランドムを時間的に近すぎる場合に発生する問題で、結果として同一になります)シーケンス)。

何が起こっているのかについての洞察は私にとって非常に貴重です!

4

3 に答える 3

2

これらのアプローチのどちらも、本当に良い乱数を与えることはありません。

このブログ投稿では、Randomを使用してより良い乱数を取得するための多くのアプローチについて説明しています。

http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx

これらは、多くの日常のアプリケーションに適している場合があります。

ただし、シードが異なっていても、複数のスレッドで同じ乱数ジェネレーターを使用すると、乱数の品質に影響を与えます。これは、重複する可能性のある疑似乱数のシーケンスを生成しているためです。

このビデオでは、その理由をもう少し詳しく説明します。

http://software.intel.com/en-us/videos/tim-mattson-use-and-abuse-of-random-numbers/

本当に乱数が必要な場合は、暗号乱数ジェネレーターSystem.Security.Cryptography.RNGCryptoServiceProviderを使用する必要があります。これはスレッドセーフです。

于 2010-06-25T03:15:24.283 に答える
2

Randomクラスはスレッドセーフではありません。複数のスレッドで使用すると、めちゃくちゃになる可能性があります。

各スレッドで個別のRandomインスタンスを作成し、それらが同じシードを使用しないようにする必要があります。(例: Environment.TickCount * Thread.CurrentThread.ManagedThreadId)

于 2010-05-27T20:26:24.480 に答える
1

核となる問題の 1 つ:

  • random.Nextスレッドセーフではありません。

2 つの影響:

  1. ランダム性の品質は競合状態によって破壊されます。
  2. 偽共有は、マルチコアでのスケーラビリティを破壊します。

考えられるいくつかの解決策:

  • スレッドrandom.Nextセーフにする: 品質の問題は解決しますが、スケーラビリティは解決しません。
  • 複数の PRNG を使用する: スケーラビリティの問題は解決しますが、品質が低下する可能性があります。
  • ...
于 2012-12-28T21:31:14.210 に答える