2007年06月22日

encoding::sourceをperl5.8系で動かす

ことの発端はmiyagawaさんの

というわけで上記のような問題になやまされずにリテラルをソースに書くことが出来そう。5.9 からバックポートされたのかなぁ、と思ってソースをみると思いっきり use 5.9.5 が。5.10 のリリースがまたひとつ待ち遠しくなったなぁ。

から

なんで5.9から?

encoding:sourceのソースを見てみると5.10の新演算子//が使われていた。のでまずはdefined使いまくりでソースを書き換えたけど上手く動かない。よく見ると

my $hinthash = (caller($level))[10];

なるコードがある、callerの戻り値のリストは10個しか無い筈なのに11個目を取ってる。
調べてみると、5.10からの仕様でperlpragmaなるドキュメントがある。
callerで指定した呼び出し元のhinthashを取得出来る物らしい。

$^Hと%^H

$^Hはおなじみのuse strictとかでも使用されている、インタプリタに特殊なフラグを渡す為に使われる変数です。
%^Hなんですが、あまり使われてない(overloadとかで使ってる)ソースのコンパイル時に役立ちそうな情報を保存しておく場所みたいな使われ方です。
どれもBEGINのフェーズで活用されます。

なんでBEGINかって言うと、コンパイルフェーズ中にhintを見てコンパイラが色々挙動を買えてくれるので、BEGINフェーズが終わると値が変になる。

BEGIN {
    $^H{hoge} = 'aaa';
}
print $^{hoge};# BBBが表示される
BEGIN {
    $^H{hoge} = 'BBB';
}

こんな感じで普通に考えたら違和感の在る事になる(そういうもんだけどね)

でperlvarを見ると%^Hはレキシカルスコープを抜けたら元に戻る的な事が書いてあったんだけど、これをやるには$^Hで特定のビットを立てなきゃいけないoverload.pmとか見るとわかると思う。

(caller(x))[10]

で、さっきの11個目の値というのは

BEGIN {
    $^H{hoge} = 'aaa';
}
sub c {
    my $hinthash = (caller(0))[10];
    print $hinthash->{hoge};# aaaが表示される
}
c();
BEGIN {
    $^H{hoge} = 'BBB';
}

って事をやってくれる。
5.9.4だと上の上のコードのprint $^H{hoge}で、値が取って来れなくなってた。
perl 5.8系だとこれが使えないから意図した動作にならないわけです。

とりあえずパッチ作った

意図した通りにはならないけど、caller使わず%^Hだけを駆使してパッチを書いてみた
http://tech.yappo.jp/download/encoding-source-0.02_v58.patch
とりあえず一通りのケースで動くけどpackとかで上手く動かない。なんで買って言うとリテラルのencodingはBEGINフェーズ中に行われているので%^Hが上手く活用されるんだが、変数に入ったデータとかをdecodeしようとする時(pack)はBEGINが終わって通常のフェーズ中で処理されるおで%^Hが取れなくなるわけ。

hioさんも5.8で動くのを作っていて、そっちはソースフィルタを使ってcallerの件を上手く回避してます。さすがです><

今後

とりあえず呼び出し元のレキシカルスコープを細く出来るモジュールをXSで書いて、それを使って上手い具合に5.8で動かせる様にしたいす。

Scope::Stash (Lexical::Stash ?)

これは、呼び出し元のx階層上のスコープに特定の値をメモします。例として

use Scope::Stash;

sub hoge {
    print Scope::Stash->get(0, 'hoge');# 出力しない
    Scope::Stash->set(0, hoge => 'boofy');
    print Scope::Stash->get(0, 'hoge');# boofy
    {
        print Scope::Stash->get(0, 'hoge');# boofy
        Scope::Stash->set(0, hoge => 'mixi');
        print Scope::Stash->get(0, 'hoge');# mixi
    }
    print Scope::Stash->get(0, 'hoge');# boofy
}
print Scope::Stash->get(0, 'hoge');# 出力しない

こんな動作をするモジュールをどうにかして書こうかなぁと

PadWalkerのXSコードを見るとpads_into_hashの中のCOP_SEQ_RANGE_(LOW|HIGH)辺りを参考にすれば、補足出来そうな気がする。
呼び出し元のcos_seqを探して来てHIGHの値までに紐づく様にデータ保持しておいて、上書きが合ったら上書き前のcos_seqを、上書き呼び出し時のcos_seqに書き換えてみたいな構造で。
これを%^Hの変わりに使う策略。

%^Hと同期も取れる様にScope::Stash::Hinthashってのも作っておくと良いのかな?
プラグマパッケージのimport/unimport中ならScope::Stash::Hinthashを普通に使うだけで上手く動く気がする。

ついでにhinthashって名前のpragmaも作っておくと、普通のプログラム上で操作出来る様になるから面白い事できる?

副産物

$^ENCODINGの使い方わかったお!



このへんの事は体で理解している弾さんに添削されたい><

Posted by Yappo at 17:57 | Comments (0) | TrackBack

2007年06月17日

じゃがいも収穫祭り2007

梅雨明け前には収穫した方がいいらしいから、枯れてないけど収穫したよ!
imo1.jpg枝の方に栄養行きまくって全長2mくらいのジャガイモになってて芋出来て無いと思ったけど出来てたよ!

imo2.jpg全部引っこ抜いたから大部すっきりしたよ!次何植えよう?

imo3.jpgとりあえずジャガバタ作ったよ!そのへんに生えてるパセリとバジルも混ぜたよ!旨かった!次は何作ろうか。

Posted by Yappo at 15:43 | Comments (1) | TrackBack

2007年06月14日

誰でも紫色の何かを口に押し付ける事が出来るWeb Serviceを公開しました

今日mizzyさんが、何の前触れも無くmizzy.org : 紫色の何かを口に押し付けている…なんて画像を公開していて
「もしかしたら紫色の何かを押し付けたい需要が溢れているのか」とビジネスチャンスを見つけたので、カッとなって誰でも「あの」紫色の何かを押し付けられるWeb Serviceをリリースしました。

Geek Face Generatorといいます。

あなたの顔写真をuploadして、紫の位置をブラウザで調整すれば、あっというまに貴方もgeekの仲間に入れます。

下のサンプル画像のようにgeekじゃない人でも、お手軽にgeekに変身出来ます。
larygeek.png

紫の種類はnormal geekとsuper geekの2種類ありますので、その日の気分で入れ替えるのもおしゃれかもしれません。
さっそく、ご活用して頂いております。

多分アップロードされた画像は、公開されるとかそういう可能性もあるので、ご了承を。

アプリのコードはhttp://svn.yappo.jp/repos/public/website/geekfacegenerator/trunk/で公開しているので誰でも改造出来ます。Soozyのサンプルアプリとしてもどうぞ。

後日ペパボにドクターペッパーを送りつけよう。

Posted by Yappo at 21:17 | Comments (0) | TrackBack

2007年06月12日

Class::Component - プラガブルなモジュールを作る為のベースモジュールの解説

pluginやcomponentを取り扱うモジュールを書く時に、plugin/componentを取り扱いを全て引き受けてくれるモジュールを作りました。
だいぶ前からCPANには上げてたんですが、色々あって今日報告と。

既出のアイディアとしてはid:naoyaの Class::Pluggableid:ya_kenの Class::Pluggable等があります。

これらのモジュールもシンプルでいい感じなのですが、シンプルな分、若干物足りなさがあった(例えばメソッドの生やし方に自由度持たせたいとか)のでこしらえました。
詳しくはCPANのClass::Componentにもありますが日本語でも説明文書きたい。

出来る事

  • component追加で基本メソッド拡張が出来る
  • plugin追加で、hook pointへのhook処理追加や、pluginメソッドを追加出来る
  • pluginで追加するメソッドの実装方式(普通にメソッド生やす、AUTOLOAD、SingletonMethod)を選べる。選ばなければメソッド生えない
  • Class::Componentを使ったオブジェクトをYAML::DumpとかしてもYAML::Loadすれば普通に動く
  • Plaggerっぽくconfigを渡すとpluginまで設定が渡される
  • pluginはattributeを使い、sub jitensya: Method {} のように書くだけでメソッド追加出来る
  • Class::Component::Attribute::以下にモジュールを追加すると、任意のattribute処理が出来る
  • 作ったモジュールを、さらに別のモジュールで継承する事も可能
  • その場合のPluginの名前解決方法はClass::C3っぽい順番で解決する
  • Class::C3の再発明

影響を受けたもの

Class::Component::Attribute::*に追加してpluginで使うattributeを拡張できるようにするのはCatalystから。
Class::Component::Pluginに書いてあるAttributeを集める仕組みはClass::DBIxから
configやhookなどはPlaggerから

Component

いわゆるCatalystのPlugin方式に拡張します。
単純に@ISAにパッケージを突っ込んで行って継承ツリーを太らせます。
ここには、モジュールの動作に多大な影響を与える物を置いておきます。

Attributeを拡張した時に、呼び出すメソッドなんかはComponentで。

Class::Componentには、標準でComponentが4つ付いて来て最低限の挙動をチョイス出来ます。

DisableDynamicPlugin
blessされたobjectからはpluginを追加出来なくします。__PACKAGE__->load_pluginsやMyClass->load_pluginsの形でしか追加出来なくなります。
後述しますがClass::Componentはblessされたオブジェクト別にpluginを管理出来るのですが、この機能をOFFにします。
若干new時のコストが押さえられます。
Autocall::InjectMethod
*{"$pkg::$method"} = sub {}な形で、pluginのメソッドをパッケージに割り当てます。
これやるとオブジェクト毎にメソッド管理出来なくなりますがシンプルです。
Autocall::Autoload
AUTOLOADを使ってオブジェクト毎に使えるメソッド名を判断して処理出来ます。
Autocall::SingletonMethod
その名の通りです。id:naoya氏の実装に近いです。
これはDisableDinamicPluginとの併用が出来ません。
どっちにしろ相反するcomponentではありますね。
Autocall::*の何れかを利用すれば$obj->plugin_methodの形で呼び出せますが
一切利用しないと$obj->call( plugin_method => options ... )の形でしか呼べなくなります。

AutoloadとSingletonMethodはg:id:naoya:Pluggableの話の時のSingletonMethodなのにAUTOLOADを使ってるコードをみて、諦めちゃいなよyou的なノリで二種類作った。

Plugin

PlaggerやSledgeとかClass::Triggerを使った時のhook処理の実装や、コアには影響ないけど用途によってチョイス出来るメソッドを拡張する為にあります。
書き方のサンプルはテストを見るのが一番です。
http://search.cpan.org/src/YAPPO/Class-Component-0.05/t/MyClass/Plugin/Hello.pm"
使い方も
http://search.cpan.org/src/YAPPO/Class-Component-0.05/t/02_myclass_autoload.t

で、componentの解説をした通り、標準ではblessされたオブジェクト毎にpluginの管理されていますので、同じClassから作ったオブジェクトでも呼べるメソッドと呼べないメソッドが発生します。

Dump/Load

YAML::DumpされたデータをYAML::Loadすると自動的にプラグインのロードをしてくれます。
ただし use Class::Component reload_plugin => 1; や use MyClass reload_plugin => 1;の用にuseしておく必要があります。
http://search.cpan.org/src/YAPPO/Class-Component-0.05/t/04_myclass_autoload_dump.tが動作例です。

利用しているComponentによっては旨く動かないかもしれません。

ComponentとPluginの読み込み方

package MyBase;
use base 'Class::Component';
__PACKAGE__->load_components(qw/ Autocall::Autoload /);
package MyClass;
use base 'MyBase';
__PACKAGE__->load_components(qw/ Foo +Bar::Component::Jitensya /);
__PACKAGE__->load_plugins(qw/ Boofy +Foo::Plugin::Baz /);
package main;
use MyClass;
MyClass->load_plugins(qw/ Hello /);
こんな感じの継承されたモジュールがあるとして

MyBaseクラスでのAutocall::Autoloadは、MyBase::Component::Autocall::Autoload, Class::Component::Autocall::Autoloadの順で存在してるか確認し、一番最初に見つかったパッケージを利用します。

MyClassでは
MyClass::Component::Foo MyBase::Component::Foo Class::Component::Component::Foo の順
次のは先頭に+が付いているとCatalyst/DBICと同じ挙動をするので、Bar::Component::Jitensyaを探します。
pluginも同じ感じ。

mainパッケージのも、MyClass,MyBase,Class::Componentの順で探します。

探す順番はClass::C3のような継承ツリーの順序で探索します。

NEXT

Class::C3と同じようなNEXTメソッドを再発明して実装してます。。。
色々不安な部分が合ったのでClass::C3を利用しませんでした。
Class::C3で問題が無いかの検証もしてない。。。

new

SUPERでもNETXでも何でもいいのでClass::Component::newまで処理投げて下さい><

おわり

パフォーマンスに気を使ったりだとか、use baseする側んに余計なメソッドを継承させないだとか考えていたら、だいぶコードがかっこわるくなって来た。
しかも、シンプルじゃなくなって来ている。。。

基礎クラスをプラガブルに作って、さらにその基礎クラスを継承して使うなんて用途もありかなと思ったり。

この話は、Number::Objectへもづく

Posted by Yappo at 21:34 | Comments (0) | TrackBack

Yahoo!の検索が酷すぎる件

Yahoo!に、ものすごく嫌われているんだろうか。。。
MANKOで検索すると。。。

YappoLogs_MANKO_Yahoo.png


ひどいお、ひどすぎるお><

Posted by Yappo at 18:35 | Comments (0) | TrackBack

2007年06月11日

FedoraCoreやCentOS系の遅いPerlのパッチの件

3月くらいにkazeburoさんがメモしてた遅くなる話。

極端に遅くなる処理のベンチ例みたいなのがほしいかな
何やったら遅くなるかわかりました。
すごく正確に検証してるわけでも無いのですが、use overloadしたパッケージをblessするとき。
例えばURIやDateTime等のnew時。
この時にかかるCPUコストが激しく高いため、全体的にアプリケーションがもっさりと動く事になってました。

しかもFC4系列のディストリビューション全体で注意した方が良いかもです。
家はCentOS 4.4の perl 5.8.8-4.el4e1 のRPMで該当のパッチが混入していました。
書いたアプリが見込みよりもパフォーマンスが悪くて、最後の最後にまさかと思ってoverloadしてるモジュールを疑っていたらkazeburoさんにナイス指摘をしていただいて気づきました。
CentOSだから大丈夫だろうと油断してましたorz

以下検証コード

use strict;
use warnings;
use Benchmark;

timethese(100000, {
'overload' => sub { TestOverload->new },
'not overload' => sub { TestNoverload->new },
});

package TestNoverload;
sub new { bless { hoge => 'hoge' }, shift }

package TestOverload;
use overload (
q{""} => sub {},
);
sub new { bless { hoge => 'hoge' }, shift }

このコードをCentOSで動かすと
$ perl ./overload.pl 
Benchmark: timing 100000 iterations of not overload, overload...
not overload: 0 wallclock secs ( 0.17 usr + 0.00 sys = 0.17 CPU) @ 588235.29/s (n=100000)
(warning: too few iterations for a reliable count)
overload: 1 wallclock secs ( 0.86 usr + 0.00 sys = 0.86 CPU) @ 116279.07/s (n=100000)

FreeBSDで動かすと

$ perl /tmp/overload.pl 
Benchmark: timing 100000 iterations of not overload, overload...
not overload: 1 wallclock secs ( 0.47 usr + 0.00 sys = 0.47 CPU) @ 213333.33/s (n=100000)
overload: 0 wallclock secs ( 0.46 usr + 0.00 sys = 0.46 CPU) @ 216949.15/s (n=100000)
と、差は一目瞭然です。

他のOSでもCentOS程の差が見られませんでした。

話題のパッチを深追いしてみたところ、perl-5.8.8-U27512.patchは、perlのoverloadあたりのコードに手を加えていて

This is a hack cope with reblessing from class with overloading magic to
one without (or the other way). Search for every reference pointing to the
object. Can't use S_visit() because we would need to pass a parameter to
our function.
とか書いてあって、斜め訳すると「overloadされたクラスをblessする時に、全部のリファレンスを探索するよ。」って感じですか?

perl-5.8.8-U27509.patchの方は、overloadのテストをTest::Moreでやりたかっただけ?
それともperl coreへのpatchをあてたら元のtest通らなくなった?
調べてない。

とにかく、はてなでもFemoでも該当のパッチを除いても平気という実績はある感じなので、うちもpatchを外してrpm作り直した。
そしたらアプリケーションが6割スピードアップしたよママン;;

Posted by Yappo at 21:31 | Comments (1) | TrackBack

2007年06月01日

VirtueDesktops の 0.54 Beta 3 (339) が酷すぎた件

Betaリリースなんだから煩いぞ!とお叱りがあるのは分かりますが、ああ、あと自分が使ってるソフトの組み合わせが悪いって事の検証もしてない、する気力も消えた。。
ググっても数件しか、この話題が見つからなかったから書いとく。

初代MacBook上のOS 10.4.9の上でVirtueDesktops の 0.54 Beta 3 (339) を使っていたんですよ。
それは置いといて、ここ最近は1日辺りに500M以上も空きディスクが減ってったわけですよね。
最初は仮想メモリにでも使ってんのかなとか楽観的に見てたんですが、この数日は余計なでかめなファイルを消して行ってギガバイト単位で空き容量をせっせこ作ってたわけです。

昨日、ついに異変に気づきdf -iとかしてたら秒単位でinodeが5づつくらい減ってるんです。
しかも数百KBも要領を消費しながら!
これは明らかに可笑しいので、ここ最近入れたソフトを調べ、VirtueDesktopsを終了させたらディスク消費が収まったので犯人が解ったのです。

で、ググって見て何にディスクを使っているかというとLogファイルらしく

$ ls -l ~/Library/Logs/VirtueDesktops.log 
-rw-r--r-- 1 my my 12082867527 Jun 1 13:15 ~/Library/Logs/VirtueDesktops.log
とかいう酷い事になってました。
このログの中身をheadして見ると5月16日から記録が始まっているので、多分VirtueDesktopを最新のβ版に入れ替えた後からロギングしてるようです。
βなのでデバグ出力かな?

ファイナルリリースでてたみたいだけど、もうこのバージョンの使うの辞めて0.52 (138)を使ってLogも消して奇麗さっぱり忘れてしまおう。

Posted by Yappo at 13:35 | Comments (13) | TrackBack