C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

マルチスレッドで配列でも同期が必要ですか?

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■85877 / inTopicNo.1)  マルチスレッドで配列でも同期が必要ですか?
  
□投稿者/ コング (1回)-(2017/11/30(Thu) 09:52:14)

分類:[.NET 全般] 

2017/11/30(Thu) 09:52:46 編集(投稿者)
マルチスレッドで同じ変数に対して
アクセスする場合には、SyncLockによる同期が必要なのは知っていたのですが、
配列の場合にもこれは必要なのでしょうか?

例えば、以下のコードを走らせてみると、
sumには1000000の3/5程度の
600000〜7000000程度の値が代入されます。

一方で、SyncLock ready_diffのコメントアウトを外すと
1000000の値が代入されます。

aaaという変数に同時に+1しようとすると+2ではなく+1しか足し算されないというのは理解できますが
qwe(3,5,6)とqwe(8,8,8)など同じ配列であっても
違う要素に足し算するのであれば、
同時に実行できるのではないかとおもっていたのですが
これはできないのでしょうか?


同期を有りにすると、著しく計算速度が遅くなるので
避けたいのですが、これは仕方のないことなのでしょうか?



        Dim qwe(255, 255, 255) As Integer

        Parallel.For(2, 1000000, 
                     Sub(i)

                         ' SyncLock ready_diff

                         Dim cRandom As New System.Random()

                         Dim x = cRandom.Next(1, 255)
                         Dim y = cRandom.Next(1, 255)
                         Dim z = cRandom.Next(1, 255)


                         qwe(x, y, z) += 1


                         '  End SyncLock

                     End Sub)


        Dim sum As Long = 0

        For x = 1 To 255

            For y = 1 To 255

                For z = 1 To 255

                    sum += qwe(x, y, z)

                Next

            Next
        Next


引用返信 編集キー/
■85880 / inTopicNo.2)  Re[1]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ furu (139回)-(2017/11/30(Thu) 10:36:52)
No85877 (コング さん) に返信

詳しい人いると思うけど

あるコアで一つのIntegerを更新したら
近所のIntegerもついでにL1キャッシュに持ってくる。

ランダムにアクセスしているようでも
同じメモリのデータが複数のコアのL1キャッシュに
持っちゃてることはよく起きるんじゃないでしょうか?
引用返信 編集キー/
■85889 / inTopicNo.3)  Re[1]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ カテキン (1回)-(2017/11/30(Thu) 11:56:21)
New System.Random()で生成される時のseedは時間で決定されるので
同一seedの乱数生成でx,y,zが同じ値になり、同じ空間に同時にアクセスする
という事象になっていたりしないでしょうか?

試しに
Dim cRandom As New System.Random(i)
とループカウンタをseedとした場合、うまくいってるような感じでした

ただ、これでも同時に同一空間をアクセスしないという保証が出来ていない
と思いますので、完全ではないと思います。
引用返信 編集キー/
■85893 / inTopicNo.4)  Re[2]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ コング (2回)-(2017/11/30(Thu) 12:34:31)
ありがとうございます。
単に、私のテストプログラムがおかしいのが原因でしたか

同一空間というのは、配列内の同一要素という意味でしょうか?

 
引用返信 編集キー/
■85898 / inTopicNo.5)  Re[3]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ カテキン (2回)-(2017/11/30(Thu) 13:21:15)
No85893 (コング さん) に返信
> ありがとうございます。
> 単に、私のテストプログラムがおかしいのが原因でしたか
>
> 同一空間というのは、配列内の同一要素という意味でしょうか?
>

はい、同一要素のことです。
下記コードのようにSystem.Randomの生成がほぼ同時に行われていることでseedが同じになります。
これと同じようにループ内で同一seedでの生成が行われ、x,y,zが同じ値となり、同一要素への
加算処理がほぼ同時に行われていると思います。


Dim rnd1 As New System.Random()
Dim rnd2 As New System.Random()
Dim x As Integer, y As Integer, z As Integer

x = rnd1.Next(0, 255)
y = rnd1.Next(0, 255)
z = rnd1.Next(0, 255)

Trace.WriteLine(String.Format("x={0}:y={1}:z={2}", x, y, z))

x = rnd2.Next(0, 255)
y = rnd2.Next(0, 255)
z = rnd2.Next(0, 255)

Trace.WriteLine(String.Format("x={0}:y={1}:z={2}", x, y, z))

引用返信 編集キー/
■85899 / inTopicNo.6)  Re[4]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ コング (4回)-(2017/11/30(Thu) 13:23:02)
ありがとうございます。

解決しました。
 
解決済み
引用返信 編集キー/
■85917 / inTopicNo.7)  Re[5]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ なちゃ (230回)-(2017/12/01(Fri) 12:12:18)
No85899 (コング さん) に返信
> ありがとうございます。
>
> 解決しました。
>  

念のためですが、うまく行くかは確率の問題なので、失敗する可能性もあることは念頭に置いておいてください。
また、やらないとは思いますがCPUの種類によっては失敗する可能性があがることもあります。
x86やx64系では失敗する可能性は低いですが、他のアーキテクチャのCPUでは大抵もっと確率が上がります。

確実にしたいなら、たとえば今回ならInterlocked.Incrementなどを使う手もあります。
x86系ではおそらくですがそれほど大きなパフォーマンスペナルティはないと思います。
解決済み
引用返信 編集キー/
■85920 / inTopicNo.8)  Re[6]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ コング (5回)-(2017/12/01(Fri) 22:20:17)
No85917 (なちゃ さん) に返信

補足ありがとうございます。

失敗する、というのはどういうことですか?

変数内の同じ要素にアクセスすることだとして、
他のアーキテクチャのCPUだとその確率が上がるのはなぜですか?


引用返信 編集キー/
■85922 / inTopicNo.9)  Re[1]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ Jitta (344回)-(2017/12/02(Sat) 08:43:50)
No85877 (コング さん) に返信
>         Parallel.For(2, 1000000, 
>                      Sub(i)
> 
>                          ' SyncLock ready_diff
> 
>                          Dim cRandom As New System.Random()
> 
>                          Dim x = cRandom.Next(1, 255)
>                          Dim y = cRandom.Next(1, 255)
>                          Dim z = cRandom.Next(1, 255)

気になったのが、For 文の中で Random インスタンスを作っていることです。

参考1: http://www.atmarkit.co.jp/fdotnet/dotnettips/035random/random.html
カテキンさんが書かれていることと同じ、乱数シードが同じになると言うことが書かれています。

で、カテキンさんが書かれているように、繰り返し変数 i をシードに使うと、実行ごとに同じ結果になります。
これがいいのか悪いのか。悪いのであれば、避けるにはループの外で Random インスタンスを作ればいいのですが、
どうも Random クラスのメソッドは、スレッドセーフではないようです。
http://referencesource.microsoft.com/#mscorlib/system/random.cs,bb77e610694e64ca


問題と思われることだけ投げてみる。

引用返信 編集キー/
■85926 / inTopicNo.10)  Re[7]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ なちゃ (231回)-(2017/12/03(Sun) 12:15:32)
No85920 (コング さん) に返信
> ■No85917 (なちゃ さん) に返信
>
> 補足ありがとうございます。
>
> 失敗する、というのはどういうことですか?
>
> 変数内の同じ要素にアクセスすることだとして、
> 他のアーキテクチャのCPUだとその確率が上がるのはなぜですか?

マルチスレッドとメモリモデルなどの知識がないとちょっと理解しにくいかも知れません。
ごく簡単に言えば、あるスレッドでメモリに書き込んだ内容は、別スレッドで即座に見えるとは限らないということです。
x86系プロセッサはこのメモリモデルがかなり厳密で、実質的に書き込んだ内容がほぼ即座に別のスレッド(というか別のCPUコア)から見えます。
しかし、他のプロセッサでは大抵もっと縛りが緩く、極端に言えば他のコアに見えるようにしなさいという命令を発行するまで他のコアからは見えません。
そのため、今回の例だと計算した結果が別のスレッドにはしばらく見えず、同時にアクセスしたのと同じ問題が発生します。

実際にはCLRなどのランタイムがこれらの処置を必要に応じておこなって、ほぼほぼ問題が起きないようにしています。
なので、現実的にはたまたま同時に同じ要素にアクセスした場合だけが問題になります。
引用返信 編集キー/
■85927 / inTopicNo.11)  Re[2]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ なちゃ (232回)-(2017/12/03(Sun) 12:19:15)
あ、あと、最終的にどんなコードになったか分かりませんが、Randomインスタンスはスレッド毎に必要になるので、多分今のコードうまくできてないんじゃないかなという懸念もあります。
Jittaさんが少しふれてる内容になりますか。
引用返信 編集キー/
■85933 / inTopicNo.12)  Re[3]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ コング (7回)-(2017/12/04(Mon) 10:39:08)
No85927 (なちゃ さん) に返信

そういうことなんですね。

ちなみに、x86系プロセッサと仰いましたが
最近のPCのほとんどはx64系プロセッサだと思います
これの場合にも同じなのでしょうか?
 
引用返信 編集キー/
■85935 / inTopicNo.13)  Re[4]: マルチスレッドで配列でも同期が必要ですか?
□投稿者/ なちゃ (233回)-(2017/12/04(Mon) 13:42:19)
No85933 (コング さん) に返信
> ■No85927 (なちゃ さん) に返信
>
> そういうことなんですね。
>
> ちなみに、x86系プロセッサと仰いましたが
> 最近のPCのほとんどはx64系プロセッサだと思います
> これの場合にも同じなのでしょうか?

この点については、x64はx86と同じと思って良いです。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ