SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

C++11:スレッド・ライブラリひとめぐり

C++11:スレッド・ライブラリひとめぐり【補足編:3】

  • X ポスト
  • このエントリーをはてなブックマークに追加

 こんなはずじゃなかったんです。かなり昔に書いたスレッド・ライブラリの紹介があまりにざっくりしてたので、つけ足しのつもりで筆を進めてて気がつけば三部作になっちゃったという。標準C++ライブラリの一つthread support libraryの(補足のハズだった)解説、おしまいはスレッド間の同期をサポートしてくれるcondition_variableのおはなし。

  • X ポスト
  • このエントリーをはてなブックマークに追加

手分けのしかたは二通り

 「マルチスレッドで速くする」とは、つまるところ同時に動ける複数のスレッドが仕事のかたまりを手分けして処理することで速くしてるわけですよね。このとき「手分けのしかた」は大きく2つに分類できます。

fig01
fig01

 1つは庭の草むしり。1人でやるのはしんどいけれど、庭をN等分してN人が一斉に草をむしれば1人あたりのノルマは1/Nとなり、スピードはざっくりN倍になります。アレとコレをやんなきゃいけないけれど、アレとコレが独立してるなら2つのスレッドで同時にやれば早く片付きます。

 大量の要素が並んだ配列の総和を求める(あんまり面白くない)サンプルを書いてみました。要素数Nの配列:vector<double> data(N)の総和を求めるのに2つのスレッドを起こし、それぞれ配列の前半部と後半部の和を求めます。両者が処理を完了したら、得られた2つの和を足せば総和が求まります。

list01
#include <iostream>
#include <random>
#include <thread>
#include <future>
#include <numeric>
#include <vector>
#include <chrono>

int main() {
  using namespace std;
  using namespace std::chrono;

  const int N = 10000000;
  vector<double> data(N);

  mt19937 gen; // メルセンヌ・ツイスター
  normal_distribution<double> dist; // 正規分布(平均:0,標準偏差:1)
  auto rand = [&]() { return dist(gen); };
  generate_n(begin(data), N, rand);

  double sum;
  long long duration;
  high_resolution_clock::time_point start;
  high_resolution_clock::time_point stop;
  
  // single-thread
  start = high_resolution_clock::now();
  sum = accumulate(begin(data), end(data), 0.0);
  stop = high_resolution_clock::now();
  duration = duration_cast<microseconds>(stop - start).count();
  cout << "single thread: " << sum << " in " << duration << "[us]\n";

  // 2-threads
  start = high_resolution_clock::now();
  // forkして
  future<double> f0 = async([&]() { return accumulate(begin(data),     begin(data)+N/2, 0.0); });
  future<double> f1 = async([&]() { return accumulate(begin(data)+N/2, begin(data)+N  , 0.0); });
  // joinする
  sum = f0.get() + f1.get();
  stop = high_resolution_clock::now();
  duration = duration_cast<microseconds>(stop - start).count();
  cout << "  dual thread: " << sum << " in " << duration << "[us]\n";
}
fig02
fig02

 2倍とまではいかないけれど、まぁいいセンいってますかね。

 もう一つはラーメン屋。とあるラーメン屋に電話で注文が入ってきます。電話が鳴るたびに注文を受け(入力)、ラーメンこしらえて(処理)、バイク飛ばして届けます(出力)。それぞれに5分かかるとすると、店主1人で切り盛りする"ワンオペ"だったら入力から出力まで15分、次の注文に応じられるのは15分後です。

 バイトを雇って3人で店を回すなら、3人がそれぞれ入力/処理/出力を担当し、入力から処理へ/処理から出力へと仕事が流れていきます。お客様からすれば電話してから15分でラーメンが届くことに変わりはないけれど、注文を受けてから5分後には次の注文が受けられるんだから処理能力は3倍です。

 condition_variableはラーメン屋の流れ作業を組み立てるのに不可欠なんです。マクラが長くてごめんなさいね。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
生産者と消費者

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
C++11:スレッド・ライブラリひとめぐり連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10657 2018/03/05 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング