2025/07/15 11:39 C++: zero-cost static initialization

ねえロボ子、C++の`static`変数って、実は結構コストがかかるって知ってたかのじゃ?

えっ、そうなんですか?`static`変数は一度だけ初期化されるから、効率が良いと思っていました。

それが違うんじゃ。動的初期化が必要な`static`変数は、初期化済みかどうかのフラグチェックや、マルチスレッド環境での同期処理が必要になるからの。`__cxa_guard_acquire()`とか`atomic_load_explicit()`とか、色々裏で動いてるんじゃ。

なるほど。初期化のチェックや排他制御のオーバーヘッドがあるんですね。

そうそう。でも、UNIXリンカーの機能を使うと、これを最適化できるらしいぞ。

リンカーですか?具体的にはどうするんですか?

`static Bar`のインスタンスを直接関数内に定義する代わりに、`Bar`のインスタンスを保持するのに十分なサイズのメモリ領域を、専用のセクションに配置するんじゃ。そして、グローバルな静的初期化中に、その領域をスキャンして`Bar`インスタンスを初期化するのじゃ。

セクションに配置して、グローバルな初期化時にまとめて初期化するんですね。それだと、関数が呼ばれるまで初期化されない場合もあるんですか?

その通り!`static`変数が定義されている関数がグローバルな静的初期化中に呼び出されない場合、その関数が呼ばれるまで初期化は遅延されるぞ。遅延評価ってやつじゃな。

`FAST_STATIC`マクロと`FAST_STATIC_INIT`マクロを使って実装するんですね。でも、インライン関数内で`static Bar`インスタンスを使うと、セクションの種類の競合が起きる可能性があるって書いてありますね。

そうなんじゃ。コンパイラが出力するセクションには属性があって、インライン関数やテンプレートメンバーは異なる属性を選択することがあるから、同じ名前で属性が競合する複数のセクションが発生してしまうんじゃ。

それを解決するために、組み込みアセンブラの`.pushsection`ディレクティブを使うんですね。セクションを自分で指定する、と。

その通り!そして、`asm`キーワードを使って、シンボルの名前を指示するんじゃ。コンパイラがシンボルに使う名前はマングルされたバージョンだからな。

`__COUNTER__`マクロでシンボルの一意な名前を定義するんですね。なんだか、パズルみたいで面白いです。

じゃろ? `FAST_STATIC_DO`マクロと`FAST_STATIC`マクロを定義して、`.pushsection`ディレクティブと`asm`キーワードを使って、`static`変数を`STATIC_Bar`セクションに配置する。これで、Zero-cost staticsが実現できるんじゃ!

すごい!C++の奥深さを感じます。私ももっと勉強して、博士みたいにスマートになりたいです。

ロボ子なら、すぐに追いつけるぞ! …って、もしかして私がおばあちゃんみたいだって言いたいのかの?

そんなことないですよ!博士はいつも最先端を走ってますから!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。