質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

729閲覧

マルチスレッドでループを最適化してみたが速くない

jbe00214

総合スコア63

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2020/10/30 14:58

前提・実現したいこと

2つの同じサイズのvectorの同一性の比較としてthreadを作ったのですが,想定に反して速くありません。何が問題なのかお分かりになる方がいらっしゃれば教えてください。
構造は至って単純です。
・elapsed_time_dblは時間計測用テンプレート関数
・is_eq_ob スレッド管理クラス ,!=を見つけたときのスレッド中止用フラッグとしてatomicを使用。結果判定返却用として兼用。

該当のソースコード

C++

1#include <thread> 2#include <chrono> 3#include <vector> 4using namespace std; 5template<class TimePoint> 6double elapsed_time_dbl(const std::string& msg, const TimePoint& start) { 7 auto end=std::chrono::high_resolution_clock::now(); 8 std::chrono::duration<double, std::milli> fp_ms = end-start; 9 std::cout << msg <<" : " << fp_ms.count()<<" ms\n"; 10 return fp_ms.count(); 11} 12class is_eq_ob{ 13 int size; 14 const vector<int>& m_a; 15 const vector<int>& m_b; 16 atomic<bool> m_loop_end; 17public: 18 is_eq_ob(const vector<int>&a, const vector<int>&b): 19 m_a{a},m_b{b},m_loop_end{false},size{(int)a.size()}{} 20 21 void comp_thread(int st, int count ){ 22 for(int i=0;i < count;i++){ 23 if(m_loop_end.load()) 24 return ; 25 if( m_a[st+ i] != m_b[st+ i] ){ 26 m_loop_end.store(true); 27 return ; 28 } 29 } 30 } 31 bool go(){ 32 33 vector<thread> th_s; 34 int div = 10; 35 ///divの数のスレッド生成,sizeをdiv数で分割してブロックごとに処理 36 for(int d=0;d < div;d++){ 37 int start = size * d/div;//スレッドに渡す開始位置 38 int end = (d==div-1) ? size: size*(d+1)/div;//スレッドに渡す終了位置 39 thread a(&is_eq_ob::comp_thread,this, start, end - start); 40 th_s.push_back( move(a) ); 41 } 42 43 for(auto& th: th_s) 44 th.join(); 45 46 return !m_loop_end.load(); 47 } 48}; 49 50 51int main(){ 52 53 vector<int> a(1000,0); 54 vector<int> b(1000,0); 55 //a[100]=1; 56 57 auto start=std::chrono::high_resolution_clock::now(); 58 cout << (a==b)<<'\n'; 59 elapsed_time_dbl("a==b", start); 60 61 is_eq_ob is_eq(a,b); 62 start=std::chrono::high_resolution_clock::now(); 63 cout << is_eq.go() <<'\n'; 64 elapsed_time_dbl("is_eq_ob", start); 65 return 0; 66}

試したこと

結果は次のように常にスレッドの方が遅くなります。
1
a==b : 0.064569 ms
1
is_eq_ob : 0.37863 ms

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tiitoi

2020/10/30 15:05

配列の規模が小さすぎるので、スレッド化するコストのほうが上回ってしまっているのでは?
jbe00214

2020/10/30 15:16

オーバーヘッドですよね。たしかにそうなんですが,この例では,そうなりますね。サイズを1000000にすると, 1 a==b : 1.91292 ms 1 is_eq_ob : 0.519138 ms となります。 mainにコメント行を入れてあり,これを外して100の位置に不整合となる値を入れると, 0 a==b : 0.071875 ms 0 is_eq_ob : 0.333058 ms となり,少なくともスレッド1は早めに戻ってくると思うのですが,これもオーバーヘッドなんですかね。
pododezo469

2020/10/30 20:35

そっちの方は全スレッド終了待機のオーバーヘッドだと思います。 確認方法としては、以下のようにして不整合判定時に時間を出力すると非マルチスレッド版と同程度になると思います。 スレッド使う開始時間は上の方に別の高域変数として定義しておいて、 using namespace std; static std::chrono::high_resolution_clock::time_point thread_start; スレッドの方の開始時間をセットしてから開始、 thread_start = std::chrono::high_resolution_clock::now(); is_eq_ob is_eq(a,b); あとは不整合判定直後に出力する。 if( m_a[st+ i] != m_b[st+ i] ){ elapsed_time_dbl("is_eq_ob", thread_start); もちろんこのやり方だと複数回不整合出力されてしまう可能性のあるコードとなりますが、 全スレッド終了待機に時間がかかっていることが分かればあとはやり方次第です。
jbe00214

2020/10/31 05:30

そうなんです。私もやってみました。どうやら,早期に不一致を検出しているにもかかわらず,全部のスレッドの終了まで待機させられているようなんですね。そこまでわかったのですが,これを回避する手段がみつからなくて...。
guest

回答3

0

自己解決

諸氏のいうとおり,スレッド管理のオーバヘッドの方が大きく,単純な値の比較程度の処理であれば,スレッドを使わずに単純なループで十分のようです。3つ程度のスレッドでもオーバーヘッドは小さくなりませんでした。

投稿2020/11/19 01:06

jbe00214

総合スコア63

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

スレッドの効果を判断するためには、マルチスレッドの場合とシングルの場合で同じロジックで比較しなければなりません。試しにやってみたら、マルチスレッドの方が速くはなっていますが、やはりvectorの比較演算子は桁が違いますね。vectorの比較演算子の実装が単純なループコンペアでは無いのでしょう。

デバッグ版での結果ですが
vectorサイズ 1億

vector比較演算子 1047.04ms
main関数内でa,bのループコンペア 17159.7ms
go() div=1 23816.6ms
go() div=10 12923ms

投稿2020/11/06 23:09

ruruucky

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jbe00214

2020/11/07 02:48

vectorは速いですね。試されたのは全要素の比較でしょうか,それとも,不整合となるデータがある場合の検出での結果でしょうか。不整合データをどのあたりに挿入した結果でしょうか。
ruruucky

2020/11/07 04:10

不整合無しで全要素の比較です。
guest

0

1000個程度では処理が軽すぎて、10個のスレッド生成のオーバーヘッドの方が大きいのではないかと。
もっと数を増やしてはどうですか。

投稿2020/11/06 16:35

ruruucky

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jbe00214

2020/11/06 21:05

vectorを1'000'000,スレットを3にして動かしても同様です。なお,mainのコメント部分を外し,不一致を検出させてみます。次のようにスレッド関数内に時間計測を入れて,クラスのprivate部にmutex mを入れて計測してみます。 void comp_thread(int st, int count ){ auto start=std::chrono::high_resolution_clock::now(); for(int i=0;i < count;i++){ if(m_loop_end.load()){ lock_guard<mutex> lock(m); elapsed_time_dbl("killed ", start); return ; } if( m_a[st+ i] != m_b[st+ i] ){ m_loop_end.store(true); lock_guard<mutex> lock(m); elapsed_time_dbl("detected", start); return ; } } } 結果は, 0 a==b : 0.071853 ms detected : 0.00295 ms killed : 0.021594 ms killed : 0.0563 ms 0 is_eq_ob : 0.241904 ms のようになります。すなわち,0.00295 msという圧倒的な速度で検出しているのに,他の2つのスレッドの戻りが遅く,しかもその3つのスレッドの合計以上のオーバーヘッドが加わって,単純ループに負けているのがわかります。超高速で検出したスレッドの効用が活かせられません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問