今、大きな配列を元に処理を行うプログラムを作成しています。
シングルスレッドでも十分速度を向上するようチューニングに成功しましたが、マルチスレッド化をすればさらに速度を向上させることができるだろうと考え、先日マルチスレッドかに成功しました。
しかし・・・奇妙な現象が起こりました。
マルチスレッドで性能を引き出すには、排他制御はないほうが良いと考え、メモリは食いますがスレッドに与える入力情報(大きな配列)を2つ用意し、排他制御なしの2スレッドを実行できるようにしました。しかしやはりメモリを消費しすぎてしまうため、配列にアクセスする部分のみ排他制御を行うようクリティカルセクションを設定し入力情報を2スレッドで共有して処理を行うよう組み替えました。
結果、やはり排他制御なしの場合よりはるかにスピードダウンしてしまい、シングルスレッドより少し早い処理時間で終了してしまいました。
余りにも悔しいため、ちょっと危険な実験だとは思いましたが、入力情報を2つのスレッドで共有しているにもかかわらず、排他制御の部分、つまりクリティカルセクションを取り除いて実行してみようと考えました。予想としては同時にアクセスし衝突が起きてエラーで停止してしまうと考えましたが・・・・・・
結果なぜかエラーなく処理をし続け、普通に終了してしまいました。
これはなぜでしょう?
偶然にも共有情報に同時にアクセスすることがなかったためでしょうか?
No.4ベストアンサー
- 回答日時:
排他制御しないというよりは、同期/ロックしないのがコツというか、
単純にクリティカルセクションを取ったら唯のバグですから。
# ブレーキが邪魔だとブレーキ外してアクセルを踏むようなもの。
(小物は変に弄ってもかえって重くなることもあるので)規模にもよりますが、
その配列自体を二分割して最後に結合するようなアルゴリズム
(処理中は排他する必要がなくなる)に変更するとか、
読み出し回数が圧倒的に多いならReaders/Writerロック作るとか、
リードオンリオブジェクトにするとか。そういう組み換えを考えられえてみては。
どう考えても、巨大な単一の配列を使ってる時点で、
マルチスレッド用に最適化された設計/アルゴリズムではありませんから。
目先/小手先でどうこうするより、まずアルゴリズムを練るのが常道かと。
# プロファイリングとかして見てますか。
# 後は、キャッシュヒット率とか考慮されてますか。
返答ありがとうございました。
実はマルチスレッドの概念を自分で勘違いしていました。
MrBan様のおっしゃられた通り、共有する配列は読み出しのみ行っていました。自分は共有する配列を同時に読みに行ってしまった場合衝突が起きるのではと考え、毎回の読み出しに排他制御を行っていました。
しかしまずマルチスレッドで同時に・・・なんてことは起こりませんよね。回答者No.2様がおっしゃられるように矛盾のみ発生するもので、同時になんてあるわけがないし、別に読み出しに排他制御は必要ありませんね。
今回の処理は入力の配列を複数のスレッドに渡し、共有変数への書き込みは行わない処理で、各スレッドはそれぞれ入力として与えられた配列から別々に結果を出力するプログラムなので、排他制御はまず不要と考えるべきでしょうね。MrBan様がおっしゃられる通り、巨大な単一の配列を共有して読み書きを行うとなるとそれはアルゴリズムを改良すべきでしょうね。
皆様返答ありがとうございました。
No.3
- 回答日時:
がると申します。
ちょっとご自身のお立場が不明なので「お仕事でコーディングされている」前提とします。
> マルチスレッドで性能を引き出すには、排他制御はないほうが良いと考え
これは確定でNGです。thread化したプログラムできちんと排他処理をしていないのは「エラーが出るプログラムです」と明記しているようなものなので。
性能云々以前の問題として、排他制御は「必ず」やってください。
> 結果なぜかエラーなく処理をし続け、普通に終了してしまいました。
> これはなぜでしょう?
>
> 偶然にも共有情報に同時にアクセスすることがなかったためでしょうか?
偶然、です。まぁ2threadくらいなら「たまたまぶつからなかった」んでしょう。
経験的には、2桁前半くらいなら、排他制御をしなくても(或いは雑な排他制御でも)「ある程度」動くみたいです。
3桁になるといきなりぶつかり出しますが。
いずれにしても、threadできちんと排他制御をせずに、というのは、あらゆる観点から「お勧めできない」ので。
趣味であればそれはそれで「面白い」と思いますが、お仕事であれば「絶対にやってはいけない」と思います。
No.2
- 回答日時:
それはたぶん、共有している情報が同時アクセスしてもハングアップにつながらない情報だったからでは無いでしょうか?
同じメモリアドレスに同時アクセスすると言っても、メモリにアクセスできるのは、瞬間的にはどちらかのスレッドだけです。
クリティカルセクションが必要な理由は、以下の様な場合です。
簡単にするためスレッドの処理は、
a++;
だけです。
(1)int a = 1;でaが0x800000番地だったとします。
(2)番地0x800000からint値をCPUレジスタに読み出す(スレッドA)
(3)スレッドA→B切り替え
(4)番地0x800000からint値をCPUレジスタに読み出す(スレッドB)
(5)CPUレジスタでint値に+1を行う(スレッドB)
(6)番地0x800000にCPUレジスタのint値を書き出す(スレッドB)
(7)スレッドB→A切り替え
(8)CPUレジスタでint値に+1を行う(スレッドA)
(9)番地0x800000にCPUレジスタのint値を書き出す(スレッドA)
(10)printf("a=%d\n",a);(スレッドA)
(11)スレッドA→B切り替え
(12)printf("a=%d\n",a);(スレッドB)
で結果は、どちらのスレッドもa=2と表示されます。
ちゃんと排他処理ができていればa=3となるべき処理ですよね。
ちゃんと処理できてませんが、エラーになるわけではありません。結果はちゃんと合っていますか?もし合っていたら単なる偶然だと思ってください。
No.1
- 回答日時:
> 結果、やはり排他制御なしの場合よりはるかに
> スピードダウンしてしまい、シングルスレッドより
> 少し早い処理時間で終了してしまいました。
読んでいる限りでは、妥当だろうと納得できる結果です。
コードを見ていないので、ご自身も意図せず排他されてる、
または排他不要、読み出ししかしていない等の可能性も
ゼロではないと思いますが、普通に考えてただの偶然です。
たまたま発生頻度が低かったか、発生しても害がないコードだったか、
害に発生に気づかなかったか、いずれかでしょう。
> 予想としては同時にアクセスし衝突が起きてエラーで停止してしまうと考えましたが・・・・・・
配列の中身のデータに不整合が出た場合にそれを検出したり、
エラーを出したり、停止したりというコードを書いているのですか?
それらがなくて、単純に同時アクセスが発生しただけなら、
衝突してもエラーなんて出ませんし、停止もしません。
スレッド間の排他制御はプログラマの責任です。
排他が漏れても、おかしな値のまま処理がそのまま動くだけですので、
「実は結果がおかしくなっている(が気づかなかった)」という可能性もありそうに思います。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・ハマっている「お菓子」を教えて!
- ・最近、いつ泣きましたか?
- ・夏が終わったと感じる瞬間って、どんな時?
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
マルチスレッドの同期方法
-
pthread_cond_wait 取りこぼし?
-
msec単位のWait Timerが作れない!
-
.NetのBackgroundWorkerクラス...
-
MFC通信プログラムマルチスレッ...
-
待機関数(WaitForMultipleObjec...
-
スレッドにて同一メモリの書き...
-
Windows上で、シグナル(SIGTERM...
-
スレッドでWM_TIMERを受け取れない
-
sleep関数とは?
-
スレッドの監視方法について
-
クラス内でのpthread_createに...
-
C++ GUIのメッセージループ。
-
PHP スレッド構成の掲示板について
-
Macターミナルで実行中のプログ...
-
C言語で、メモリを解放しないで...
-
バックグラウンドのプロセスの...
-
プロダクションコードとは?
-
緯度、経度の 10進法と 60進法...
-
VBSの処理中一旦処理を止めて再...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
WaitForSingleObjectの使い方に...
-
スレッドの監視方法について
-
Windows上で、シグナル(SIGTERM...
-
VC++スレッドの正しい終了のさ...
-
スレッドにて同一メモリの書き...
-
スレッドの安全な終了のさせ方
-
メインスレッドのPostMessageと...
-
C# スレッドから親ウィンドウへ...
-
別スレッドからメインダイアロ...
-
MFC通信プログラムマルチスレッ...
-
スレッドの終了はどうやるんで...
-
同一スレッドで、ロックをかけ...
-
VB2005 シリアル通信のClose処理
-
Linuxでスレッド優先度って変え...
-
スレッドの終了の仕方
-
LinuxでDoEvents()同等機能
-
msec単位のWait Timerが作れない!
-
C#でスレッド実行中のイベント...
-
win32 スレッドのハンドルついて
-
スレッド終了を待つ間に開放さ...
おすすめ情報