« March 2007 | Main | May 2007 »

Monday, April 30, 2007

ついったー

今更感があるものの,とりあえず登録してみた.

http://twitter.com/mikage

しかし・・・身の回りでついったーしている人ってほとんどいない気が(^^;

| | Comments (0) | TrackBack (0)

lists:keymember

[Erlang]listsより

lists:keymember

 これは関数名からは何をするのかよく分からなかったので、ドキュメントを見たのですが、それでもよく分からない・・・

指定したキーを持つタプルが存在するか調べるために使うものかと思います.

1> lists:keymember("mocha", 1, [{"cinnamon", 1}, {"mocha", 2}, {"milk", 3}]).
true
2> lists:keymember("cappuccino", 1, [{"cinnamon", 1}, {"mocha", 2}, {"milk", 3}]).
false

こんな感じでしょうか.

lists:key~ を使えば,Perlなどのハッシュに相当する処理を行える(もちろん処理時間は異なるけれど),ということだと思います.

1> X1 = [].
[]
2> X2 = lists:merge(X1, [{"cinnamon", 1}, {"mocha", 2}]).
[{"cinnamon",1},{"mocha",2}]
3> lists:keysearch("mocha", 1, X2).
{value,{"mocha",2}}
4> X3 = lists:keydelete("mocha", 1, X2).
[{"cinnamon",1}]
5> lists:keysearch("mocha", 1, X3).
false

margeを使ってキーを追加,キーの上書きは keyreplace,取得は keysearch,存在確認は keymember,削除は keydelete,ソートは keysort で出来るようです.

タプルの数に比例して処理時間がかかると思うので,要素数が少ない場合にしか使えないとは思いますが・・・


| | Comments (1) | TrackBack (0)

Sunday, April 29, 2007

チャットをCometより楽に実装する方法

JavaScriptベースのチャットを作るとき,Cometを使うのが一般的とされているようだけど,もっと楽な方法でも良さそうに思える.

Cometでは1回リクエストを受け取るたびにコネクションを切断しているので,Keep-aliveなどと併用しなければならない.
実際に都度TCP接続を張り直すとコストが大きいので,Keep-Aliveを併用したりする必要があるものの,それも色々裏技的な回避をしないとだめな模様.
Cometの正しい使い方を参照)

でも,チャットを実装する上では,都度コネクションを切断する必要がなさそうに思える.
iframeで別のページを読み込み,その中で script タグを使って親フレームを更新するだけではダメなのだろうか.

この方法であれば,
・Cometのように,切断してから再接続するまでのタイムラグがないので,サーバ側の余計な処理が減る.(切断中に届いたメッセージを探す動作などが不要)
・サーバに再接続しないので,サーバ側からのpushが連続したときでも余計なタイムラグが発生しない.
・Keep-AliveやPipeliningなどとの相性問題の心配が不要
といった点でメリットがありそう.

実際にサンプルを作ってみると,
chat.html
chat.cgi (ソース)
こんな感じでちゃんと動く.

ただ,Comet+JSONPならメインのドメインとは別のドメインで接続が出来るけど,この方法ではクロスドメイン制約にひっかかって無理.
しかし,ページ自体を複数ドメインにすれば簡単に回避できるはず.
(チャットルームごとに http://部屋名.example.com/ のURLを使うとか)

Cometのメリットがこれだけなら,多くのケースでは接続しっぱなしの方が実装も楽で良さそうに思うけど,何か見落としているのかなぁ?
Cometのページを見ても,その辺に関しての記述は見つけられなかった...

それとも,Amazonアフィリエイトみたいな事をやろうとするときに,メインのページが固定ドメインでないと不都合がありそうなので,そこが重要だったりするのだろうか…
(とりあえず気になったので,Amazonに http://*.example.com/ のような形で登録できるか聞いてみた.返事待ち)

----

追記.

今更1つ大きな違いに気づいた.
iframeでは,ブラウザのロード中の表示が継続してしまう.
IEだとあまり気にならないけど,Firefoxとかでは結構気になる.

Cometであれば,見た目は変わらないので,やっぱりそこがメリットなのかな.

GmailもどうやってるのかFirebugで見てみたら,Cometっぽい感じだった.
だいたい200秒くらいでタイムアウトして,新しく接続し直している感じ.

----

さらに追記.

久しぶりにはてブを見てみたら,コメントがあった.

throbber が回ることさえ気にしないのなら、それでOK。切断時の対応コードを含めると、コード量はかわらないと思う。iframe なら document.domain 操作でサブドメイン越えが可能

とあった.

まぁ,ぐるぐる回るのは一度気づいてしまうと気になって仕方ないので,無しかなぁ.

| | Comments (0) | TrackBack (0)

Tuesday, April 24, 2007

DBの分散方法

Rails って,結構人気があるから,既にそういう機能もあるのではと思っていたけど,無かったらしい.
簡単に実装できるから,誰も公開していなかっただけなのかもしれないけど.(^^;

2種類のモジュールがあるようだけど,readonlyable 方式はすぐ問題が出てきそうな気がする.

たとえば,ユーザ登録時にIDの重複判定をするような場合,read_only 指定をしていると,masterからslaveへの更新反映が遅延するため,正しく重複判定出来ない可能性が出てしまいそう.

かといって,read_only 指定をしないと,アクセスが多そうなユーザ情報のテーブルが分散されない.

その辺を考えると,多少面倒でも magic_multi_connections のやり方が正解じゃないのかなぁ,と思う.


TripletaiL でも,こういう分散は考慮していて,magic_multi_connections と同じようなやり方を採用している.
ただし,複数のサーバを登録しておいて,ラウンドロビンで適当なDBを使う機能がついているので,コード側ではマスターかスレーブかを指定しておいて,複数台のスレーブの分散はフレームワーク任せという感じ.

Tripletail::DB


縦の分散だけじゃなく,横の分散まで自動化できるツールがあると便利そうだけど,見たことがない….
実装,というか上手く分散するロジックを考えるのが難しいからかなぁ.

% 分散DBを使う方がお手軽?ErlangのMnesiaに期待中…

| | Comments (0) | TrackBack (1)

Monday, April 23, 2007

ErlangとPerlの速度比較 その2

ErlangとPerlの速度比較の文字コード変換部分に関して, Danさんからトラックバックをいただいたので再度測定してみた.

そもそもはじめからUTF-8、それもutf8フラグがたっている文字列にfrom_toを使うのはばかげている。
とのことだけど,utf8フラグは立っていない想定.
use utf8 は罠がいっぱいな割にメリットが少なく,普段は使用しないので・・・. (DBIが未対応だったり,モジュールによってフラグの扱いが違ったり(YAML::*とか)なので)
また,多くの場合は,ファイルやソケット等からUTF8バイト列を受け取って,それを利用することになるので, utf8フラグ無しの文字列からの変換で比較する方が実際の利用状況からもフェアではないかと感じるけれども.
(ただ,バイト列にしたとしても,やっぱりEncodeが一番速かった)

そんなわけで,コードは以下のように修正.

my $dec = find_encoding("UTF-8");
my $enc = find_encoding("Shift_JIS");
my $str = "123あいう";
for(my $i = 0; $i < 0xffff; $i++) {
        $enc->encode($dec->decode($str));
}

これで測定してみると・・・劇的に速くなった!
今までのpodにはfind_encodingのこのような使い方が載っていなかったので,from_toを使って遅いと思っている人は多そう. ここまで速いとは知らなかった.
エンコード名を探す(?)処理などで時間がかかっているのだろうか. (Encodeがサポートするエンコード名は山ほどあるし)

方法言語時間(sec)
iconvErlang (beam)0.604394
Erlang (native)0.612490
Erlang (beam+smp)1.232886
Erlang (native+smp)1.250170
Unicode::JapanesePerl0.646837
EncodePerl1.403991
Encode(改)Perl0.151645


文字列が短いせいか,Danさんの結果より差が大きい.
そして,UniJPが遅い...(^^;
Erlangに比較しても,やはり文字列処理はPerlが得意,というのを実感.(正規表現も速いし)

Erlangの方も速くできないかと,ロックレベルを細かくすることでsmpで性能を向上させるべく, とりあえずソースに
driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING

を追加したら,動かなくなってしまった.
やはりそんな簡単にはいかない模様.

ドライバのマニュアルを読んでいたら,
int control(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen) This is a special routine invoked with the erlang function port_control/3. It works a little like an "ioctl" for erlang drivers. The data given to port_control/3 arrives in buf and len. The driver may send data back as a driver binary, using *rbuf and rlen. This is the fastest way of calling a driver and get a response. It won't make any context switch in the erlang emulator, and requires no message passing. It is suitable for calling C function to get faster execution, when erlang is too slow.

なんて記述を発見.
iconvドライバはコレを使っていないので,controlを使う方法で呼び出せば,さらに高速化が可能かも.
時間が出来たらチャレンジしてみよう・・・

| | Comments (0) | TrackBack (1)

Saturday, April 21, 2007

regexpの代わりにparser

Erlangで正規表現がイマイチ使えないので,パーサを書く方法を試してみた.
HaskellのParsecのようなライブラリ parser.erl を使ってみた.

実行回数を1桁増やして,0xffff回実行にして,再度PerlとErlangで比較.

方法言語時間(sec)
parserErlang (beam)2.843180
Erlang (native)1.079725
Erlang (beam+smp)3.035176
Erlang (native+smp)0.776946
正規表現Perl0.197326


Perlの正規表現最強.
まぁ,言語自体にCで最適化されているエンジンが搭載されているのと比較するのがアレなのかもしれないけど. それにしても,もう少し速い方法があるといいんだけども...
nativeコンパイルでだいぶ速くなるのが救いかな?
oregexpを使うとかPCREを外部で呼び出すとかしても,ドライバのオーバーヘッドが大きそうだから,iconvで同一回数回したときの0.6秒よりは小さくならなさそうだし,互角なレベルに持って行くのは厳しそう.
言語レベルで内部構造べったりな感じで拡張する方法があれば良いけど,マニュアルを見る限りはなさそうだし.(互換性のためにあえてそういう仕様にしているぽい感じ)
とりあえず,正規表現を使うのに比べて10倍以上は速くなっているから,こんなもんかなぁ.

ソースコードは以下の通り.(適当なところで改行いれてあります)
-module(strparser).
-export([bench/0, bench/1]).

bench() ->
    Time = timer:tc(strparser, bench, [16#ffff]),
    io:format("~w~n", [Time]).

bench(0) ->
    0;
bench(N) ->
    String = "12.34.123.12 - - [17/Apr/2007:23:53:13 +0900] \"
    GET /html/1234.html HTTP/1.1\" 200 6980 \"http://www.
    mikage.to/html/1234.html\" \"Mozilla/4.0 (compatible; MS
    IE 6.0; Windows NT 5.1; SV1)\"\n",
    {Str1, Str2} = parser:parse(parser:pAnd([
        parser:pMany1(parser:pSat(fun (C1) ->
            not lists:member(C1, " ") end)),
        fun parser:pSpace/1,
        parser:pMany1(parser:pSat(fun (C1) ->
            not lists:member(C1, " ") end)),
        fun parser:pSpace/1,
        parser:pMany1(parser:pSat(fun (C1) ->
            not lists:member(C1, " ") end)),
        fun parser:pSpace/1,
        parser:pChar($[),
        parser:pMany1(parser:pSat(fun (C1) ->
            not lists:member(C1, "]") end)),
        parser:pChar($]),
        fun parser:pSpace/1,
        parser:pChar($")
    ]), String),
    {Str3, Str4} = parser:parse(parser:pAnd([
        parser:pMany1(parser:pSat(fun (C1) ->
            not lists:member(C1, "\"") end))
    ]), Str2),
%   io:format("~s~n", [Str3]),
    bench(N - 1).

| | Comments (0) | TrackBack (0)

oregexp動かない・・・

oregexp動いたで追記しています.

oregexpを試そうとしたけど,make test で失敗してしまう.
ソースを追う気力がないのでまた今度かなぁ.

rd@dev2:~/src/oregexp-1.2> make test
/usr/local/bin/erl -pa oregexp-1.2/ebin test -s run_tests run
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

{"init terminating in do_boot",{undef,[{eunit,run,[[test_basic,test_compare,test_options],{prefix,"test_"}]},{run_tests,run,0},{init,start_it,1},{init,start_em,1}]}}

Crash dump was written to: erl_crash.dump
init terminating in do_boot ()
make: *** [test] エラー 1

| | Comments (0) | TrackBack (0)

Wednesday, April 18, 2007

ErlangとPerlの速度比較

Erlangは並列処理が得意なので,それに関しては他の言語より圧倒的に速いのは確認したけれど, わたしがよく使う通常の処理に関してもどの程度違うのか測定してみた.
一般的な処理に関しては,The Computer Language Shootout Benchmarks にたくさん結果があるので,そちらが参考になる.

言語としては,どちらも動的型付けで,コンパイラで内部表現に直してからインタープリタで実行,というあたりは一緒.
(ただしErlangはnativeコンパイラが別途ある)
ほぼ同じくらいの速度が出ても良さそうなところ.

Perlの代替を考えていたので,比較対象はPerl.
Language Shootout によれば,Erlang の方が多くのテストで2~3倍前後高速.nativeコンパイルすると,その差がさらに広がる感じ.

Erlang 5.5.4 と Perl v5.8.5.
SuSE Linux 9.2 (x86_32) でメモリ4G,Xeon 3G×2の環境で測定.
測定は5回ずつ行って,中央値を採用.

・" " を 0xfffff個 join

文字列の扱いの比較.Erlangは,StringとBinaryの2種類があるので,両方で測定した.
Stringは1文字あたり32bit環境で8バイト,64bit環境で16バイト使うので,あまり大きなデータには使えない.
Binaryは32bit環境で文字数+12~24バイト,64bit環境で文字数+24~48バイト.同じバイナリは共有される.
Binaryは扱いにくい部分があるので,使い分けが必要そう.

方法言語時間(sec)
文字列はStringErlang (beam)0.312705
Erlang (native)0.118335
Erlang (beam+smp)0.235028
Erlang (native+smp)0.142893
文字列はBinaryErlang (beam)0.458387
Erlang (native)0.388640
Erlang (beam+smp)0.415061
Erlang (native+smp)0.343201
配列にpushしてjoinPerl0.741943
.=でつなげるPerl0.538587

Perlは配列にしてつなげるより,.=でつなげた方が速かった.文字列が短いから配列管理のオーバーヘッドの方が大きいのかも? いずれの方法でもErlangの方が全般的に高速.

・"123あいう" の文字コードを 0xffff回変換

文字コードをUTF-8からShift_JISに変換する速度を測定.
Erlangはiconvを利用するモジュールが(非標準で)あり,それしか方法がないのでそれで測定. nativeコンパイルに関しては,iconvを利用するモジュール自体はnativeコンパイルなしで, ベンチを実行する側のみとした.
Perlは,Unicode::JapaneseとEncodeの両方で測定した.
どちらもモジュールのロードなどの時間は除いて,純粋に変換部分だけの時間を測定.

方法言語時間(sec)
iconvErlang (beam)0.604394
Erlang (native)0.612490
Erlang (beam+smp)1.232886
Erlang (native+smp)1.250170
Unicode::JapanesePerl0.646837
EncodePerl1.403991

Erlangはsmpにすると極端に処理が遅くなってしまう.
Cのモジュールと通信してデータをやりとりする仕様だけれど, C側のモジュールがシングルスレッドで実行されて, そのモジュールとの通信を奪い合うのが原因?なのかなぁ.
C側のモジュールとのやりとりをマルチスレッドで実行させる方法があるのかもしれないけど, まだそこまで調べられてない状態. マニュアルをざっと眺めた限りでは無理そう...
どうも,外部プログラムは,Erlang内の1プロセスと同様の扱いになっていて, 漢字コードの変換のような,内部的な処理の高速化にCコードを使うのには向いていない感じ.
Erlangコードで漢字コード変換を書いて,それをnativeコンパイルした方が速いかもしれない.

Perl側は,Encodeが遅い.
Encode::from_toがinplaceでコンバートしてしまうために,直前に文字列コピーがあるのも影響しているのかも?
もう少し速くても良さそうだけども...
コードは以下のような感じ.
(UniJP側は,newする時間は含まない形で測定)
my $uj = new Unicode::Japanese;
my $t0 = [gettimeofday];
for(my $i = 0; $i < 0xffff; $i++) {
        $uj->set("123あいう")->sjis;
}
my $str = "123あいう";
for(my $i = 0; $i < 0xffff; $i++) {
        my $str2 = $str;
        Encode::from_to($str2, "UTF-8", "Shift_JIS");
}


・正規表現でアクセスログからURL部分を0xfff回取得

正規表現で,アクセスログからURLの部分を取得するテスト.メソッドとHTTP/1.1の部分も含めて取得する.
Erlangは標準ライブラリにregexpがあるので,それを利用.

方法言語時間(sec)
regexpErlang (beam)2.841569
Erlang (native)3.163096
Erlang (beam+smp)2.474175
Erlang (native+smp)2.693628
正規表現Perl0.012415


Perlの正規表現の圧勝.
というより,Erlangのregexpモジュールはかなり機能が貧弱なようで,普通には実装できなかった.
無理に正規表現で済ませようとした結果なので,速度が遅いと言うよりは機能不足かな.
その上,ソースを見てみたら,普通にErlangのコードで実装されていた. これでは言語自体に内蔵されているPerlにかなうはずが無い…
Perlなら,正規表現の中で()で一部分を指定して,それを$1で後から取得できる. でも,Erlangのregexpにはそのような機能がない.
結局以下のようなコードになってしまった.
bench() ->
        {ok, Reg1} = regexp:parse(
            "^[^ ]+ [^ ]+ [^ ]+ \\[[^\\]]+\\] \""),
        {ok, Reg2} = regexp:parse(
            "^[^ ]+ [^ ]+ [^ ]+ \\[[^\\]]+\\] \"([^\"]+)\""),
        String = "12.34.123.12 - - [17/Apr/2007:23:53:13
         +0900] \"GET /html/123.html HTTP/1.1\" 200 6980
         \"http://ayaya.jp/html/123.html\" \"Mozilla/4.0
          (compatible; MSIE 6.0; Windows NT 5.1; SV1)\"\n",
        Time = timer:tc(strregexp2, bench,
            [16#fff, Reg1, Reg2, String]),
        io:format("~w~n", [Time]).

bench(0, _Reg1, _Reg2, _String) ->
        0;
bench(N, Reg1, Reg2, String) ->
        {match, _Start1, Len1} = regexp:match(String, Reg1),
        {match, _Start2, Len2} = regexp:match(String, Reg2),
        Match = string:substr(String, Len1 + 1, Len2 - Len1 - 1),
        bench(N - 1, Reg1, Reg2, String).


この辺は,regexpじゃなくて,C言語的に実装しなければならないのかなぁ.
正規表現は便利なだけに,これはいまいち.


----
追記.
erl_driverのマニュアルを読むと,ロックレベルを変えることで,複数スレッドで同時に使えるようになる模様.
後日試してみよう・・・

| | Comments (0) | TrackBack (1)

Thursday, April 12, 2007

Erlang楽しい♪

最近Erlangを勉強中なのだけど,かなり良い感触.

メジャーじゃない言語(C/C++,Perl,Java,PHP,Rubyくらい?)は,興味を持っても,実際に業務で導入する,というところまでは行かないのだけど,Erlangは実際に使いたいと思うほど.

MLでErlangの話の資料が公開されていて,
Threads is evil.
Processes are ugly.
State machines send you mad.
ということで,Erlang良いよ,という内容.

多数のリクエストを扱うようなサーバアプリケーションを作る場合,スレッドは見つけにくいバグがよく起こる上にそこまでスケールしないし,マルチプロセスは遅い&スケールしない.

C10K問題と言われていて,解決する方法としては,ノンブロッキング呼び出しとか非同期呼び出しで自前で各コネクションの状態を管理してがんばるしかないわけだけど,これは実装がかなり大変.
確かにパフォーマンスは最大限出るけれど,作るのが大変なので,普通のWebサービスに使ったりとかは難しい.

その点,Erlangなら,複数プロセスで処理をする場合と同じようにコードを書くだけで,C10K問題をクリアできる.
実際に,Erlangで書かれたサーバ(yaws)とApacheの対決結果などを見ても,8万以上のコネクションでも問題なく動作.

マイナー言語の宿命か,ドキュメントが貧弱だったり,ライブラリがもう少しほしい感じはするけど,オンメモリDB,ディスクDB(耐障害性は無し),分散DBは標準で付いてくる.ついPerlのCPANと比べてしまうけど(^^;,そういう比較をしなければそれなりに用意はされている感じ.
(ただしマルチバイト処理が欠けてるのは残念…)

Erlangのメリットとして,かなり並列処理でスケールしてくれる以外にも,クラッシュダンプの取得・解析が標準装備だったり,実行中のプロセスの状態を外部から確認できたり,動作したままコードを差し替えたり,ちゃんとプロファイラとかカバレッジが標準で付いていたり,業務で運用する場合に便利な機能がいっぱい.

どちらかというと,言語そのものというより,Erlang VM の環境が素敵.なのかな.

Erlang + Yaws vs. Ruby on Railsなんて記事もあるようで,Webアプリ作成にも向いていそう.
日本で使うなら,文字コード周りの面倒とかみないといけないし,自前で色々用意しないとだめそうだけど(^^;

もう少し日本で流行らないかなぁ...
ぐぐっても利用者少ない(^^;


| | Comments (0) | TrackBack (0)

Wednesday, April 04, 2007

jQuery history プラグイン

jQuery history plugin - Ajaxアプリで戻るボタンを押したときに、操作前の状態に戻すためのjQueryプラグイン

にて紹介いただきました.ありがとうございます.

この動作は、イベント発生時に履歴にURLを追加しておいて、戻るボタンが押されたときは、ブラウザのキャッシュを利用して操作前の状態に戻すという感じでしょうか?

動作原理ですが,
1.location.hashを書き換えて戻るボタンの履歴に残るようにする
2.iframeのopen/closeで履歴に残るようにする
の2つをブラウザによって使い分けることで実現しています.

上記をすると,#以降の部分を変更させたものを履歴に残すことができます.
戻るボタンを押すと,#以降が以前の状態に戻るので,#以降の部分が変化していないか定期的にチェックすることで戻ったことを判定しています.

Ajaxと戻るボタン・ブックマーク
に参考にしたリンクがあります.

というわけで,

うーん。URLに画面の状態を持っておくとうまくいくのだろうか。。。

その通りで,#以降の部分に画面の状態を持たせておいて,必要に応じて#以降の部分から画面の状態を復元できるように作る必要があります.

| | Comments (8) | TrackBack (1)

Sunday, April 01, 2007

jQuery ドキュメント更新

1.1系が出てからだいぶたったけど,ようやくドキュメントを更新.

http://www.mikage.to/jquery/

プラグイン周りはあまり調査していないので,若干情報が古いかも・・・

| | Comments (3) | TrackBack (0)

« March 2007 | Main | May 2007 »