毎秒1000リクエスト を捌く超高速CMS「adiary」
2006/12/25(月)IEとFirefoxで、onMouseMoveを使わずにポップアップを出す
サンプルと概要
上のリンクのマウスを乗せるとわかりますが、画像がポップアップで表示されます*1。
このようなポップアップを実現するには、Firefox (Netscape)において、onMouseMoveを使うことが一般的のようです。しかし、マウス座標を取得するためだけに、マウス移動のイベントを拾うというのは非効率的すぎてどうにも釈然としません。
IE/Firefoxで使えるポップアップウィンドウ
以下のスクリプトは自由に使って構いません。(2008/09/17 Update)
function popup_img( img_url, evt ) { var text = "<img src=\"" + img_url + "\">"; var div = document.getElementById( 'popup' ); var style = div.style; var cx; var cy; if (! evt) { evt = event; } cx = evt.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); cy = evt.clientY + (document.body.scrollTop || document.documentElement.scrollTop); style.left = (cx + 16) + 'px'; style.top = (cy + 12) + 'px'; div.innerHTML = text; style.zIndex = 9999; style.display = 'block'; } function popdown() { document.getElementById( 'popup' ).style.display = 'none'; }
となります。ポップアップを実現したい場所では、
<tag onMouseOver="popup_img('<画像URL>', arguments[0])" onMouseOut="popdown()"></tag>
とします。
仕組み
見ればわかりますが、呼び出し側でarguments[0]と書かれた場所があります。これがミソです。onMouseMoveを使う実装方法では、マウスを動かすたびにいちいちイベントを取得していましたが、この方法ではonMouseOver発生時のイベント(イベントオブジェクト)をarguments[0]によって取得しています。
2006/12/04(月)Perlと標準入出力ファイルハンドル
Perlで「use strict」で使用している場合、ファイルハンドルを動的に生成し変数に代入するには一工夫する必要がありました。
さらに、このファイルハンドルに標準入力などを代入する場合、
my $fh = 'STDOUT';
ではダメで、
my $fh = *STDOUT;
とします。少々気持ち悪い感じもしますが、STDOUTという定義済の名前に対する型グロブを与えることになります。Symbolなどのライブラリをみると分かりますが、動的ファイルハンドルの実体は、特定の名前に対する型グロブになっています。
2006/11/05(日)perlの長所と短所
所詮Perl?
ネットをみていると「所詮Perl」的な発言をみかけるのですが、いやいやマテマテマテマテ。かと思うと、はてなもmixiもPerlだ! 的な肯定意見があったりして、それもまた視点としておかしいような。
Perlの長所
- Rubyなどのスクリプト言語と比べ速く動作する(時に数倍以上)。
- CやC++に比べはるかに開発が容易。
- CやC++に比べどうしても遅くなるものの、重い計算をループ処理させなければ実用上そこまで大きく変わらない。
- PHPに比べ、MVC的なソース(ソースとHTML部分を分離させるコード)を書くのに適当である。
- 世の中のほとんどのサーバで動作する
という特徴があります。adiaryの基礎になっているスケルトンシステム(Satsuki-system)開発時、本当はRubyも検討したんですが、簡単なテストスクリプトをいくら走らせてもRubyの処理速度はPerlのそれに到底及びません。Perlは隅々まで最適化が行き届いていて、うまく書いてやれば相当早く動作します。
Perlの短所
- 言語仕様が非常にきたない(アセンブラおなじくスクリプト言語として原始的)。
- 標準ではCGIとして動作させるため、スクリプトとしての起動が遅い。処理が遅いのではない。
ほとんどこの2点に尽きると思います。1つめは対Rubyで考えると非常に分かりやすい。互換性を保ってきた結果が現在のPerl 5+オブジェクト指向という複雑な状況なんですが、それでもこのゆるゆるのオブジェクト指向がまた逆手に取ってうまく使うと非常に効率的なシステムが作れます。
2つめはネットを検索すると沢山出てきますが、「PHPはPerlより速い」という意味での速いです。起動が速い。ネット上の共有レンタルサーバでなければmod_perlなどを使うことで解決できますし、うまく書けばCGIとして動かしてもそこまで重くなりません。*1
結局
Perlってのは使い倒せば大規模開発にも十二分にも耐えられる言語です。ただこの辺は適切に判断すべきで、仕様をきっちり決めることで綺麗なメンテ性を取るならRubyもありですし、Webアプリとして手軽に作りたいならPHP、処理速度と開発効率を同時に求めるならPerlということになると思います。*2
adiaryを開発する人間としては
「早くPerl6出ないかなぁー」ってのが本音です(笑) Satsuki-systemが全面書き直しになりそうで怖いのは怖いけども(苦笑)
2006/08/23(水)CSSXSS
CSSの中でjavascript実行しちゃったりする問題
CSSXSSというのがあるんですが、もちろんIE専用。要するに「罠サイトからスタイルシートに見せかけることで好きなサイトのデータを(クライアントに)取得させ、それをJavaScriptで処理することで罠サイト側に情報漏洩出来る」セキュリティホールです。さすがIEやることが違う!
というわけなんですが、CSSXSSさせない(加害者にならない)対策は取れても、CSSXSSされない(被害者にならない)対策は取りようがない。漏洩したことろでセッションは盗めないのですが(でも機密とかダダ漏れ)、CSSXSS+CSRFされるともはや手の打ちようがない。
世の中のIE使ってる人、危険極まりないですよこれは(汗
2006/07/31(月)Perl正規表現「$& $` $'」のオーバーヘッド検証
$&によるオーバーヘッドとは?
perlfaq6によると
なぜ $&とか$`、$'といった変数を使うとプログラムが遅くなるのですか?
プログラムのどこかでそういった変数が使われているのを見つけてしまうと、Perlはすべてのパターンマッチに対してそれに対処することをやらなければなりません。同様のからくりが、$1、$2などを使ったときにも行なわれます。このためすべての正規表現において、部分正規表現を捕捉するために同じコストがかかることになります。しかし、スクリプト中で $&などを全く使っていないのであれば、正規表現は部分正規表現を捕捉して不利になるようなことはしません。ですから、可能であれば $&や$'、$`を使わないようにすべきなのですが、それができないのであれば(一部のアルゴリズムはこれを使うのが便利なのです)、一度これらの変数を使ってしまったら好きなように使いましょう。なぜなら、罰金はすでに払ってしまったのですから。アルゴリズムの中にはこういった変数を使うことが適切であるものがあるということに注意してください。リリース5.005では、$&はもはや“高価な”ものでは ありません。
と書かれています。adiaryでは$' $` を多用しているので*1今更なのですが、使わないことでどれくらいの速度アップが見込めるか、実際に検証してみたいと思います(以下Perl5.8.xにて検証)。
$&等は実際どの程度のオーバーヘッドを発生するか?
この記事のソースを$textに代入した状態で、
$test{test} = sub { my $x = "\n" . $text; $text =~ s/\W/\./g; };
のベンチマークを取ってみました。
同一ソース中に"$&"を書いた場合 test1: 3 wallclock secs ( 3.27 usr + 0.01 sys = 3.28 CPU) @ 304.76/s (n=1000) 同一ソース中に"$&"などを書かない場合 test2: 1 wallclock secs ( 1.52 usr + 0.00 sys = 1.52 CPU) @ 659.79/s (n=1000) 同一ソース中に"$&"などを書かず、$`などが使われたソースをrequireした場合 test3: 3 wallclock secs ( 3.27 usr + 0.01 sys = 3.28 CPU) @ 304.76/s (n=1000)
比較として、ほかの場所では$&などは使わずに、次のようなソースも検証してみました。
sub { my $x = $text; $text =~ s/\W/$&/g; }; 結果:7 wallclock secs ( 6.77 usr + 0.00 sys = 6.77 CPU) @ 147.64/s (n=1000) sub { my $x = $text; $text =~ s/(\W)/$1/g; }; 結果:7 wallclock secs ( 7.35 usr + 0.00 sys = 7.35 CPU) @ 136.03/s (n=1000)
ここまでの結果をまとめると。
- ソース自身の他、使用しているライブラリの1ヶ所でも $` $& $' が使われていれば、Perlの正規表現は常にそれらを返すようになり(正規表現を使用しているすべての場所で)オーバーヘッドが発生する。
- $&等によるオーバーヘッドは1回のマッチング当たり最大2倍程度、Perlの正規表現式が複雑になればこの差は縮むと思われる。
$` や $' を置き換えたらどうなるか?
次のようなテストプログラムで検証しました。test1, test2 にそれぞれコメントアウトしてから検証しています。@aryには15件ほどのデータが入っています。
foreach(@ary) { if ($_ =~ /::/) { my $category_main = $`; my $category_sub = $'; } } ●結果:1 wallclock secs ( 1.18 usr + 0.00 sys = 1.18 CPU) @ 8476.82/s (n=10000) foreach(@ary) { if ($_ =~ /^(.*?)::(.*)$/) { my $category_main = $1; my $category_sub = $2; } } ●結果:2 wallclock secs ( 1.62 usr + 0.00 sys = 1.62 CPU) @ 6183.57/s (n=10000)
やはり、$` $' を使った方が速いという結果になりました。ただこれも正規表現が複雑になればなるほど、差は縮まっていくものと思われます。ただし $' などの与える影響の範囲はプログラム全体の正規表現に及ぶので、実際のプログラムを書いたときどちらが高速かは2パターン書いて比べてみないと分からないという結論になると思います。
おまけで、こんな実験もしてみました。/gとposについてはこちら。
foreach(@ary) {
if ($_ =~ /::/g) {
my $category_main = substr($_, 0, pos($_));
my $category_sub = substr($_, pos($_)+2);
pos($_) = 0; #記事初出時、この行がない不正確な計測していました。
}
}
●結果:1 wallclock secs ( 1.46 usr + 0.00 sys = 1.46 CPU) @ 6844.92/s (n=10000)
$1 $2を使うよりは速いですね。$` $'より遅くなってしまいますが、他の正規表現に影響を与えないという意味では良いかもしません。ただ$` $'よりもソースが分かりにくくなるので微妙ですね……。
結論としては
$` $'を使わないで済むのならば、なるべく使わないようにして、$` $'を使うのがスマートならば気にせず使えという感じですね。無理矢理$` $'を排除しても、その正規表現自体が遅くなっては意味がありませんからね。
ドキュメントにあるとおり、
リリース5.005では、$&はもはや“高価な”ものでは ありません。
古いPerlではまた事情が違ってくるでしょう(^^::