第4回 並列処理プログラミングの基本用語
株式会社フィックスターズ
好田 剛介
2009/12/18
CPUの周波数の高速化競争が頭打ちになり、1コアにおける処理能力は限界となった。CPUの進化がマルチコア化に向かった結果、並列コンピューティングの門戸が開かれた(編集部)
「並列化されていないコードよりは速く」から始めよう
第2回「現代のプロセッサと並列実行」や第3回「プロセッサ別に見る並列アーキテクチャ」では、さまざまなプロセッサの構造について説明しました。
しかしながら、それらのプロセッサにおける並列実行の仕組みのすべてを利用して、 その性能を限界まで引き出すのは簡単ではありません。
そこで、今回から3回に分けて、並列処理プログラミングの第一歩として、「並列化されていないコードよりは速い」ことを目標に、簡単な並列プログラムを実際に書いていきます。
今回は、覚えておくべき用語を、実際にコードを動かしながら解説します。第5回と第6回では、スレッドを用いた並列プログラムと、ベクトル演算を用いた並列プログラムの2つを紹介します。
これらの手法は計算機内部での並列化を実現するものです。複数の計算機にまたがる並列プログラムについては、また別の機会に述べることにします。
今回のスレッドに関するプログラムは、Ubuntu 9.04 x86版とg++ 4.3.3、Boost c++ library 1.35.0で動作確認を行っています。ベクトル演算に関するプログラムは、Yellow Dog Linux 6.1とCellSDK 3.1で動作確認を行っています。
Boost C++ライブラリ
今回はスレッドを用いた並列プログラムとして、Boost C++ライブラリ(以下、Boost)を利用したマルチスレッドプログラムについて説明します。
Boostは、フリーで検証された移植性の高いC++のライブラリです。既存のC++標準ライブラリとの相性が良く、Boostのライブラリの内のいくつかは次期C++標準ライブラリの候補になっています。
例えば、Boost.Threadは、Boostのスレッドライブラリで、TR1として知られているC++の技術報告書に次期C++標準ライブラリとして候補に挙げられています。
このような移植性の高いスレッドライブラリを使用すると、プロセッサに依存しない並列プログラムを書くことができます。
基本的な用語を覚えよう―スレッド
手続き型プログラムでは、変数の読み書きをしたり、演算をしたり、関数を呼んだりしますが、このようなプログラム実行時における処理の流れをスレッドと呼びます。
従来のプログラムではスレッドは1つしかありませんでしたが、複数のスレッドを用意することで、データを共有しつつ複数の独立した処理の流れを扱えるようになります。このような複数のスレッドを扱うプログラムをマルチスレッドプログラムと呼びます。
これまではシングルコアのプロセッサが主流であったため、マルチスレッドプログラムを書いても1つのコアで複数のスレッドを処理することとなり、マルチスレッド自体のオーバーヘッドのために若干遅くなってしまいました。このような、見掛け上は並列のように見えるが実際には並列でないものを、疑似並列(pseudo parallel)と呼びます。
しかし、現在はマルチコアのプロセッサが広く使われるようになっていますので、マルチスレッドプログラムは高速化の有効な手段となります。
それでは、実際にマルチスレッドプログラムについて説明していきます。次のコードはエラーチェックなどを省いた単純なスレッドプログラムです。
#include <iostream> #include <boost/thread.hpp> void para_func(void) { for (int i = 0; i < 3; ++i) { std::cout << "0" << std::endl; } } int main(void) { boost::thread th(para_func); for (int i = 0; i < 3; ++i) { std::cout << "1" << std::endl; } th.join(); return 0; }
sample_boost_thread.cxxとして保存し、コンパイルします。
$ g++ sample_boost_thread.cxx -lboost_thread -o sample_boost_thread.elf
それでは実行してみましょう。
$ ./sample_boost_thread.elf 1 0 1 0 0 1
para_func関数で出力される0と、main関数で出力される1が混ざっていることから、並列に実行されていそうだ、ということが分かるかと思います。
では、本プログラムを行順に解説していきましょう。
このプログラムは実行されると、まずmain関数からスタートします。この時点では1つのスレッドのみが存在します。
main関数の14行目で、boost::threadクラスのthという変数をpara_funcという引数で定義しています。ここでmain関数が実行されているスレッドとは別の新たなスレッドが生成され、そのスレッド上でpara_func関数の実行が開始します。
この時点でmain関数が実行されているスレッドと、para_func関数が実行されているスレッドの2つのスレッドが存在することになります。
その後、main関数は18行目で1を標準出力に出力し、21行目のth.join()でpara_func関数が実行されているスレッドの終了を待ちます。それと同時にpara_func関数は8行目で0を標準出力に出力し、関数のリターンとともにスレッドを終了します。
このjoin関数による待ちを行わず、main関数がpara_func関数よりも早く終わった場合、para_func関数実行中にも関わらずプログラムが終了してしまうので注意してください。
これでスレッドの一番基礎的な部分は終了です。
1/2 |
Index | |
並列処理プログラミングの基本用語 | |
Page1 「並列化されていないコードよりは速く」から始めよう Boost C++ライブラリ 基本的な用語を覚えよう―スレッド |
|
Page2 基本的な用語を覚えよう―ミューテックス そのほかの基本的な用語 |
Think Parallelで行こう! |
- プログラムの実行はどのようにして行われるのか、Linuxカーネルのコードから探る (2017/7/20)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。最終回は、Linuxカーネルの中では、プログラムの起動時にはどのような処理が行われているのかを探る - エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (2017/7/13)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、プログラムの終わりに呼び出されるexit()の中身を探る - VBAにおけるFileDialog操作の基本&ドライブの空き容量、ファイルのサイズやタイムスタンプの取得方法 (2017/7/10)
指定したドライブの空き容量、ファイルのタイムスタンプや属性を取得する方法、FileDialog/エクスプローラー操作の基本を紹介します - さらば残業! 面倒くさいエクセル業務を楽にする「Excel VBA」とは (2017/7/6)
日頃発生する“面倒くさい業務”。簡単なプログラミングで効率化できる可能性がある。本稿では、業務で使うことが多い「Microsoft Excel」で使えるVBAを紹介する。※ショートカットキー、アクセスキーの解説あり
|
|