Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Boost.Flyweight




          Presented by SubaruG
   @ Boost.勉強会 #2, 2010/09/11
Boost.Flyweight って?
●   Boost 1.38.0 から追加
    ●   ScopeExit, Swap と同期
●   (その名の通り)Flyweight パターンを実装
    ●   Wikipedia: Fly...
自己紹介
●   銀天 すばる (SubaruG)
    ●   本名: 齋藤 昂也(Takaya Saito)
               昂也(Takaya
        –   大学生のような何か
        –   基本的にポ...
発表内容
●   理論編
    ●   導入
    ●   Boost.Flyweight の利点
●
    実践編
    ●
        ぼくのかんがえたさいきょうのもじれつクラス
    ●   Key-Value Flywei...
導入
●   Boost.Flyweight って何?
    ●   とりあえず公式ドキュメントを読んでみる
        –   Flyweights are small-sized handle classes granting
   ...
const
const って何?
●   殆どの C++er には馴染み深い概念
●   定数を示す為の const
    ●   int const n = 100; int a[n]; for( int i = 0; i < n; ++i ) ~
 ...
const って要するに
●   「変更しない」ことを表明する為に使う
    ●   「プログラム中ずっと同じ値ですよ」
    ●   「受け取った参照先は書き換えませんよ」
    ●
        「最初に設定された状態を保ちますよ」...
const オブジェクト
●   最初から const として作られたオブジェクト
    ●   int const n = 100;
    ●   std::string const s = “hoge”;
    ●   while( ...
const 参照(1)
●   const なオブジェクトに対する参照
    ●   std::string const& line = *line_;
●   非 const なオブジェクトに対する参照
    ●   for( int i...
const 参照(2)
●   「参照先を変更しない」ことを示す
    ●   「変更されないものを参照する」ではない
        –   参照先が変更されたら、 const 参照も変更される
        –   参照先が変更されるこ...
部分的に const な型
●   T const* と T* const
    ●   T const*
        –   「 const 参照を示すポインタ」(オブジェクト、ではない)
        –   どのオブジェクトを参照...
const の難点
●   「変更しない」と「変更されない」は割と別物
    ●   C++ の const はそれらを一緒くたに扱ってる
    ●   const が出来た当時はそれが妥当だった
        –   T& を T co...
そこで: immutable
●   D言語では言語組み込みで実現
●   C++ ではテンプレートで実現できる
    ●
        組み込みに比べ細かくカスタマイズ可能
    ●   例えば const_cast を封じることができ...
C++ における immutable
●   C++ は値の言語
    ●   GCはない
        GCはない
    ●   基本的にオブジェクトと変数は1対1対応
●   immutable なオブジェクトは値を共有できる
   ...
どうやって実装する?
●   std::shared_ptr<T const> を使う
     template<class T>
     struct immutable {
        template<class...Args>
...
shared_ptr<T const> を使うと
●   コピーが高速に行える
    ●   高レベル領域で参照カウントの変更がボトル
        ネックになることは通常ない
    ●   RVOや move semantics もある...
で、ようやく本題
●   Flyweight デザインパターンは、この「値の共
    有」という考え方を突き詰めたもの
●   とはいえ、基本的には shared_ptr<T const>
    と全く変わらない
●
    唯一の違いは、...
Boost.Flyweight の特徴(1)
●   気軽に使える
    ●   非侵入的
        –   const T が要件を満たせば、どんな型でも Flyweight に
            できる
        –  ...
Boost.Flyweight の特徴(2)
●   パフォーマンス上の特徴
    ●   公式ページにあるのでそちらも参照
    ●   std::string のようなクラスの場合
        –   構築は遅い
        –...
例えば
●   動的型付けの言語では、文字列をキーとした
    データ構造は多い
●   文字列を等値比較することも多い
●
    動的片付け言語のインタプリタを作る場合、
    boost::flyweight<std::string>...
実際に使ってみる
●   boost::flyweight<std::string> を
    std::unordered_set に格納する
    ●   typedef boost::flyweight<std::string> fs...
もっと楽するには
●   専用のラッパークラスを作る
    ●   例:
        –   template<class charT, class traits = std::char_traits<charT>,
          ...
問題点(1)
●   ハッシュ関数はどうしよう?
    ●   アドレスを使うのが楽だが、プログラムを実行す
        る度に(下手すると実行中でも)値が変わる
    ●   空間効率を落としていいなら、flyweight
     ...
問題点(2)
●   実装がかなり面倒くさい
    ●   std::string ってメンバ関数が多い
    ●
        どこまで実装する?
●   Boost.Flyweight を使わずにチューニングし
    たほうが基本的...
より実際的な例
●   Key-Value Flyweight を使った例
●   Lua が好きなので、そのネタ
●
    何がしたい?
    ●   Lua のソースファイルを扱うクラス
    ●   読み込んだ後にコンパイル済みのチ...
Key-Value Flyweight
●   boost::flyweight<
     boost::flyweights::key_value<Key, Value>
    >
●   構築時は flyweight<Key>, 使用時...
実際に作る
●   まずファイル実体を表すクラスを作る
    ●   遅延評価を採用
        –   class lua_sourcefile_body {
             typedef boost::filesystem...
ファイルロードの実装
●   void lua_sourcefile_body::load( lua_State* L ) const {
      std::string const filename = path_.string();
 ...
後は簡単
●   ハンドルクラスを作る
    ●   struct lua_sorucefile {
         typedef boost::filesystem::path path_t;
         explicit lua...
まとめ
●   Boost.Flyweight は便利。
●   今回の例は単純なものだったが、工夫次第
    でいろいろと発展できそう
●   例えば PImpl イディオムと組み合わせる
    ●   公式曰く
         “si...
Upcoming SlideShare
Loading in …5
×

Boost.Flyweight

5,080 views

Published on

  • Be the first to comment

Boost.Flyweight

  1. 1. Boost.Flyweight Presented by SubaruG @ Boost.勉強会 #2, 2010/09/11
  2. 2. Boost.Flyweight って? ● Boost 1.38.0 から追加 ● ScopeExit, Swap と同期 ● (その名の通り)Flyweight パターンを実装 ● Wikipedia: Flyweight パターン Wikipedia: – 等価なインスタンスを別々の箇所で使用する際に、一つ のインスタンスを再利用することによってプログラムを省 リソース化する ● よくわからないけどなんかすごそう
  3. 3. 自己紹介 ● 銀天 すばる (SubaruG) ● 本名: 齋藤 昂也(Takaya Saito) 昂也(Takaya – 大学生のような何か – 基本的にポンコツ ● Blog: 銀天随筆集 Blog: – http://d.hatena.ne.jp/gintenlabo/ – 最近は Lua に浮気中 ● 野良C++er 野良C++er – ご主人様募集中
  4. 4. 発表内容 ● 理論編 ● 導入 ● Boost.Flyweight の利点 ● 実践編 ● ぼくのかんがえたさいきょうのもじれつクラス ● Key-Value Flyweight
  5. 5. 導入 ● Boost.Flyweight って何? ● とりあえず公式ドキュメントを読んでみる – Flyweights are small-sized handle classes granting constant access to shared common data, thus allowing for the management of large amounts of entities within reasonable memory limits. – Boost.Flyweight makes it easy to use this common programming idiom by providing the class template flyweight<T>, which acts as a drop-in replacement for const T. ● ん?
  6. 6. const
  7. 7. const って何? ● 殆どの C++er には馴染み深い概念 ● 定数を示す為の const ● int const n = 100; int a[n]; for( int i = 0; i < n; ++i ) ~ ● C++0x では constexpr ● パラメータの const 参照渡し ● int count_a( std::string const& x ); std::cout << count_a(“aaabc”) << std::endl; // 3 ● const ローカル変数 ● auto const iter = map.find(“hoge”); if( iter != map.end() ) ~
  8. 8. const って要するに ● 「変更しない」ことを表明する為に使う ● 「プログラム中ずっと同じ値ですよ」 ● 「受け取った参照先は書き換えませんよ」 ● 「最初に設定された状態を保ちますよ」 ● 「変更しない」ってどういうこと? ● 厳密に考えると少しばかりややこしい – const オブジェクトと const 参照 – 部分的に const な型の存在
  9. 9. const オブジェクト ● 最初から const として作られたオブジェクト ● int const n = 100; ● std::string const s = “hoge”; ● while( boost::optional<std::string> const line_ = getline_opt( std::cin ) ) { std::string const& line = *line_; /* ... */ } ● 原則として「変更されない」もの ● 例外: T* const, shared_ptr<T> const
  10. 10. const 参照(1) ● const なオブジェクトに対する参照 ● std::string const& line = *line_; ● 非 const なオブジェクトに対する参照 ● for( int i_ = 0; i_ < n; ++i_ ) { int const& i = i_; /* … */ } ● 一時オブジェクトに対する参照 ● std::string const& s = str + “n”; ● 典型的には関数の引数として使う
  11. 11. const 参照(2) ● 「参照先を変更しない」ことを示す ● 「変更されないものを参照する」ではない – 参照先が変更されたら、 const 参照も変更される – 参照先が変更されることは普通に起き得る – 多くの標準ライブラリは const 参照ではなく値渡し – const_cast ● 多くの const は、実は const 参照 ● 効率はいいが、少しばかり扱いが面倒
  12. 12. 部分的に const な型 ● T const* と T* const ● T const* – 「 const 参照を示すポインタ」(オブジェクト、ではない) – どのオブジェクトを参照するかを「再設定」できる ● T* const – 「再設定できないポインタ」。実質的に T& と同じ – T& との違いは「どこも参照してない」状態を作れる点 ● std::unique_ptr<T const> ● ちゃんと考えればわかるが、ややこしい!
  13. 13. const の難点 ● 「変更しない」と「変更されない」は割と別物 ● C++ の const はそれらを一緒くたに扱ってる ● const が出来た当時はそれが妥当だった – T& を T const& として扱えればコード量が減る – テンプレートは無かったし、有ったとしても冗長 ● 今でも妥当な部分は多い – 部分的な const は不変性を崩すが、便利 – shared_ptr<T> const とかが最たる例 ● でも、やっぱり「変更されない」方が扱いが簡単
  14. 14. そこで: immutable ● D言語では言語組み込みで実現 ● C++ ではテンプレートで実現できる ● 組み込みに比べ細かくカスタマイズ可能 ● 例えば const_cast を封じることができる – バグが入り込む余地を排除 ● 「変更できない」性質に基づく最適化が可能 – ハッシュ値などを予め計算しておける – 遅延評価することもできる(意味論的const性) 遅延評価することもできる(意味論的const性) – 値の共有
  15. 15. C++ における immutable ● C++ は値の言語 ● GCはない GCはない ● 基本的にオブジェクトと変数は1対1対応 ● immutable なオブジェクトは値を共有できる ● immutable でない場合はコピーするしかない – std::string とか std::map<Key, T> とか – Copy on Write という手法もあるが大変 ● immutable ならば簡単に共有できる ● その場合は GC が欲しい
  16. 16. どうやって実装する? ● std::shared_ptr<T const> を使う template<class T> struct immutable { template<class...Args> explicit immutable( Args&&... args ) : p_( std::make_shared<T>( std::forward<Args>(args)... ) ) {} operatpr T const& () const { return *p_; } private: std::shared_ptr<T const> p_; }; ● この場合の T const は、 const 参照ではなく const オブジェクト ● private に閉じ込めてるので不変性を保証できる
  17. 17. shared_ptr<T const> を使うと ● コピーが高速に行える ● 高レベル領域で参照カウントの変更がボトル ネックになることは通常ない ● RVOや move semantics もある RVOや ● 値を再束縛できる ●再束縛したくない場合は const を使えばいい ● 実際のところ、これは厳密には「immutable object への参照を保持するクラス」である。 ● これを単に「immutable object」と言う場合も多い。
  18. 18. で、ようやく本題 ● Flyweight デザインパターンは、この「値の共 有」という考え方を突き詰めたもの ● とはいえ、基本的には shared_ptr<T const> と全く変わらない ● 唯一の違いは、等値のオブジェクトを一つの 実体にまとめ上げてしまう点 ● 雑多なオブジェクトを大量に作る場合にリ ソースを節約できる
  19. 19. Boost.Flyweight の特徴(1) ● 気軽に使える ● 非侵入的 – const T が要件を満たせば、どんな型でも Flyweight に できる – 例: boost::flyweight<std::string> ● std::string は flyweight のために作られた型じゃない! ● T const& への暗黙変換 ● 細かく条件を設定できる ● オブジェクトを保持するデータ構造, GCの方法, オブジェクトを保持するデータ構造, GCの方法, key-value flyweight による遅延構築, etc... による遅延構築,
  20. 20. Boost.Flyweight の特徴(2) ● パフォーマンス上の特徴 ● 公式ページにあるのでそちらも参照 ● std::string のようなクラスの場合 – 構築は遅い – コピーや等値比較は非常に高速 – ハッシュ関数に保持されたオブジェクトのアドレスを渡す ことが出来る ● これらの特徴から、特に unordered な連想コ ンテナに格納する場合に非常に強力。
  21. 21. 例えば ● 動的型付けの言語では、文字列をキーとした データ構造は多い ● 文字列を等値比較することも多い ● 動的片付け言語のインタプリタを作る場合、 boost::flyweight<std::string> を使うことでパ フォーマンスの向上を期待できる ● 問題点も多い ● 非侵入的なのでそこまで高速ではない ● flyweight 側からその言語のGCを使えない 側からその言語のGCを使えない
  22. 22. 実際に使ってみる ● boost::flyweight<std::string> を std::unordered_set に格納する ● typedef boost::flyweight<std::string> fstring; struct hash_addr { std::size_t operator()( fstring const& x ) const { return std::hash<std::string const*>(&x.get()); } }; std::unordered_set<fstring, hash_addr> s; s.emplace( “hoge” ); // あるいは s.insert( fstring(“hoge”) ); s.emplace( “fuga” ); ● そのままでもそれなりに使えるが、少し面倒
  23. 23. もっと楽するには ● 専用のラッパークラスを作る ● 例: – template<class charT, class traits = std::char_traits<charT>, class Allocator = std::allocator<charT> > struct basic_flystring { typedef std::basic_string<charT, traits, Allocator> string_type; string_type const& str() const { return impl_; } /* ... */ private: boost::flyweight< string_type, boost::flyweights::hashed_factory < std::hash<string_type>, std::equal_to<string_type>, typename Allocator::template rebind<string_type>::other > > impl_; };
  24. 24. 問題点(1) ● ハッシュ関数はどうしよう? ● アドレスを使うのが楽だが、プログラムを実行す る度に(下手すると実行中でも)値が変わる ● 空間効率を落としていいなら、flyweight 空間効率を落としていいなら、flyweight に格納 する文字列にハッシュ値を紛れ込ませれる – template<class T, class Hash=std::hash<T> > struct hashed { template<class... Args> explicit hashed( Args&& ...args ) : value_( std::forward<Args>(args)... ), hash_( Hash()(value_) ) {} operator T const&() const { return value; } std::size_t hash() const { return hash_; } private: T value_; std::size_t hash_; };
  25. 25. 問題点(2) ● 実装がかなり面倒くさい ● std::string ってメンバ関数が多い ● どこまで実装する? ● Boost.Flyweight を使わずにチューニングし たほうが基本的に速い ● いったん std::string を作ってから比較し、必要 なら登録する、という無駄な処理を行っている ● 文字列なら trie を使えばさらに効率化出来る? ● 誰かやってみてください。
  26. 26. より実際的な例 ● Key-Value Flyweight を使った例 ● Lua が好きなので、そのネタ ● 何がしたい? ● Lua のソースファイルを扱うクラス ● 読み込んだ後にコンパイル済みのチャンクを保 存し、対象ファイルが更新されてない限り、次か らはそちらを使うことで高速化 ● ファイル名を Key とした Flyweight で実現
  27. 27. Key-Value Flyweight ● boost::flyweight< boost::flyweights::key_value<Key, Value> > ● 構築時は flyweight<Key>, 使用時は flyweight<Value> として扱える ● 言い換えると、 Value の構築は遅延される ● 詳しくは公式のチュートリアルを参照
  28. 28. 実際に作る ● まずファイル実体を表すクラスを作る ● 遅延評価を採用 – class lua_sourcefile_body { typedef boost::filesystem::path path_t; explicit lua_sourcefile_body( path_t const& path ) : path_(path), timestamp_(), chunk_() {} void load( lua_State* ) const; private: boost::filesystem::path path_; mutable std::time_t timestamp_; mutable std::vector<char> chunk_; bool up_to_date_() const { return !chunk_.empty() && timestamp_ == last_write_time(path_); } };
  29. 29. ファイルロードの実装 ● void lua_sourcefile_body::load( lua_State* L ) const { std::string const filename = path_.string(); if( up_to_date_() ) { luaL_loadbuffer( L, chunk_.data(), chunk_.size(), filename.c_str() ); } else { luaL_loadfile( L, filename.c_str() ); chunk_.clear(); auto const dump_f = []( lua_State* L, void const* p_, std::size_t sz, void* ud ) -> int { auto& chunk = *static_cast<std::vector<char>*>(ud); char const* const p = static_cast<char const*>(p_); chunk.insert( chunk.end(), p, p + sz ); return 0; }; lua_dump( L, dump_f, &chunk_ ); timestamp_ = last_write_time(path_); } }
  30. 30. 後は簡単 ● ハンドルクラスを作る ● struct lua_sorucefile { typedef boost::filesystem::path path_t; explicit lua_sourcefile( path_t const& path ) : impl_( path ) {} void load( lua_State* L ) const { impl_.get().load(L); } private: boost::flyweight< boost::flyweights::key_value<path_t, lua_sourcefile_body> > impl_; }; ● おしまい。
  31. 31. まとめ ● Boost.Flyweight は便利。 ● 今回の例は単純なものだったが、工夫次第 でいろいろと発展できそう ● 例えば PImpl イディオムと組み合わせる ● 公式曰く “sizeof(flyweight<T>)=sizeof(void*)” なので、 reinterpret_cast を使える ● 興味があれば是非とも使ってみてください
  32. 32. 捕捉 ● no_tracking 時に全てのオブジェクトを破棄し たい

×