カスタムメモリマネージャと高速なメモリアロケータについて
Upcoming SlideShare
Loading in...5
×
 

カスタムメモリマネージャと高速なメモリアロケータについて

on

  • 22,645 views

 

Statistics

Views

Total Views
22,645
Views on SlideShare
17,398
Embed Views
5,247

Actions

Likes
24
Downloads
87
Comments
0

16 Embeds 5,247

http://d.hatena.ne.jp 4576
https://bozuman.cybozu.com 403
https://twitter.com 146
https://bozuman.s.cybozu.com 42
http://us-w1.rockmelt.com 25
http://faithandbrave.hateblo.jp 24
https://twimg0-a.akamaihd.net 9
https://si0.twimg.com 6
https://www.chatwork.com 3
http://tweetedtimes.com 3
http://twitter.com 2
http://translate.googleusercontent.com 2
http://207.46.192.232 2
http://webcache.googleusercontent.com 2
http://s.deeeki.com 1
http://paper.li 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

カスタムメモリマネージャと高速なメモリアロケータについて カスタムメモリマネージャと高速なメモリアロケータについて Presentation Transcript

  • カスタムメモリマネージャと高速なメモリアロケータについて @aizen76
  • 自己紹介ハンドルネーム alweiTwitter ID @aizen76alweiもしくはaizenのどちらかを使っています流浪のゲームプログラマWii、DS、PSP、PS3、3DSのゲームとか作ってた2Dゲーム好きなのに、実は3Dゲームしか作ったことがない最近触手軍団に仲間入りして、オンラインゲームを作り始めましたアジャイルとかTDDとか勉強中
  • 自己紹介根っからのVimmerだったり、Opera使いだったり、Happy Hacking Keyboard使いだったり、SKK使いだったりであとは極普通のC++er闇の軍団とかこわいなのに気づいたら周りに沢山いたりしてガクブルしてます
  • 本題
  • カスタムメモリマネージャ
  • メモリ確保を監視、もしくは変更するC++においてnew演算子でメモリを確保する際に、operatorオーバーロードでメモリ動作をフックしますinline void* operator new(std::size_t size) { return memAlloc(size);}inline void operator delete(void* deletePtr) { memFree(deletePtr);}
  • 何が嬉しいの?* メモリをどこで確保したのかわかる* 残りのメモリ量がわかる* メモリの断片化具合がわかる* フリーストアやヒープ以外からでもメモリを取得出来る* メモリアラインメントが指定出来る* 状況によってメモリセクションを使い分けることが出来る* etc...
  • 楽なことばかりではないメモリを管理するということは自分で責任を持つということメモリ関係のバグは言わば即死系なものや、メモリ破壊による追跡不能なものが多い自分のやっていることに自信がない限りは、安易にメモリ管理しようとは思わないことメモリ破壊バグで丸一日潰れるくらい覚悟する必要もコンソールゲームの世界ではメモリがカスタム出来ないと、お話にならないことが多いのでやらざるえない
  • カスタムメモリマネージャを使用しての様々な実用例
  • メモリリークを調べるnewした場所を記憶し、そのメモリがどこで確保されたかを知る確保したメモリをリスト化し、一定感覚で情報をログとして出力させる例えばゲームのステージ開始時と終了時をチェックステージ開始時と終了時で差異が出ている場合は警告として報告するなどするとすぐにリークが発覚する
  • エラーチェックカスタムメモリマネージャで確保されたメモリかをチェックメモリを確保する際にヘッダ情報を追加しておくヘッダには一意のシグニチャを付加(0xDEADC0DE等が有名)確保と開放時にこれが一致しないとエラー■ new内部AllocHeader* header = reinterpret_cast<AllocHeader*>(allocPtr)header->signature = 0xDEADC0DE;■ delete内部assert(header->signature == 0xDEADC0DE);
  • エラーチェックメモリ確保時に確保サイズ末尾にマーカー情報を埋め込むマーカーは二度と書き換えられないので、変更があったら不正扱いとしてエラー処理を行う配列などで確保した際にデータが壊れていないかに役立つ■ new内部int* marker = reinterpret_cast<int*>(memAddr + size);marker = MARKER_NUMBER;■ delete内部int* marker = reinterpret_cast<int*>(memAddr + size);assert(*marker == MARKER_NUMBER);
  • 更にもっと高度なメモリ管理
  • デフラグ・メモリコンパクション次々とメモリを確保していくと次第にメモリが断片化していく断片化は長く起動するアプリケーションほど起きやすい断片化が緩やかに進むといつかメモリが確保出来なくなる小さいデータが大量にあって大きいデータを確保すると、メモリの容量的には全然足りていても普通に確保出来ないことも■ どうする? C++以外の言語ではGCで勝手にお掃除してくれる メモリの情報を再配置(コンパクション)するしかない でもどうやって?
  • スマートポインタでポインタをラップ生ポインタをスマートポインタでラップして確保されたポインタの情報を連結リストにするポインタがスマートポインタにラップされているので、リストを辿りながら少しずつメモリの内容を移動させていくインタフェースに制限を加えれば安全にコンパクション出来るはずアンチャーテッドで有名なノーティドッグ社のゲームエンジンでは上記の方法で出来る限りスマートポインタを使用し、安全にメモリの内容を定期的にデフラグしている ※Game Engine Architectureより
  • デフラグコストの分割ゲームはリアルタイム要求がとてもきついので、単純にデフラグすればいいというものではない特にデフラグとはメモリブロックのコピーなのでとても遅い断片化は即死するわけではないので、数フレームにわけて少しずつ移動させていけばいい処理負荷が高い時などは自動的にデフラグを停止する機能があったりすれば処理落ちにも強い
  • メモリ管理はとても便利だが危険メモリを管理するメリットというのは沢山あるが、ややこしさはかなり増すので必要性がなければオススメ出来ないしかし上手くやればどんどん便利になるので、積極的に活用すべき時は活用すべし「えーマジメモリ管理!?」「メモリ管理が許されるのは小学生までだよねー」みたいに他のLLやVMで動く言語と比較されてバカにされても負けずにメモリのことを正確に知りちゃんと扱いましょう
  • 高速なメモリアロケータ
  • メモリアロケータとは一般的にはメモリを確保する仕組みである通常はフリーストア(ヒープ)上から取得するものだが、メモリアロケータを自作することにより、それ以外の場所からでも自由に取得出来るようにすることが出来る通常、mallocやnewはフリーストア(ヒープ)から取得するしかしplacement new等を使用すればそれ以外の領域からでもメモリを扱うことが出来る工夫次第でメモリ使用量を削減したり、高速に動作するアロケータを自作するようなことも可能
  • 様々なアロケータの紹介
  • スタックアロケータその名の通りスタックベースなアロケータ先頭ブロックから順にメモリを取得していきますメモリの確保と開放は必ず同じ順番にならないといけない確保されているメモリのサイズと順番がわかるため、非常に高速でかつ、無駄なメモリを使用しない欠点はメモリを自由に開放出来ない一時的に大きなテンポラリバッファが必要な場合に有効
  • 両端スタックアロケータスタックアロケータで使用する単一のメモリブロックの最下位と最上位で互いにトレードオフを行い、必要なメモリを分配しながら確保していくMidway社が開発したHydro Thunderというレースゲームでは全てのメモリ割り当てが両端スタックアロケータになっており、最下位スタックはレベルのロードやアンロードに使われ、最上位スタックはテンポラリバッファとして使用していたメモリの断片化が一切起こらず、非常に高速に動作していた
  • シングルフレームアロケータこれもスタックアロケータを使用したアロケータゲームの1フレームをメモリの寿命とする次フレームの頭で全てのメモリは開放されるので、自分でfreeやdeleteをする必要がない寿命が固定されているので自分ではメモリを制御出来ない確保したメモリを次フレームを跨って使用してはいけない扱いは難しいが割り切って使えば非常に高速
  • ダブルバッファアロケータシングルフレームアロケータをダブルバッファにしたもの確保したメモリは次のフレームまで持続し、アクティブなバッファをフレームごとに切り替えて寿命がきた時に自動で開放していく1フレームだけ長生きするので、後で確保したメモリを使用して処理させたいという時には非常に便利自分でdeleteする必要がないのも一緒
  • スモールオブジェクトアロケータModern C++ Designの作者、Andrei Alexandrescuさんが考案したアロケータ小さいオブジェクトのメモリ確保に特化している小さいオブジェクトを効率よく確保しつつサイズも抑える本当に小さいオブジェクト向けらしい具体的には32バイト程度で64バイト以上は普通に遅い?LokiというC++ライブラリの中ではあらゆるものでこのアロケータを使用し、高速に動作しているらしい
  • メモリプールアロケータ固定長サイズの領域から固定サイズのメモリを確保する必ず固定サイズなので確保時間も開放時間も常に一定断片化の心配も一切ないdeleteを忘れてもリークすることはないが、長時間開放しないとメモリプールを圧迫し、最終的にメモリが更に必要になる■有名な実装 Boost.Pool Efficient C++ MemoryPool
  • メモリプールアロケータメモリプールはかなり万能なアロケータメモリ容量を無駄に使用してしまうという欠点を除けば、newやmallocのデメリットはほぼ消しさることが出来るお手軽に使用出来るので、使わない手はないplacement newを使用することにより、実装することも出来るが自分で拡張出来るようにしておいた方が後々得することが多い特に断片化知らずなので、断片化で悩んでいた人はこれを使って解消しましょう
  • 実際にメモリプールを使ってみる
  • 汎用メモリプール!!
  • 汎用メモリプールnewで確保するものを全てメモリプールから取得メモリマネージャ内に5つ程度のメモリプールを作成しておくメモリマネージャが自動で複数あるプールから必要なメモリサイズに応じてプールを選択する大体16バイト〜256バイトくらいまでサポートすれば十分それ以上のメモリを確保する際は通常のnewの処理を行う
  • 結論汎用メモリプールが超万能STLをバリバリ使っても「もう何も怖くない」状態Boostだろうとメモリをフックしてしまえばどうにでもなるみんなもメモリプールを使って「あのライブラリがメモリ確保しまくってオセーんだよ!!」みたいな状況から脱却しましょう
  • GitHubにてソースコード公開中!! https://github.com/alwei/MemoryMaster
  • ご静聴ありがとうございました