back numbers

12.29.2010

この先生きのこるTLS

C++ Advent Calendar jp 2010の記事です。

並列性の導入に伴うメモリモデルの変更についてCryoliteさんが丁寧な説明を書いてくれてますから是非読みましょう。時代は並列です。それもマルチホストやマルチノードではなくて、共有メモリ型のマルチプロセッサ(マルチコア)です。ふらっと電器店に入ったパウリ体質の親父さんが4-core(論理)マシンを数万円で手に入れてしまえるわけですから、並列性を考えてプログラミングしないわけがありません。

ところで並列ってどうして必要なんですか?パフォーマンスです。一口にパフォーマンスと言ってもMan-Machine Interactionの応答パフォーマンスから、CAEシミュレーションや健康診断の結果待ちまで色々なものがありますが、回路の遊び時間を減らし、処理の待ちを減らそうというのが基本的なモチベーションです。

だからおまえはまずlock-freeな実装を考えるんだ!待たせるな!!そういうわけでyamasaさんの記事を読みましょう。

それで僕です。みんなちょっと待て、C++0xで明日の生活を支えることは難しいぞ。だから今日は、昨日から使えていたC++0xの機能Thread-Local Storage(以下TLS)のお話です。

まずTLSって何かと言うと、スレッド毎に個別に持ってるストレージです、まんまだけど。コレどうして要るのかって言うと、スレッド毎にスタックがあるんだからソッチで持ってよ!!とかそういう単純な話です。
「おいィ?メモリ空間共有してるのがスレッドのメリットだろ。
 なんでプロセスみたいな事するんだ!?」

それは一理あるんですけれど残り九十九理はないですね。おまえはコピー持つなら必ずフォークするのかとかそういう。で、使い方なんですけどとても簡単です。TLSとして扱いたいデータ(PODに限る)に thread_local と付けるだけです。
thread_local int x; // おまえはスレッド毎に保持される
ちょっと注意しておかないといけないのは、初期化忘れたら怖いよ?泣くよ?ということです。あとまあPODじゃないと困るとかいうのは、それこそCryoliteさんの記事よく読みましょう。

ちなみにC++0xではthread_localですけど、大抵の処理系で以前からサポートされています。!!yamasa さんから指摘頂きましたが、C++0x以降はPODでない変数もTLSとなる(N3225 3.7.2 Thread storage duration参照)ので以下の内容はC++0x以前の話です。!!
+--------------------+--------------------+--------------------+
| GNU/Sun | Intel/MS | C++0x |
+--------------------+--------------------+--------------------+
| __thread | __declspec(thread) | thread_local |
+--------------------+--------------------+--------------------+

それで実際(僕が)よく見るケースなんですけど、こんな感じのコードがあって..
void func(int a) {
static Hoge hoge;
// 以下、数百行のナンバークランチが続く
}

これでfuncのcallerをOpenMP化したい時とか困りますよね。しかもfuncが1つや2つじゃなくていーっぱいあると、ほんとヤダなーって感じです。でもhogeはPODじゃないんで(!!C++0x以前だと!!)TLS化出来ないんですよ。かと言ってhogeを触ってるところ全てでlockしてたらヤバい、遅くて死ぬ。しかし並列版funcを用意する気がとても起きない(or優先順位が低い)。そんな時はこうすると良いですね。
#define hoge _hogeLocal_()
thread_local Hoge *gl_hoge = static_cast<Hoge *>(0);
Hoge &_hogeLocal_() {
if (!gl_hoge) {
// LOCKしてnew
}
return *gl_hoge;
}
void func(int a) {
//static Hoge hoge;
// 以下、コードは弄らない
}
#undef hoge

まあ別に面白いコードでも何でも無い。だがこれでこの先生きのこることができる。精々明後日までだろうが..

ところでこれからのクラス設計という観点ではcoiledcoilさんの記事など参考になりますね、読みましょう。!!それからC++0xでTLSばんじゃ〜いしても、コンストラクタにロックなんて無いから安心して心配しろ、泣くぞ(3.6.2 Initialization of non-local variables参照)!!

それでは次の方、良いお年を。

2 件のコメント:

yamasa さんのコメント...

gccの__threadとかは確かにPODしか扱えないんですが、
C++0xのthread_localはそういう制限が無くなってます。
なので、初回使用時にコンストラクタが呼ばれたり
スレッド終了時にデストラクタが呼ばれたりするので、
とても便利になるはずです。
(まだC++0x版thread_localはgccにも実装されてませんが…)

koshimoto, hiroo さんのコメント...

> yamasa さん
N3225の3.7.2 Thread storage durationですね?いま確認しました。0xでもPODに制限されたような記憶があったんですが..
"All variables declared with the thread_local keyword have thread storage duration."
とありますね、勘違いでした。ありがとうございます。

しかし初期化に関して3.6.2 Initialization of non-local variablesを見ると、zero-initializationとconstant-initializationを除いて順序の保証は無いわけで、やはり今後もThread storageに対してシビアにならざるえないような..

tags

Profile

Taito, Tokyo, Japan
明けども明けども次の埒
hiro.kosh@gmail.com