SlideShare une entreprise Scribd logo
1  sur  31
Télécharger pour lire hors ligne
Boost.Flyweight




          Presented by SubaruG
   @ Boost.勉強会 #2, 2010/09/11
Boost.Flyweight って?
●   Boost 1.38.0 から追加
    ●   ScopeExit, Swap と同期
●   (その名の通り)Flyweight パターンを実装
    ●   Wikipedia: Flyweight パターン
        Wikipedia:
        –   等価なインスタンスを別々の箇所で使用する際に、一つ
            のインスタンスを再利用することによってプログラムを省
            リソース化する
●   よくわからないけどなんかすごそう
自己紹介
●   銀天 すばる (SubaruG)
    ●   本名: 齋藤 昂也(Takaya Saito)
               昂也(Takaya
        –   大学生のような何か
        –   基本的にポンコツ
    ●   Blog: 銀天随筆集
        Blog:
        –   http://d.hatena.ne.jp/gintenlabo/
        –   最近は Lua に浮気中
    ●   野良C++er
        野良C++er
        –   ご主人様募集中
発表内容
●   理論編
    ●   導入
    ●   Boost.Flyweight の利点
●
    実践編
    ●
        ぼくのかんがえたさいきょうのもじれつクラス
    ●   Key-Value Flyweight
導入
●   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.
    ●   ん?
const
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() ) ~
const って要するに
●   「変更しない」ことを表明する為に使う
    ●   「プログラム中ずっと同じ値ですよ」
    ●   「受け取った参照先は書き換えませんよ」
    ●
        「最初に設定された状態を保ちますよ」
●
    「変更しない」ってどういうこと?
    ●
        厳密に考えると少しばかりややこしい
        –   const オブジェクトと const 参照
        –   部分的に const な型の存在
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
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”;
●
    典型的には関数の引数として使う
const 参照(2)
●   「参照先を変更しない」ことを示す
    ●   「変更されないものを参照する」ではない
        –   参照先が変更されたら、 const 参照も変更される
        –   参照先が変更されることは普通に起き得る
        –   多くの標準ライブラリは const 参照ではなく値渡し
        –   const_cast
●   多くの const は、実は const 参照
●   効率はいいが、少しばかり扱いが面倒
部分的に const な型
●   T const* と T* const
    ●   T const*
        –   「 const 参照を示すポインタ」(オブジェクト、ではない)
        –   どのオブジェクトを参照するかを「再設定」できる
    ●   T* const
        –   「再設定できないポインタ」。実質的に T& と同じ
        –   T& との違いは「どこも参照してない」状態を作れる点
●   std::unique_ptr<T const>
●   ちゃんと考えればわかるが、ややこしい!
const の難点
●   「変更しない」と「変更されない」は割と別物
    ●   C++ の const はそれらを一緒くたに扱ってる
    ●   const が出来た当時はそれが妥当だった
        –   T& を T const& として扱えればコード量が減る
        –   テンプレートは無かったし、有ったとしても冗長
    ●   今でも妥当な部分は多い
        –   部分的な const は不変性を崩すが、便利
        –   shared_ptr<T> const とかが最たる例
    ●   でも、やっぱり「変更されない」方が扱いが簡単
そこで: immutable
●   D言語では言語組み込みで実現
●   C++ ではテンプレートで実現できる
    ●
        組み込みに比べ細かくカスタマイズ可能
    ●   例えば const_cast を封じることができる
        –   バグが入り込む余地を排除
    ●   「変更できない」性質に基づく最適化が可能
        –   ハッシュ値などを予め計算しておける
        –   遅延評価することもできる(意味論的const性)
            遅延評価することもできる(意味論的const性)
        –   値の共有
C++ における immutable
●   C++ は値の言語
    ●   GCはない
        GCはない
    ●   基本的にオブジェクトと変数は1対1対応
●   immutable なオブジェクトは値を共有できる
    ●   immutable でない場合はコピーするしかない
        –   std::string とか std::map<Key, T> とか
        –   Copy on Write という手法もあるが大変
    ●   immutable ならば簡単に共有できる
    ●   その場合は GC が欲しい
どうやって実装する?
●   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 に閉じ込めてるので不変性を保証できる
shared_ptr<T const> を使うと
●   コピーが高速に行える
    ●   高レベル領域で参照カウントの変更がボトル
        ネックになることは通常ない
    ●   RVOや move semantics もある
        RVOや
●
    値を再束縛できる
    ●再束縛したくない場合は const を使えばいい
●   実際のところ、これは厳密には「immutable object
    への参照を保持するクラス」である。
●   これを単に「immutable object」と言う場合も多い。
で、ようやく本題
●   Flyweight デザインパターンは、この「値の共
    有」という考え方を突き詰めたもの
●   とはいえ、基本的には shared_ptr<T const>
    と全く変わらない
●
    唯一の違いは、等値のオブジェクトを一つの
    実体にまとめ上げてしまう点
●   雑多なオブジェクトを大量に作る場合にリ
    ソースを節約できる
Boost.Flyweight の特徴(1)
●   気軽に使える
    ●   非侵入的
        –   const T が要件を満たせば、どんな型でも Flyweight に
            できる
        –   例: boost::flyweight<std::string>
            ●   std::string は flyweight のために作られた型じゃない!
    ●   T const& への暗黙変換
●
    細かく条件を設定できる
    ●   オブジェクトを保持するデータ構造, GCの方法,
        オブジェクトを保持するデータ構造, GCの方法,
        key-value flyweight による遅延構築, etc...
                            による遅延構築,
Boost.Flyweight の特徴(2)
●   パフォーマンス上の特徴
    ●   公式ページにあるのでそちらも参照
    ●   std::string のようなクラスの場合
        –   構築は遅い
        –   コピーや等値比較は非常に高速
        –   ハッシュ関数に保持されたオブジェクトのアドレスを渡す
            ことが出来る
●   これらの特徴から、特に unordered な連想コ
    ンテナに格納する場合に非常に強力。
例えば
●   動的型付けの言語では、文字列をキーとした
    データ構造は多い
●   文字列を等値比較することも多い
●
    動的片付け言語のインタプリタを作る場合、
    boost::flyweight<std::string> を使うことでパ
    フォーマンスの向上を期待できる
●   問題点も多い
    ●   非侵入的なのでそこまで高速ではない
    ●   flyweight 側からその言語のGCを使えない
                  側からその言語のGCを使えない
実際に使ってみる
●   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” );
●
    そのままでもそれなりに使えるが、少し面倒
もっと楽するには
●   専用のラッパークラスを作る
    ●   例:
        –   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_;
            };
問題点(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_;
            };
問題点(2)
●   実装がかなり面倒くさい
    ●   std::string ってメンバ関数が多い
    ●
        どこまで実装する?
●   Boost.Flyweight を使わずにチューニングし
    たほうが基本的に速い
    ●   いったん std::string を作ってから比較し、必要
        なら登録する、という無駄な処理を行っている
    ●   文字列なら trie を使えばさらに効率化出来る?
    ●   誰かやってみてください。
より実際的な例
●   Key-Value Flyweight を使った例
●   Lua が好きなので、そのネタ
●
    何がしたい?
    ●   Lua のソースファイルを扱うクラス
    ●   読み込んだ後にコンパイル済みのチャンクを保
        存し、対象ファイルが更新されてない限り、次か
        らはそちらを使うことで高速化
    ●   ファイル名を Key とした Flyweight で実現
Key-Value Flyweight
●   boost::flyweight<
     boost::flyweights::key_value<Key, Value>
    >
●   構築時は flyweight<Key>, 使用時は
    flyweight<Value> として扱える
●   言い換えると、 Value の構築は遅延される
●
    詳しくは公式のチュートリアルを参照
実際に作る
●   まずファイル実体を表すクラスを作る
    ●   遅延評価を採用
        –   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_);
              }
            };
ファイルロードの実装
●   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_);
      }
    }
後は簡単
●   ハンドルクラスを作る
    ●   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_;
        };
●   おしまい。
まとめ
●   Boost.Flyweight は便利。
●   今回の例は単純なものだったが、工夫次第
    でいろいろと発展できそう
●   例えば PImpl イディオムと組み合わせる
    ●   公式曰く
         “sizeof(flyweight<T>)=sizeof(void*)”
        なので、 reinterpret_cast を使える
●   興味があれば是非とも使ってみてください

Contenu connexe

Tendances

JDK 10 へようこそ
JDK 10 へようこそJDK 10 へようこそ
JDK 10 へようこそDavid Buck
 
不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarrayRyosuke839
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだGenya Murakami
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)信之 岩永
 
Qt Creator を拡張する
Qt Creator を拡張するQt Creator を拡張する
Qt Creator を拡張するTakumi Asaki
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexprGenya Murakami
 
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Keisuke Fukuda
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門natrium11321
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPUTakuro Iizuka
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるHideyuki Tanaka
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由kikairoya
 
boost::shared_ptr tutorial
boost::shared_ptr tutorialboost::shared_ptr tutorial
boost::shared_ptr tutorialNU_Pan
 
Impractical Introduction of Boost Spirit Qi [PPT]
Impractical Introduction of Boost Spirit Qi [PPT]Impractical Introduction of Boost Spirit Qi [PPT]
Impractical Introduction of Boost Spirit Qi [PPT]yak1ex
 
NumPyが物足りない人へのCython入門
NumPyが物足りない人へのCython入門NumPyが物足りない人へのCython入門
NumPyが物足りない人へのCython入門Shiqiao Du
 
新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案yohhoy
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会Akihiko Matuura
 

Tendances (20)

JDK 10 へようこそ
JDK 10 へようこそJDK 10 へようこそ
JDK 10 へようこそ
 
不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray不遇の標準ライブラリ - valarray
不遇の標準ライブラリ - valarray
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
 
Qt Creator を拡張する
Qt Creator を拡張するQt Creator を拡張する
Qt Creator を拡張する
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr
 
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
Emcpp item41
Emcpp item41Emcpp item41
Emcpp item41
 
Boost tour 1.60.0 merge
Boost tour 1.60.0 mergeBoost tour 1.60.0 merge
Boost tour 1.60.0 merge
 
Qt小技(修正版)
Qt小技(修正版)Qt小技(修正版)
Qt小技(修正版)
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
boost::shared_ptr tutorial
boost::shared_ptr tutorialboost::shared_ptr tutorial
boost::shared_ptr tutorial
 
Impractical Introduction of Boost Spirit Qi [PPT]
Impractical Introduction of Boost Spirit Qi [PPT]Impractical Introduction of Boost Spirit Qi [PPT]
Impractical Introduction of Boost Spirit Qi [PPT]
 
NumPyが物足りない人へのCython入門
NumPyが物足りない人へのCython入門NumPyが物足りない人へのCython入門
NumPyが物足りない人へのCython入門
 
新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会
 

Similaire à Boost.Flyweight

わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61TATSUYA HAYAMIZU
 
Learning Template Library Design using Boost.Geomtry
Learning Template Library Design using Boost.GeomtryLearning Template Library Design using Boost.Geomtry
Learning Template Library Design using Boost.GeomtryAkira Takahashi
 
Flutterを体験してみませんか
Flutterを体験してみませんかFlutterを体験してみませんか
Flutterを体験してみませんかcch-robo
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
Python standard 2022 Spring
Python standard 2022 SpringPython standard 2022 Spring
Python standard 2022 Springanyakichi
 
第2回勉強会スライド
第2回勉強会スライド第2回勉強会スライド
第2回勉強会スライドkoturn 0;
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#信之 岩永
 
Boost.B-tree introduction
Boost.B-tree introductionBoost.B-tree introduction
Boost.B-tree introductionTakayuki Goto
 
規格書で読むC++11のスレッド
規格書で読むC++11のスレッド規格書で読むC++11のスレッド
規格書で読むC++11のスレッドKohsuke Yuasa
 
すごいConstたのしく使おう!
すごいConstたのしく使おう!すごいConstたのしく使おう!
すごいConstたのしく使おう!Akihiro Nishimura
 
WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!
WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!
WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!文樹 高橋
 
C++ lecture-0
C++ lecture-0C++ lecture-0
C++ lecture-0sunaemon
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門伸男 伊藤
 
Write good parser in perl
Write good parser in perlWrite good parser in perl
Write good parser in perlJiro Nishiguchi
 

Similaire à Boost.Flyweight (20)

わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
Learning Template Library Design using Boost.Geomtry
Learning Template Library Design using Boost.GeomtryLearning Template Library Design using Boost.Geomtry
Learning Template Library Design using Boost.Geomtry
 
Flutterを体験してみませんか
Flutterを体験してみませんかFlutterを体験してみませんか
Flutterを体験してみませんか
 
Boost Fusion Library
Boost Fusion LibraryBoost Fusion Library
Boost Fusion Library
 
What is template
What is templateWhat is template
What is template
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
emc++ chapter32
emc++ chapter32emc++ chapter32
emc++ chapter32
 
Python standard 2022 Spring
Python standard 2022 SpringPython standard 2022 Spring
Python standard 2022 Spring
 
第2回勉強会スライド
第2回勉強会スライド第2回勉強会スライド
第2回勉強会スライド
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#
 
Map
MapMap
Map
 
Boost.B-tree introduction
Boost.B-tree introductionBoost.B-tree introduction
Boost.B-tree introduction
 
規格書で読むC++11のスレッド
規格書で読むC++11のスレッド規格書で読むC++11のスレッド
規格書で読むC++11のスレッド
 
すごいConstたのしく使おう!
すごいConstたのしく使おう!すごいConstたのしく使おう!
すごいConstたのしく使おう!
 
Tokyor23 doradora09
Tokyor23 doradora09Tokyor23 doradora09
Tokyor23 doradora09
 
WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!
WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!
WPD-Fes #3 2015年のサバイバル学習術 Web開発技術の税引後利益 を最大化しよう!
 
C++14 Overview
C++14 OverviewC++14 Overview
C++14 Overview
 
C++ lecture-0
C++ lecture-0C++ lecture-0
C++ lecture-0
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
 
Write good parser in perl
Write good parser in perlWrite good parser in perl
Write good parser in perl
 

Boost.Flyweight

  • 1. Boost.Flyweight Presented by SubaruG @ Boost.勉強会 #2, 2010/09/11
  • 2. Boost.Flyweight って? ● Boost 1.38.0 から追加 ● ScopeExit, Swap と同期 ● (その名の通り)Flyweight パターンを実装 ● Wikipedia: Flyweight パターン Wikipedia: – 等価なインスタンスを別々の箇所で使用する際に、一つ のインスタンスを再利用することによってプログラムを省 リソース化する ● よくわからないけどなんかすごそう
  • 3. 自己紹介 ● 銀天 すばる (SubaruG) ● 本名: 齋藤 昂也(Takaya Saito) 昂也(Takaya – 大学生のような何か – 基本的にポンコツ ● Blog: 銀天随筆集 Blog: – http://d.hatena.ne.jp/gintenlabo/ – 最近は Lua に浮気中 ● 野良C++er 野良C++er – ご主人様募集中
  • 4. 発表内容 ● 理論編 ● 導入 ● Boost.Flyweight の利点 ● 実践編 ● ぼくのかんがえたさいきょうのもじれつクラス ● Key-Value Flyweight
  • 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. ● ん?
  • 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. const って要するに ● 「変更しない」ことを表明する為に使う ● 「プログラム中ずっと同じ値ですよ」 ● 「受け取った参照先は書き換えませんよ」 ● 「最初に設定された状態を保ちますよ」 ● 「変更しない」ってどういうこと? ● 厳密に考えると少しばかりややこしい – const オブジェクトと const 参照 – 部分的に const な型の存在
  • 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. 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. const 参照(2) ● 「参照先を変更しない」ことを示す ● 「変更されないものを参照する」ではない – 参照先が変更されたら、 const 参照も変更される – 参照先が変更されることは普通に起き得る – 多くの標準ライブラリは const 参照ではなく値渡し – const_cast ● 多くの const は、実は const 参照 ● 効率はいいが、少しばかり扱いが面倒
  • 12. 部分的に const な型 ● T const* と T* const ● T const* – 「 const 参照を示すポインタ」(オブジェクト、ではない) – どのオブジェクトを参照するかを「再設定」できる ● T* const – 「再設定できないポインタ」。実質的に T& と同じ – T& との違いは「どこも参照してない」状態を作れる点 ● std::unique_ptr<T const> ● ちゃんと考えればわかるが、ややこしい!
  • 13. const の難点 ● 「変更しない」と「変更されない」は割と別物 ● C++ の const はそれらを一緒くたに扱ってる ● const が出来た当時はそれが妥当だった – T& を T const& として扱えればコード量が減る – テンプレートは無かったし、有ったとしても冗長 ● 今でも妥当な部分は多い – 部分的な const は不変性を崩すが、便利 – shared_ptr<T> const とかが最たる例 ● でも、やっぱり「変更されない」方が扱いが簡単
  • 14. そこで: immutable ● D言語では言語組み込みで実現 ● C++ ではテンプレートで実現できる ● 組み込みに比べ細かくカスタマイズ可能 ● 例えば const_cast を封じることができる – バグが入り込む余地を排除 ● 「変更できない」性質に基づく最適化が可能 – ハッシュ値などを予め計算しておける – 遅延評価することもできる(意味論的const性) 遅延評価することもできる(意味論的const性) – 値の共有
  • 15. C++ における immutable ● C++ は値の言語 ● GCはない GCはない ● 基本的にオブジェクトと変数は1対1対応 ● immutable なオブジェクトは値を共有できる ● immutable でない場合はコピーするしかない – std::string とか std::map<Key, T> とか – Copy on Write という手法もあるが大変 ● immutable ならば簡単に共有できる ● その場合は GC が欲しい
  • 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. shared_ptr<T const> を使うと ● コピーが高速に行える ● 高レベル領域で参照カウントの変更がボトル ネックになることは通常ない ● RVOや move semantics もある RVOや ● 値を再束縛できる ●再束縛したくない場合は const を使えばいい ● 実際のところ、これは厳密には「immutable object への参照を保持するクラス」である。 ● これを単に「immutable object」と言う場合も多い。
  • 18. で、ようやく本題 ● Flyweight デザインパターンは、この「値の共 有」という考え方を突き詰めたもの ● とはいえ、基本的には shared_ptr<T const> と全く変わらない ● 唯一の違いは、等値のオブジェクトを一つの 実体にまとめ上げてしまう点 ● 雑多なオブジェクトを大量に作る場合にリ ソースを節約できる
  • 19. Boost.Flyweight の特徴(1) ● 気軽に使える ● 非侵入的 – const T が要件を満たせば、どんな型でも Flyweight に できる – 例: boost::flyweight<std::string> ● std::string は flyweight のために作られた型じゃない! ● T const& への暗黙変換 ● 細かく条件を設定できる ● オブジェクトを保持するデータ構造, GCの方法, オブジェクトを保持するデータ構造, GCの方法, key-value flyweight による遅延構築, etc... による遅延構築,
  • 20. Boost.Flyweight の特徴(2) ● パフォーマンス上の特徴 ● 公式ページにあるのでそちらも参照 ● std::string のようなクラスの場合 – 構築は遅い – コピーや等値比較は非常に高速 – ハッシュ関数に保持されたオブジェクトのアドレスを渡す ことが出来る ● これらの特徴から、特に unordered な連想コ ンテナに格納する場合に非常に強力。
  • 21. 例えば ● 動的型付けの言語では、文字列をキーとした データ構造は多い ● 文字列を等値比較することも多い ● 動的片付け言語のインタプリタを作る場合、 boost::flyweight<std::string> を使うことでパ フォーマンスの向上を期待できる ● 問題点も多い ● 非侵入的なのでそこまで高速ではない ● flyweight 側からその言語のGCを使えない 側からその言語のGCを使えない
  • 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. もっと楽するには ● 専用のラッパークラスを作る ● 例: – 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. 問題点(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. 問題点(2) ● 実装がかなり面倒くさい ● std::string ってメンバ関数が多い ● どこまで実装する? ● Boost.Flyweight を使わずにチューニングし たほうが基本的に速い ● いったん std::string を作ってから比較し、必要 なら登録する、という無駄な処理を行っている ● 文字列なら trie を使えばさらに効率化出来る? ● 誰かやってみてください。
  • 26. より実際的な例 ● Key-Value Flyweight を使った例 ● Lua が好きなので、そのネタ ● 何がしたい? ● Lua のソースファイルを扱うクラス ● 読み込んだ後にコンパイル済みのチャンクを保 存し、対象ファイルが更新されてない限り、次か らはそちらを使うことで高速化 ● ファイル名を Key とした Flyweight で実現
  • 27. Key-Value Flyweight ● boost::flyweight< boost::flyweights::key_value<Key, Value> > ● 構築時は flyweight<Key>, 使用時は flyweight<Value> として扱える ● 言い換えると、 Value の構築は遅延される ● 詳しくは公式のチュートリアルを参照
  • 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. ファイルロードの実装 ● 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. 後は簡単 ● ハンドルクラスを作る ● 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. まとめ ● Boost.Flyweight は便利。 ● 今回の例は単純なものだったが、工夫次第 でいろいろと発展できそう ● 例えば PImpl イディオムと組み合わせる ● 公式曰く “sizeof(flyweight<T>)=sizeof(void*)” なので、 reinterpret_cast を使える ● 興味があれば是非とも使ってみてください