皆様、ナマステ!
ええっとTwitterで、気になるサイトを見かけたのでそこはかとなく論破していきます。
元サイトは
高速化 - ゲームプログラミングWiki
https://www.c3.club.kyutech.ac.jp/gamewiki/index.php?%B9%E2%C2%AE%B2%BD
です。
元記事がDirectXが動く環境を前提に話を進めているので、この記事もその前提でいきます。HW性能によってやるべきことが違うのは当然ですし。
それではありきたりな論破を。
追記
高速化についてちゃんと真面目に書いたものは
艦これシミュレーターを作ったときとかに得た高速化のノウハウ
を御覧ください。
オペレータ・オーバーロードは使うな
主張としては
void Test()
{
D3DXVECTOR3 a(0,1,2),b(1,-1,2),c(0,2,3),d;
d = a + b - c;
}
のようなコードは一時オブジェクトを生成するから使わず
void MyAdd(D3DXVECTOR3 *out,D3DXVECTOR3 *in1,D3DXVECTOR3 *in2)
{
out->x = in1->x + in2->x;
out->y = in1->y + in2->y;
out->z = in1->z + in2->z;
}
のような内部処理を期待して
void Test()
{
D3DXVECTOR3 a(0,1,2), b(1,-1,2), c(0,2,3), re;
D3DXVec3Add(&re, &a, &b);
D3DXVec3Add(&re, &re, &c);
}
と書け、というものです。
反論としては、**コンパイラの最適化舐めすぎてませんか?**という話と、仮に最適化されなかったとして一体何clockの差ですか?と言いたいですね。第一、この手の物を高速化したいなら、GPUでやるなりSIMD使わないと意味がないですね。労力の無駄。
構造体、クラスの値返しはやめよう
一見まともな主張に思えますが、解決策がダメです。
int *hoge()
{
int *tmp = new int;
*tmp = 1;
reutrn tmp;
}
int main()
{
int *tmp;
tmp = hoge;
*tmp = 5;
delete tmp;
return 0;
}
開放を忘れるリスク云々以前の問題があります。それは、動的確保、とくに小さな大きさの確保は遅いということです。
void hoge(D3DXVECTOR3 &ioVec)
{
ioVec.x = 10.0f;
ioVec.y = 2.0f;
ioVec.z = 5.0f;
}
これも話になりません。そもそもreferenceにもコストがかかかりますし、引数経由で値を返すことは著しく可読性を下げます
追記
NRVO(Named Return Value Optimization)という最適化ですね。コメントで思い出させてくれました。参考リンク貼っときます
http://isoparametric.hatenablog.com/entry/20091219/1261192152
http://d.hatena.ne.jp/gintenlabo/20110125/1295991902
http://kmc.hatenablog.jp/entry/2014/12/20/231430
つまり
D3DXVECTOR3 hoge()
{
return D3DXVECTOR3(10.0f,1.0f,10.0f);//RVOが働くことがある
}
int main()
{
auto a = hoge();//見やすい!
return 0;
}
でいいですね。
標準数学関数は使わない
話になりません。テーラー展開して近似計算しろだなんて!
標準関数を使っていれば、まともなコンパイラなら(VSですら!)SIMDの関数を呼んでくれます。
同じ出力の計算は二度としない
これも論外です。そんな作業はコンパイラにやらせるべきです。共通項のくくりだしなんて基礎的なこともできないコンパイラはゴミ箱に捨てましょう。それにさらに重要な話があります。
最近の実行環境では、CPUの計算速度よりもむしろ、メモリーアクセスの時間のほうが時間がかかります。
PMD_MT 高速化版
http://rigaya34589.blog135.fc2.com/blog-entry-483.html
現代では迷信級の話でしょう。
描画するときはポインタで
本文の主張から離れますが、std::map
をつかってはいけません。
http://qiita.com/h_hiro_/items/a83a8fd2391d4a3f0e1c
std::unorderd_map
を使用しましょう。
で本題の主張ですが、std::vector
かstd::unorderd_map
でいいです。もちろん画像のベクターだ、というなら話は別ですが、DirectXでもOpenGLでもOpenCVでも、手にしているのはすべて画像のハンドルのはずです。int型2つとさして変わらない大きさです。悩んでる暇があったらstd::vector
かstd::unorderd_map
使いましょう。Range-based forで簡単に書けるし。
あと
でもこの場合keyの前のデータが開放された場合にはデータを取得できない場合があります。 そうなると面倒です。
と
そこで描画するオブジェクトに毎フレームKeyを渡してデータを取得しないで、KeyをオブジェクトにSetするときにテクスチャのポインタをオブジェクトに渡すようにします。 こうすればO(1)で描画することができます。 こうすれば描画速度がかなり上がり、std::mapで管理できると思います。
は何が言いたいかわからん。
てかそんなことより、毎frame更新しない部分を予め描画してとっとけ。GPUのVRAMに乗っかることだろうからな。
最後に
プログラムの高速化、最適化は時代によって、機械の性能によって大きく異なります。ある場面では正しくても他の場面では正しくありません。
しかしコンパイラはたいていのプログラマなんかよりよほど優秀です。したがって大事なのは
- コードの可読性を上げる
- コンパイラの最適化を邪魔しない
- アルゴリズムを綿密に検討する
- 自分でコードを書かない
- どうしても高速化したいならアルゴリズム変えろ、SIMD使え
- 動的確保するときは頻繁に確保・開放するな
- moveの概念を覚えろ
というものになります。
あ、名誉のために元サイトの「最後に」を引用しておきます。まったくそのとおりなので。
最後に一つ語ろうと思います。 この高速化はもちろんソースコードを書くときにも大事です。 ですが、高速化を意識してるあまりスパゲティーコードになってしまうことも多々あります。 あくまでも見やすいプログラムを重点的においてください。
そしてゲームができたら、一番最後に高速化も忘れないでください。 そのときにプロファイラを使えばかなり有効なはずです。