2008年06月27日

C++ で自動型変換

 C++ の拡張ライブラリである boost には、lexical_cast というライブラリがあって、iostream への入出力ができる型ならなんでも文字列化 (その逆も) できるので、とても便利です。でも、lexical_cast は、int → long のような変換の場合にも iostream を使った変換をしてしまうので、汎用のリフレクションコードを書こうとすると、そのままでは使いにくいです (オーバーヘッドが大きすぎる)。というわけで、変換前後の型に応じて、static_cast と lexical_cast を自動的に切り替えるようなキャスト関数を作ってみました。こんな感じで使います。

// calls lexical_cast<int>(123)
int i = intelligent_cast<int>("123");

// calls static_cast<long>(123)
long l = intelligent_cast<int>(123);

// calls std::string(const char*)
std::string s = intelligent_cast<std::string>("abc");

 これで、不用意に数値型間や std::string → std::string のような変換 (コピー) が必要になる場合でも、速度の低下を心配する必要がなくなりました。

 ライブラリのコードは以下のような感じ。boost におんぶにだっこなので簡単です (std::string については、実用上の観点から専用コードを追加しています) 。最新版は CodeRepos (/lang/cplusplus/reflection) においてあるので、バグ等ありましたら、指摘もしくは修正いただければ幸いです。

続きを読む "C++ で自動型変換"

投稿者 kazuho : 2008年06月27日 17:22 | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

2008年06月25日

なんとなくリフレクション in C++

 C++ はとてもいい言語なのですが、リフレクションがありません。昨夜、1年ぶりくらいに C++ でリフレクションしたい熱に感染したのですが、ちょっとググった範囲では良いものが見つからなかったので、作ってみました (単に作りたかっただけという説も)。こんな感じで使います。

#include "reflection.hpp"

struct Foo {
  int i;
  std::string s;
};

// リフレクション情報を定義
namespace reflection {
  template <> struct def<Foo> : public def_base<Foo> {
    def() {
      REFLECTION(i);
      REFLECTION(s);
    }
  };
};

  // リフレクションを通してデータを読み込み
  // 変数が見つからない場合は reflection::name_not_found_error が、
  // キャストできない場合は std::bad_cast 例外が送出されます
  int t = reflection::get<int>(f, "i");
  cout << "i=" << t << endl;
  
  // リフレクションを通してデータを書き込み
  reflection::set(f, "s", string("hello world"));
  
  // リフレクション情報を iterate して表示
  for (reflection::def<Foo>::const_iterator i =
         reflection::def<Foo>::map.begin();
       i != reflection::def<Foo>::map.end();
       ++i)
    cout << i->first << " = "
      << i->second->get<std::string>(f) <<endl;

 このリフレクションライブラリの特徴は、以下のようなものになります。

続きを読む "なんとなくリフレクション in C++"

投稿者 kazuho : 2008年06月25日 08:34 | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

2008年06月12日

MySQL (InnoDB) に直接アクセスしてタイムライン処理を高速化する話

 フレンド・タイムライン処理の原理と実践 の続きです。

 先のエントリでは、プルモデルの速度が当初予測していたよりも遅かった (というより SQL レイヤでのオーバーヘッドが大きそうだった) ので、MySQL Internals メーリングリストで質問したりしながら、C++ で直接 InnoDB にアクセスするようなコードを書いてみました。

タイムライン構築速度
タイムライン/秒
SQL56.7
ストアドプロシージャ136
C++ での直接アクセス1,7102,000 (追記参照)

 そしたら、10倍以上高速に! ベンチマークを perl ベースのものから mysqlslap に変えたのですが、プッシュモデルの 2/3 の速度が出ています。これなら、データサイズが約 1/10 になることを考えると、メモリの代わりに CPU に投資するほうが良い、という判断も非常に現実味を帯びてきます。また、最近のクアッドコアな CPU を使えば 10,000 タイムライン/秒クラスも夢じゃないでしょうから、memcached による支援の必要もないのかもしれません。

続きを読む "MySQL (InnoDB) に直接アクセスしてタイムライン処理を高速化する話"

投稿者 kazuho : 2008年06月12日 17:12 | コメント (1) | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

2008年04月18日

C++ テンプレートを使って高速な高機能サーバを書く方法

 「C++ のメンバ関数ポインタって何のためにあるの」という質問を耳にすることがあります。実際は、たとえばステートマシンを書くのに便利なのですが、ちょうどサイボウズ・ラボの C++ 熱が盛り上がっていることもあり、昔の作ったサーバフレームワークを再実装してみました。ちなみにもともとは、1990年代に東京大学駒場キャンパスで使われていた friends というサービスのバックエンドだった、finger プロキシ用に書いたコードです。ソースコードは /lang/cplusplus/friends_framework - CodeRepos::Share - Trac においてあります。特徴は以下のとおり:

  • シングルスレッド、select(2) ベースによる実装
  • C++ テンプレートとメンバ関数ポインタによるステートマシン化
  • タイムアウト処理の容易な組み込み

 実際の処理をどのように書くかは、test/proxy.cpp をご覧いただければいいと思います。これは TCP の L4 Proxy を実装したものですが、関数ポインタとフラグの切り替えによって、転送処理とタイムアウト処理を実装しています。

続きを読む "C++ テンプレートを使って高速な高機能サーバを書く方法"

投稿者 kazuho : 2008年04月18日 16:39 | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

2008年04月17日

C++ テンプレートで(いまさら)FizzBuzz

re C++テンプレートでFizzBuzz - おびなたのはてな日記

 C++ テンプレートで if を定義しているのは面白いと思いましたが、一方で FizzBuzz を実現するためのコードとして見た場合は冗長じゃないかと思ったので、自分でも書いてみました。こんな感じ。

#include <iostream>

using namespace std;

template<int N, int Mod3=N%3, int Mod5=N%5> struct Print {
  Print() {
    cout << N << endl;
  }
};

template<int N> struct Print<N, 0, 0> {
  Print() {
    cout << "FizzBuzz" << endl;
  }
};

template<int N, int Mod5> struct Print<N, 0, Mod5> {
  Print() {
    cout << "Fizz" << endl;
  }
};

template<int N, int Mod3> struct Print <N, Mod3, 0> {
  Print() {
    cout << "Buzz" << endl;
  }
};

template<int N> struct FizzBuzz {
  FizzBuzz() {
    FizzBuzz<N-1>();
    Print<N>();
  }
};

template<> struct FizzBuzz<0> {
  FizzBuzz() {}
};

int main()
{
  FizzBuzz<100>();
}

投稿者 kazuho : 2008年04月17日 18:55 | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

2008年02月26日

Range Coder の展開速度を SSE で高速化 (してもらった)

 二分検索 (バイナリサーチ) は、分岐予測が効きにくいため、パイプラインが深い最近の CPU とは相性の悪い処理です。Range Coder の展開処理においても二分検索がボトルネックになるのですが、光成さんに「SSE で高速化できないんですかね」と話したところ、さくっとコードを書いてくれました。ありがとうございます。

 というわけで、まずは Calgary Corpus の paper1 でベンチマーク。

続きを読む "Range Coder の展開速度を SSE で高速化 (してもらった)"

投稿者 kazuho : 2008年02月26日 16:31 | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

2008年02月22日

Range Coder の終了処理

 CodeZine:高速な算術圧縮を実現する「Range Coder」(算術圧縮, データ圧縮, Range Coder)等を見ていると、多くの Range Coder の実装では、終了処理において冗長な出力をしているようです。

 私の理解と記憶が正しければ、予測の上限値と下限値が異なる最初のビットまで出力すれば、残りのビットの出力は不要なはずです。Range Coder が一般化する以前の、ビット単位の操作を行っていた Jones 符号化器はそのような実装がされていたように思うのですが、Range Coder で速度を稼ぎ始めた時に、この点が見過ごされるようになったのでしょうか。

 もちろんデータサイズが大きい際は、この点に起因する数バイトの無駄は問題はならないのですが、小さなデータを多数、静的テーブルを用いて圧縮するような場合は、無視できない影響が発生します。奥は Range Coder をちゃんと見たのは昨日が初めてなので、理解が間違っているかもしれませんが、備忘録をかねてブログエントリに起こしておきたいと思います。

21:56追記: CodeZine 上にある岡野原さんのコードは BSD ライセンスらしいので、奥の改変版を /lang/cplusplus/range_coder/range_coder.hpp - CodeRepos::Share - Trac にコミットしました。よろしければご覧ください。

投稿者 kazuho : 2008年02月22日 20:55 | コメント (1) | トラックバック (0) このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク