Hatena::ブログ(Diary)

Ideals and Reality このページをアンテナに追加 RSSフィード

2012-02-12

Boost.勉強会 #8 大阪で発表してきた

Boost.勉強会 #8 大阪へいってきました。

そして闇の軍団に囲まれながら喋ってきました。



内容はメモリを管理しつつ様々な恩恵を受けられるカスタムメモリマネージャの話と非常に高速に動作するメモリアロケータの話です。

ゲームでは割りと一般的な話ですが、あまり他の業界のプログラマには知られていないのかなと思い、今回の発表内容にしました。

更に今回の発表中にVimを使ったライブコーディング的な実演も挟みました。

その時に使ったコードもGitHubで公開しています。


MemoryMaster


実はまだこのメモリマネージャとメモリプールは問題が多いので実用は出来ないです。

具体的に言うと、

等々…


ちなみに発表した内容の機能はほとんど実装されていません。

もう少し機能拡張はしたいなと思っています。

とりあえずエラーチェック辺りは。

あとは単にmallocを使うのではなく、その内部も高速なものにしてみたいですね。

2011-12-25

ゲームプログラマのためのC++を読んだ

三宅陽一郎さん監修の本がまたでたという事で購入。

一部の人にとっては待望のその名も「ゲームプログラマのためのC++」です。

どういう本だったか紹介していきたいと思います。


入門書とEffective C++の間くらいの立ち位置の本

本書は所謂C++の入門書ではないことは間違いありません。

C++の構文を教えてくれるみたいなところは一切なく、様々なノウハウやテクニックを教えてくれる本です。

継承の使い方、const、参照、キャスト、テンプレート、例外、RTTI、デザインパターンSTLなど扱う分野はとても広いです。

そのどれもゲームを作る前提の話で書かれているため、例となっているコードもゲームの話がほとんど。

しかし、ゲームにおいて例外やRTTIの話はほとんど見かけないためそういう意味では貴重な資料だと思います。

Effective C++ほど濃くはないものの、そこそこに深い話も多いです。


メモリ周りは特に詳しい

ゲーム開発において一番気になるところはやはりメモリ周りの情報です。

本書はそこに関してかなりの情報量を割いており、ことあるごとにメモリの話が出てくるので安心?出来る内容です。

特にメモリ割り当てとカスタムメモリマネージャ、メモリプールの話はゲームにおいてほぼ必須な内容なので知らない人は知る必要があります。

もちろん知らなくてもゲームを作ることは出来ますが、これを知っているだけで限界をどこまで引き伸ばせるか差が出るでしょう。


パフォーマンス戦略

多くの章ではパフォーマンス戦略やコスト分析の話が載っており、何が重くて何が軽いかの情報があります。

当然便利なものは多くのコストがかかりますが、それを踏まえてもC++のこの機能は使用すべきだろうという指針を与えてくれます。

何よりもまずは計測重視で、重いか軽いかは実際に使用してみて計測してから検討すべきだろうと教えてくれます。

ここらへんは当たり前の話ですが、私が知る限りでは「とりあえず遅いみたいだから使わない」という人は沢山いました。

例外やRTTIは特にその最たるものですが、まずは使わないという選択肢ではなくて使ってみてから考えよう。


何気に詳しいSTL

ゲーム開発においてよく避けられるSTLことStandard Template Libraryですが、この本では積極的に活用しようという事でかなりページを割いて解説してくれます。

もちろんメリットデメリットをしっかりと理解した上で使用するということになっていますが、デメリットよりもメリットが勝るくらいの魅力があるという事を有名なゲーム開発者が教えてくれるというのは素晴しい事です。

この本ではSTLの初級解説から応用方法までしっかりと解説してくれてSTLを知らない開発者を助けてくれる内容になっています。

STLを知らない開発者はこのためにこの本を読む価値があると言ってもいいくらい分かりやすい解説だと思います。

本当に少しだけですが、Boost Libraryの紹介もあったりとこの手の本にしては珍しい内容だと思いました。


最後に

この本を読んでも残念ながらゲームを作れるようにはなりません。

なぜならゲームの作り方は一切解説してくれないからです。

C++を使用していて当然の事を出来るようにしてくれる本です。

そして出来あがるものの品質を何段階か引き上げてくれる事は間違いないでしょう。

大型プロジェクトでの対処法、クラッシュしないゲームの作り方、とても大切な事です。

ですが、それが出来ていない開発者はとても沢山います。

そういった人達に向けてぜひ本書は読んでもらいたい内容だと思います。

2011-12-14

rvalue reference、move semanticsに挫折した人のための再入門解説

この記事は C++11 Advent Calendar 14日目の記事です。

何がわかりにくいのか

C++11から正式に導入される新機能である、move semanticsとrvalue referenceですが、なかなかこれを理解出来ないという人が多いようです。

ちょっと考えればなるほど、と思いますが私が思うにこのわかりにくい最大要因はやはりその名前のせいかと思います。

特にrvalue referenceですが、日本語に訳すと右辺値参照という呼ばれ方をしています。

そもそも右辺値や左辺値という名称はC言語時代の流れでそのまま引き継いでいるものであり、右辺値参照という名称は言わば後付けで名称されたものです。

実際に使用する際に右辺値なのか左辺値なのかを意識している人は殆どおらず、これが余計な混乱を招いているのではないかと思います。

ではコードを書いてみて理解しましょう。


lvalueとrvalue

int main()
{
  int a = 0;
  a;  // lvalue
  0;  // rvalue
}

とても単純なコードです。

これが理解出来ない人がいるとなるとさすがに私でも教えられる気がしないので1度C言語入門からやり直すことをオススメします。

そしてこれをみてなんとなく理解出来た人は既にrvalueを殆ど理解していると言えます。


そう、lvalueとrvalueの最大の違いというのは"名前を持つオブジェクト"と"名前を持たない無名オブジェクト"ということです。

named valueとunnamed valueとでも呼んだ方がわかりやすいでしょう。

次回からは"lvalue -> named value"、"rvalue -> unnamed value"と置き換えて読んでみてください。

それでは別の例もみてみましょう。

struct A {};
int func() { return 0; }

int main()
{
  A a;
  a;      // lvalue
  A();    // rvalue
  func(); // rvalue
}

もう説明するまでもないでしょう。

つまり、rvalueとは生まれた次の瞬間には死ななくてはならないオブジェクトのことなのです。

逆に言えば生まれたあとも自分で寿命管理出来るオブジェクトはlvalueということです。

ではrvalue referenceというのはその名の通りrvalueへの参照というだけです。


rvalue reference

struct A {};

int main()
{
  A a;

  // lvalue reference
  A& lr1 = a;     // 問題ない
  A& lr2 = A();   // lvalue referneceへrvalueを渡そうとしているのでerror

  // rvalue reference
  A&& rr1 = a;    // rvalue referenceへlvalueを渡そうとしているのでerror
  A&& rr2 = A();  // 問題ない
}

"A&& rr"という部分が実際にrvalue referenceとして受け取っている部分です。

要はこれだけという話なのですが、ひとつ注意しなくてはならないのが、rvalue referenceとして受け取ったオブジェクトはrvalueではなくて、lvalueということです。

なぜなら最初にも言った通り"名前を持つオブジェクト"になるからです。

rvalue reference型の変数はrvalueではなくて、rvalueへの参照が受け取れるだけで通常の変数と変わらないということです。


move semantics

そして出てきましたmove semanticsさん。

言葉で説明するのはなかなか難しいのですが、もの凄く単純な話「値のセマンティクスを移動させる」というだけのことです。

そのまんまじゃねーかバカやろうというツッコミがあると思いますが、とりあえずセマンティクスの意味に誤解がないように説明リンクを。


セマンティクスとは

http://www.kab-studio.biz/Programing/JavaA2Z/Word/00000315.html


セマンティクスの意味という意味がわからない説明ですが、とりあえず理解は出来るでしょう。

つまりセマンティクスを移動させるということはプログラムとして意味のある値を移動させるということになります。

移動させるということは一般的に代入操作で行なわれるcopy semanticsとは別物になり、移動元となった値は完全に無効値となります。

内部的にポインタだけが移動したと考えるとわかりやすいかもしれません。

実際にコードで書くとこうなります。

template<typename T>
void swap(T& a, T& b)
{
  //T tmp = a;          // これだとコピーが発生する
  T tmp = std::move(a); // Tのmove constructorが呼ばれる
  a = std::move(b);     // Tのmove assignment(T::operator(T&& b))が呼ばれる
  b = std::move(tmp);   // tmpの中身をbに移動させる。tmpの中身はなくなる
}

move semantics仕様のswap()関数です。

std::move()関数はその中で引数のlvalueをrvalueにキャストします。

そしてキャストしたrvalueをそのままmove constructor、もしくはmove assignment operatorへと渡します。

move constructorや、move assignment operatorはcopy constructorやcopy assignment operatorなどと同じようにクラスに定義出来ます。

定義しなければコンパイラが自動で生成します。


参考

http://d.hatena.ne.jp/faith_and_brave/20100331/1270020213


ちなみにstd::move()関数の他にstd::forward()という関数もありますが、これは難しい話になるのでここでは解説しません。

std::forward()に関してはきっと id:gintenlabo さんが書いてくれるはず!!


結局何が嬉しいのか

以上でmove semanticsを使用したコードが書けたわけですが、結局何が嬉しいんでしょうか?


答えはひとつ。

copy semanticsだと発生するコピーのコストがmove semanticsでは一切ないということです。

小さいオブジェクトでなら無視出来るコピーコストでも大きいオブジェクトになると問題になりがちなコピーコストが一切ありません。

BigDataなクラスでも作ってSTLコンテナに突っ込もうものなら大変なコピーコストになってしまうということです。

いちいちポインタでnewしてそれを突っ込んで消す際にdeleteなんて無駄なことは必要ありません。

コピーが発生しないので気軽にコンテナに入れ放題ということです。

では最後にSTLを使ったcopy semanticsとmove semanticsのベンチマークを比較しましょう。

#include <vector>
#include <iostream>
#include <time.h>
#include <set>
#include <algorithm>

static const int N = 3001;

std::set<int> get_set(int)
{
  std::set<int> s;
  for (int i = 0; i < N; ++i) {
    while (!s.insert(std::rand()).second) ;
  }
  return s;
}

std::vector<std::set<int> > generate()
{
  std::vector<std::set<int> > v;
  for (int i = 0; i < N; ++i) {
    v.push_back(get_set(i));
  }
  return v;
}

void output(const char* s, clock_t t1, clock_t t2)
{
  std::cout << s <<
    static_cast<float>(((t2 - t1) /
    static_cast<double>(CLOCKS_PER_SEC)))
  << std::endl;
}

float time_it()
{
  clock_t t1, t2, t3;
  clock_t t0 = clock();
  {
    std::vector<std::set<int> > v = generate();
    t1 = clock();
    output("construction ", t0, t1);
    std::sort(v.begin(), v.end());
    t2 = clock();
    output("sort ", t1, t2);
  }
  t3 = clock();
  output("destruction ", t2, t3);
  return static_cast<float>(((t3 - t0) /
         static_cast<double>(CLOCKS_PER_SEC)));
}

int main()
{
  float t = time_it();
  std::cout << "Total time = " << t << '\n';
}

以下の結果はgcc 4.5.2で最適化なしでCore i7 920 2.67GHzで比較したものです。

move semanticsは-std=gnu++0xオプションをつけてビルドしています。

// copy semantics
construction 9.182
sort 8.302
destruction 0.984
Total time = 18.468

// move semantics
construction 5.228
sort 0.041
destruction 1.015
Total time = 6.284

トータル時間で三倍程度差がでました。

特にソートに関しては殆んど時間がかかっていません、まさしく爆速。

デストラクト時に若干時間を食っているのが気になりますが、それでも誤差程度。

これが単純な結果とは言いづらいですが、それでもこれほどの差がでます。

move semantics万歳。


総括

move semanticsは誰もが幸せになれる素晴しい機能。

STLの場合はC++11対応コンパイラさえ使用していれば勝手にmove semanticsを使用します。

意識していなくても速くなるというのは素晴しい。

もちろん理解して使えばこの上ない強力な武器です、ぜひ活用しましょう。


それでは明日のid:iorateさんにバトンタッチです。


追記

lvalueではない名前つきオブジェクトがあるとツッコミを受けたので以下にまとめておきます。

やはりrvalueを完全に意識して書くのはなかなか大変かもしれません。

int& f(int& x) { return x; }  // f()の戻り値はlvalue

*new int(0); // 名前はないけどlvalue

enum { hoge };  // hogeは名前があるけどrvalue

template<char const * ch> // chはrvalueだけど、*chはlvalue

template <int I>  // Iはrvalue

追記2

今回のセマンティクスに対する解説は人によって誤解を生むものかもしれません。

そして私も誤解しているかもしれないので、あまり真に受けすぎない程度に参考にしてください。

2011-12-04

Boost.勉強会#7 東京に参加してきた

Boost.勉強会#7 東京

冒頭のprogress_display追悼式にはみんな笑った。

各々の発表内容はまとめるほど記憶が残っていないので印象に残っているところだけ。


Boostライブラリ一周の旅 - id:faith_and_brave

最近のBoostにて追加されたライブラリを軽く紹介。


個人的に特に注目したいのが計算幾何ライブラリのBoost.Geometryと新しいラムダ式を提供するBoost.Phoenix、C++11のコンテナ相当の機能を提供するBoost.ContainerとC++03でmove semanticsをエミュレーションするBoost.Move。


Boost.GeometryはBoost.FusionやBoost.Graphなどと同じようにコンセプトベースに設計されたライブラリでSTL同様、アルゴリズムとデータ構造を使い分けて使用出来ます。

ゲームなどで使えますが、まだ実用例がないと思われるので1度ちゃんと使ってみたいところです。


Boost.Phoenixは既にあるBoost.Lambdaとは違う手法でラムダ式を提供してくれるようですが、まだ私がまともにBoost.Lambdaすら使っていないのでその違いがよくわかっていません。


Boost.ContainerとBoost.Moveはそれぞれ既に連動しており、Boost.Containerを使用するとそれだけでBoost.Moveの恩恵を受けられるはず。

Boost.MoveはC++11で使用出来るmove semanticsをエミュレーションしてくれる素晴らしいライブラリですが、その中はなかなかの黒魔術が使用されているようです。

詳しいことはそのうちにでも調べてみたいなー。


clangで入門 解析戦略ー - id:fjnl

clangを使用してC++のソースコード解析を行なう話。

普段Vimでclang_completeを使用してC++のソースコード補完をしている私としては非常に興味のある話でした。

ただ、LLVMやclangの中は何度か読んだ事があるのですが、なかなか私の手に負えるようなものではありませんでした。

うーん、ドキュメントがもっとしっかりとしていればやってみる気もするんだけどなー。


Introduction to Boost.B-tree - id:eldesh

B-treeのBoost実装らしいですが、いまひとつ実用性があるのかわかりません。

個人的に質問してみたんですが、通常のsetやmapにはあるアロケータ指定がないのはちょっと致命的。

そして他にも色々と謎が多い。パフォーマンス的にも速いとは言えない?うーん…


中3女子でもわかるconstexpr - id:boleros

間違いなく今回一番濃かった発表。

C++11で新たに使用出来るようになったconstexprをフルに使用した場合どこまで出来るか試してみた、といった感じ。

コンパイル時文字列解析やコンパイル時レイトレーシングとか普通に考えたらマジキチですね…

しかしこれを理解出来る人は魔界の人間らしいので、人間界にいる私には到底理解出来ませんでした。

他にもネタ満載の発表で終始笑えるネタもあったりで満足した内容でした。


Boost.忘年会へ

その後は約50名近くで近くのバーに行き、色々飲んだりしながらベラベラと。

ここでしか聞けないような話やソーシャルゲームの裏話など、貴重なお話が沢山聞けました。

Boostスペシャルと名付けられたカクテルが本当に美味しかったです。バーのマスターもイケメンだった。

色々と満足し、二次会のカラオケで色々と別の議論をしてたりするのはお約束。

そのままバスへ乗り無事関西へと帰還しました。


今回も非常に充実していたBoost.勉強会ですが、2月にはまた大阪でもやるようです。

その時は何か発表したいような気がしますが、果たしてどうなるやら…

2011-11-22

PPPUC++読み終わった

日本語正式名称は「ストラウストラップのプログラミング入門」

原著名は「Programming Principles and Practice Using C++

実はVimテクニックバイブルと同時期に購入していたこの本ですが、なかなかゆっくりと読む時間がなくて全部読み終わるまでに時間がかかりました。

1176ページは伊達じゃないです。こんな本はそうポンポンと読めるもんじゃないです。

だからと言って読む価値がないわけではなくて、むしろこれだけは読んでおけと言わんばかりの内容です。


実際に読むべき読者層

あくまでも個人的な感想です。

  • C++をある程度知っているが本気で勉強をした事がない人
  • 仕事でC++を使っていて恥かしくないコードが書きたい人
  • C++を普段から使っているけど、標準ライブラリとかよくわかっていない人
  • C++使ってないけど、ちょっとC++を本気でやってみたくなった人
  • モダンなプログラミングを実践してみたい人
  • 組込み開発でC++を使っている人
  • たった一冊しか本が買えなくて、どんなプログラミングの本を買うべきか迷っている人

他にも色々とありそうだがこんなところで…


これは入門書?

本書は入門書と呼ばれていますが、プログラミングのプの字も知らないような人が読む本ではなくて、上記にも書いている通りある程度のプログラミング経験者向けではあります。

かと言ってそこまで難しい内容が書いているわけではありません。

あくまでも基本的なアルゴリズムやデータ構造に基づいてC++を用いた場合、どうやって実装すべきか?という現代的な実装方法を示しています。

そしてC言語などと比べた場合、何が悪くて何が良いのかを明確に示してくれます。

なので本当に細かいC++に関しての説明はありません。

が、大事なところはしっかりと説明してくれるので問題ありません。

個人的に面白かったのはゼロからスクラッチでstd::vector風のコンテナを作っていくところ。

標準ライブラリと言えば複雑なものですが、これを読めば少しはそんなイメージが払拭出来るかもしれません。


割りと上級者な人

まぁC++上級者なんてどのレベルから区切ればいいのかわかりませんが、ある程度C++の深いところを知っている人にはそこまで重要な本ではないと思います。

そういう意味では初級中級者向け。

この本を読んでみて「ああ、大体知っているし、普段からそれを実践しているよ」という人は値段相応の価値はないかもしれません。

しかし、基本を抑えておきたいという人にはオススメ出来ると思います。

私もそれが理由で購入した人なので。


結局いい本なのか?

全体的にみて素晴しい本だと思いましたが、ネックなのはやはりその圧倒的なぶ厚さです。

持ち運ぶのはもちろん、家の中で読むのも躊躇う厚さです。

それがネックと感じない人には絶対オススメ出来る内容ではないかと。