0
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

4,342

Published on

0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,342
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
23
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Transcript of "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 時に全てのオブジェクトを破棄し たい
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×