Intel Threading Building Blocksを使ってみる

Linuxのお勉強をVMWare上のUbuntsu 10.04でやっている。最近、C++でマルチスレッドもできるようになりたいなーということもあって、Intel TBB(Threading Building Blocks)をインストールした。

そんなに難しい作業ではないはずなのだが、Linuxディレクトリ構成とか環境変数とかに無知なのでちょっと苦戦した。結局はぐぐって出たページを参考にしてそれをそのまま使わせてもらった。偉大な先人たちに感謝。

どれぐらい有効か

高速化のためのマルチスレッドプログラミングの経験はほとんどないので、TBBは具体的にどれぐらい有効なのか、全然想像もつかない。そこで何か簡単なコードを書いてみて検証しようとした。お題としては、そこそこコードが小さく済み、並列化が有効だと考えられるものということで、台形公式の実装を試みた。

でもよく考えたら、それ以前にC++をほとんど使ったことがなかった!! ので、参考書を引っ張り出して見よう見まねで書いた。C++のコードとしては微妙な出来になったかも。

#include <tbb/task_scheduler_init.h>
#include <tbb/parallel_reduce.h>
#include <tbb/blocked_range.h>
#include <tbb/tick_count.h>

#include <cmath>
#include <iostream>

using namespace tbb;
using namespace std;

struct SumTranpezoid {
  double x, y;
  double h;
  double sum;

  inline double f(double x) { return 0.01 * x * fabs(sin(x)); }

  void operator()(const blocked_range<size_t>& range) {
    for (size_t i = range.begin(); i != range.end(); ++i) {
      sum += h * (f(x + h*(i - 1)) + f(x + h*i)) / 2;
    }
  }

  void join(const SumTranpezoid& t) { sum += t.sum; }

  SumTranpezoid(SumTranpezoid& t, split) : x(t.x), y(t.y), sum(0), h(t.h) {}

  SumTranpezoid(double x0, double y0, size_t n) : sum(0) {
    x = x0; y = y0;
    h = (y - x) / n;
  }
};

double parallel_tr(int x, int y, size_t n) {
  task_scheduler_init init;
  SumTranpezoid tr = SumTranpezoid(x, y, n);
  parallel_reduce(blocked_range<size_t>(1, n), tr);
  return tr.sum;
}

inline double f(double x) { return 0.01 * x * fabs(sin(x)); }

double serial_tr(double x, double y, int n) {
  double h = (y - x) / n;
  double sum = 0.0;

  for (int i = 1; i < n; i++) {
    sum += h * (f(x + h*(i - 1)) + f(x + h*i)) / 2;
  }
  return sum;
}

int main() {
  tick_count t0 = tick_count::now();
  cout << serial_tr(0.0, 1000, 30000000) << endl;
  tick_count t1 = tick_count::now();
  cout << parallel_tr(0.0, 1000, 30000000) << endl;
  tick_count t2 = tick_count::now();

  cout << "serial  :" << (t1 - t0).seconds() << endl;
  cout << "parallel:" << (t2 - t1).seconds() << endl;

  return 0;
}

実行結果

手元の2コアの環境で実行したところ、並列化によってだいたい数割ぐらい高速化された。ちなみに関数fが(x * xとか)単純すぎた場合は並列化の効果が出るどころか逆に遅くなった。なんでだろう?そういうものなのだろうか。並列化のオーバーヘッドの方が相対的に大きくなったのか。

思ったほど効いてくれないなあ。あとやっぱり、コアが沢山ないと楽しくない。8コアぐらいのマシンがないと劇的な高速化は望めなさそう。

参考書

Accelerated C++―効率的なプログラミングのための新しい定跡 (C++ In Depth Series)

Accelerated C++―効率的なプログラミングのための新しい定跡 (C++ In Depth Series)

  • 作者: アンドリューコーニグ,バーバラ・E.ムー,Andrew Koenig,Barbara E. Moo,小林健一郎
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2001/12
  • メディア: 単行本
  • 購入: 10人 クリック: 120回
  • この商品を含むブログ (42件) を見る
インテル スレッディング・ビルディング・ブロック ―マルチコア時代のC++並列プログラミング

インテル スレッディング・ビルディング・ブロック ―マルチコア時代のC++並列プログラミング