«

[C++] 高速化について思うところ

明けましておめでとうございます♪
遥佐保(はるか・さお)です
2011年もどうぞ、よろしくお願いします

これは C++ Advent Calendar jp 2010 (http://atnd.org/events/10573) への26日目(補欠1号!)の参加記事です

C++の仕様やBoostについては全然詳しくないので、、すみません
C++でのゲーム作成の時期が長かったので、高速化について、思うところを記載してみます
読み物として目を通して頂ければと思います

最初に。。
ゲームは結果を出せれば何でもアリな世界で
極端な話どんなに可動性に欠けても規格に則ってなくても
良い作品が出来ればそれでヨシ!という感じなので
有識者が見たら反感を買うコードがうじゃうじゃかもしれません

例えば、オブジェクトを生成するためには new するわけですが、new は知ってのとおり、1フレーム内で大量に処理するには時間がかかり過ぎるし、タイミングによっては最悪、確保出来ない場合(※)もあります

(※)確保出来なかった場合どうなるのか?ですが、
優先順位を決めて確保の可否を判断したりします
自分がどうしても確保されなければならないOBJの場合
他の生きているOBJで優先順位の低いやつを探して
そのOBJを殺し、自分に新たに割り当てたりします

そうなってくると、最初に予め new しておいて、それを使いまわすということになるのですが、そしたらコンストラクタやデストラクタに依存しない作りをルールにしておく必要があります

// もしくは、デストラクタを明示的に自力で呼ぶルール

全然事情の知らない人が、このルールだけを見たら
「C++のデストラクタは信用できないから使わないんだってさ、ばかじゃないの!?」
と言うかもしれないけど、ゲームはメモリ確保関連は動的に見えて、ほぼ静的なので
こりゃ仕方ない

そういうのって凄まじい試行錯誤から生み出された究極のソースコードなので、許してもらいたいです
↓↓例えばこんな例

1
2
3
4
5
6
7
8
9
// 最初に確保
void *p_mem = operator new( max_size * sizeof( WORK ));
 WORK *box = static_cast<WORK*>( p_mem );
 for( i = 0; i < max_size; i ++ ){
    new( &box[i] ) WORK();
 }
 
 // 自分でデストラクタ呼び
 box[i].~WORK();


インライン化をうまく使う

何度も何度も、1フレームに何千回と呼ばれるような処理も中にはあります
その処理を関数にしておくと、もちろん関数呼び出しの分処理が遅くなってしまいます
そこでインライン化です
これはC言語のマクロ展開ほとんど同じです

// マクロ展開はプリプロセッサで行われるけど、インライン化はコンパイラが行います
// インライン化の方が型宣言の指定などがきっちりできるしね

結構みんなインライン化大好きなのですが、ソースコードの量が増えていくことになるので、多分あまり大きなインライン関数を書いてしまうと、キャッシュが効きにくくなったり、ソースの肥大化によるコンパイル時間の増加(そこらじゅうでincludeされてたり)など、諸刃の剣ですけどね

STLコンテナは使用用途を考えて

vector,mapなど使うのは、楽して(作業時間をかけずに)早く作りたいときだと思います
例えばツール類やデバック環境での操作などなど

実行速度を求めだすと、やはりこれらは使えないかな。。
私的には最後はC風の自作コンテナが攻めるところになってくるのかと思います
(そこまでやっても、実は対して高速化できないんですけどねぇ
 それよりは、ポリゴン数減らす方がよっぽど速度向上になるんですけど
 プログラマの頑張りどころではあるので、こういうところに行き着くのかも。。)

オーダリングテーブルなどは高速化対象になる

オーダリングテーブルとは、3D描画処理を行う場合によく使用されます
自分(カメラ)から見て奥のもの、つまり遠くのものから書いていく必要があります
不透明なオブジェクトばかりの場合(建物とか人物とか)ピクセル単位のZバッファがかかるので、あまり意識しなくても良いのですが、半透明のオブジェクトを描画するときには、既に自分より奥のものを描画し終わっている必要があります

というわけで、奥のものから順に描画していくための仕組みがオーダリングテーブルです
プログラマはオーダリングテーブルに自分の描画したいポリゴンを登録しておけば
あとは描画システムがそのテーブル順に描画していくことになります

例えばZバッファ(奥行)を1024段階(程度)に分割します
自分のオブジェクトの位置(ポジション)を1024段階のどこに相当するか、割り当てます
その後、奥から順にレンダリングしていきます

1
2
3
4
5
// 分かりやすく書くとvectorで
 vector <Ordering_tbl> ot;
 for( i = 0; i < ot.size(); ++ i ){
    ot[i].draw();
 }

こういう部分は高速化のやりがいがあるところです
使用する立場から見ると、各ot[n]の中がさらにリンクになっていると、効果抜群です
ばかみたいですが、過去に自作リンク作ったことがあるので、参考までに

他に余談ですが、いわゆる「生ポインタ」の隠ぺいなどは、実はあまり考えてなかったような気がします
生ぽ使うな!ってことは、使用者のスキルが低いときには大変有効です、新人さんと組むとか
もちろん、ケアレスミスを防ぐ意味では、有識者に対しても効果ありなのですが、そこに力を注ぐ風潮は、わたしの周りにはあんまり無かったかな。。
やっぱり昔ながらのアセンブラ上がりの方も多かったため、今の風潮にはマッチしてないのかもしれませんね

だらだらと書いてしまいました
どうぞ、今年もよろしくお願いします

comments powered by Disqus