2016-05-30 9 views
0

私は複数の次元で座標をハッシュするメソッドにスレッドサポートを追加しようとしています。ここでの長期目標は、FNV1Aハッシュを含めることですが、Hashメソッドの座標配列を初期化するとすぐに減速が表示されます。アレイの初期化がマルチスレッド化を遅らせているのはなぜですか?

私は100万回繰り返します.1回分のスレッドでは、約300msのストップウォッチ時間があります。 8スレッドの場合、〜6000msにバンプする。

これは間違った共有の問題ですか?もしそうなら、私の心配は、パディングとオフセットが配列に注入されるとハッシュが劣化することです。ローカル配列を使ってこれを実行するための助けがあれば、大歓迎です。どうもありがとう!

public class Foo : MonoBehaviour { 
    #region Fields 
    private readonly int iterations = 1000000; 
    private readonly int threadNum = 1; 
    private int iterationsCompleted = 0; 
    #endregion 

    void Start() { 
     Stopwatch stopWatch = new Stopwatch(); 
     stopWatch.Start(); 
     Multithread(); 
     stopWatch.Stop(); 
     UnityEngine.Debug.Log(stopWatch.Elapsed.TotalMilliseconds); 
    } 

    private void Multithread() { 
     for (int i = 0; i < threadNum; i++) { 
      Hash hash = new Hash(); 
      new Thread(() => { 
       while (Interlocked.Increment(ref iterationsCompleted) < iterations) { 
        hash.Get(0, 0, 0); 
       } 
       UnityEngine.Debug.Log("Finished thread"); 
      }).Start(); 
     } 
     while (iterationsCompleted < iterations); 
    } 
} 

public class Hash { 
    #region Fields 
    // FNV parameters can be found at http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param 
    private const uint _FNVPrime = 16777619; 
    private const uint _FNVOffset = 2166136261; 
    private const uint _FNVMask8 = (1<<8)-1; 
    #endregion 

    #region Class Methods 
    private static uint FNV1A(uint[] data) { 
     uint hash = _FNVOffset; 
     int dataSize = data.Length * sizeof(UInt32); 
     byte[] byteArray = new byte[dataSize]; 
     Buffer.BlockCopy(data, 0, byteArray, 0, dataSize); 
     for (int i = 0; i < dataSize; i++) { 
      hash = hash^byteArray[i]; 
      hash = hash * _FNVPrime; 
     } 
     return hash; 
    } 

    public uint Get(int x, int y, uint seed) { 
     uint[] data = new uint[3] { (uint)x, (uint)y, seed }; 
     //return FNV1A(data); 
     return 0; 
    } 
    #endregion 
} 
+0

私はまだ統一のスレッドを使用していなかったので、私はこれについてはよく分からないが、私はMonoBehaviour派生クラスが(それから継承されている以上の機能で)マルチスレッドに問題があると思います。 –

+0

新しい配列を割り当てるためのオーバーヘッドでなければなりません。 2つのオプションがあります:配列の代わりに固定量のintを使ってFNV1Aを使用するか、スレッドごとに1つずつ配列を事前に割り当てる。 –

+0

@TamasHegedus百万回の反復と1つのスレッドで、約300msのストップウォッチ時間を得ていたことに気付くと役に立ちます。 8にぶつけて〜6000msに時間を押しました。それは配列オーバーヘッドのようには見えません。その情報を投稿にも追加します。 –

答えて

1

ここではメモリの割り当てが問題になっているようです。私は両方の配列を作って、一度関数の外にメモリを割り当てました。は、別のThreadがそれらにアクセスしていないことを確認するためにアクセスされるたびに、両方の配列に両方とも配列されます。 lockキーワードを使用した後でも自分のコードがどれほど安全であるかわかりません。

なし関連する問題

while (iterationsCompleted < iterations);がメインThreadに呼ばれています。これはうまくいかず、Multithread()が呼び出され、まだ実行されているたびにUnityを一時的にフリーズすることができます。私はもう一度Threadを追加して、それが始まるとwhenMultithread()が呼び出されます。他のスレッドは、Threadの代わりにこの新しいThreadから呼び出されています。自分のコンピュータ上

テスト結果

オリジナルコード

1 thread = 454.3515 
8 threads = 655.008 

修正コード

1 thread = 296.794 
8 threads = 107.8898 

8スレッドのパフォーマンスがよりを改善10。 すべてのテストはコードに含まれているreturn FNV1A(data);で行い、コードからはreturn 0;を削除/コメントしました。

あなたの仕事は、あなたが得ているデータ/ハッシュがあなたが期待しているものであることを確認することです。

public class Foo : MonoBehaviour 
{ 
    #region Fields 
    private readonly int iterations = 1000000; 
    private readonly int threadNum = 1; 
    private int iterationsCompleted = 0; 
    #endregion 

    void Start() 
    { 
     Multithread(); 
    } 

    private void Multithread() 
    { 
     Stopwatch stopWatch = new Stopwatch(); 
     stopWatch.Start(); 

     //Start new Thread so that while (iterationsCompleted < iterations) ; will not run in the main loop 
     new Thread(() => 
      { 

       for (int i = 0; i < threadNum; i++) 
       { 
        Hash hash = new Hash(); 
        new Thread(() => 
        { 
         while (Interlocked.Increment(ref iterationsCompleted) < iterations) 
         { 
          hash.Get(0, 0, 0); 
         } 
         UnityEngine.Debug.Log("Finished thread"); 
        }).Start(); 
       } 
       while (iterationsCompleted < iterations) ; 
       stopWatch.Stop(); 
       UnityEngine.Debug.Log(stopWatch.Elapsed.TotalMilliseconds); 

      }).Start(); 
    } 
} 

public class Hash 
{ 
    #region Fields 
    // FNV parameters can be found at http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param 
    private const uint _FNVPrime = 16777619; 
    private const uint _FNVOffset = 2166136261; 
    private const uint _FNVMask8 = (1 << 8) - 1; 
    private const int FIXEDSIZE = 3; 
    private readonly System.Object locker = new System.Object(); 
    #endregion 

    #region Class Methods 
    private static uint FNV1A(uint[] data) 
    { 
     uint hash = _FNVOffset; 

     Buffer.BlockCopy(data, 0, byteArray, 0, byteArray.Length); 

     for (int i = 0; i < byteArray.Length; i++) 
     { 
      hash = hash^byteArray[i]; 
      hash = hash * _FNVPrime; 
     } 
     return hash; 
    } 

    static byte[] byteArray = new byte[FIXEDSIZE * sizeof(UInt32)]; 
    static uint[] data = new uint[3] { (uint)0, (uint)0, 0 }; 

    public uint Get(int x, int y, uint seed) 
    { 
     lock (locker) 
     { 
      data[0] = (uint)x; 
      data[1] = (uint)y; 
      data[2] = (uint)seed; 
      return FNV1A(data); 
     } 
    } 
    #endregion 
} 
関連する問題