Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

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

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2018/03/05 14:00

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

目次

手分けのしかたは二通り

 「マルチスレッドで速くする」とは、つまるところ同時に動ける複数のスレッドが仕事のかたまりを手分けして処理することで速くしてるわけですよね。このとき「手分けのしかた」は大きく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はラーメン屋の流れ作業を組み立てるのに不可欠なんです。マクラが長くてごめんなさいね。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

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

    C++に首まで浸かったプログラマ。 Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"り わんくま同盟でたまにセッションスピーカやったり 中国茶淹れてにわか茶人を気取ってたり、 あと Facebook とか。 著書: - STL標準...

バックナンバー

連載:C++11:スレッド・ライブラリひとめぐり
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5