2008-05-13
■tieしてSTDOUTにフックかける
HTTP::Engine の STDIN STDOUT 処理を Interface に read write API を生やして、Interfaceの外からwrite readを読んで処理するのか。
もしくは Interface は STDIN STDOUT をセットアップだけして Interface の外は print STDOUT $buffer とするだけでsocketに書き込めるようにするかの話がでました。
STDIN STDOUT だけセットアップするのは、Interface とそれ以外の依存度が減るので良いのですが、 Danga::Socket や POE などの非同期系IOを使う場合は、普通だと非同期な出力処理(write)が出来ないと思われます。というのはソース読んだら嘘でした><
でも write method 使わないと大変な事になりそうな気がする。教えて偉い人。
その悩みは tie で出来るよ。という事で IO::Scalar と tie を使って、 STDOUT に print したら任意のコードリファレンスに出力内容を渡す仕組みを作ってみました。
これで Danga::Socket とかで $sock->write して出力とか POE 等の任意のメソッドで出力出来ます。
で、ベンチ取ったら素のSTDOUTより倍遅い感じ。
tied : 0 wallclock secs ( 0.60 usr + 0.00 sys = 0.60 CPU) @ 166666.67/s (n=100000) scalar: 1 wallclock secs ( 0.32 usr + 0.01 sys = 0.33 CPU) @ 303030.30/s (n=100000) normal: 0 wallclock secs ( 0.32 usr + 0.00 sys = 0.32 CPU) @ 312500.00/s (n=100000)
package Tied; use strict; use warnings; use base 'IO::Scalar'; sub open { my ($self, $ref) = @_; ### Sanity: (ref($ref) eq "CODE") or Carp::croak "open() needs a ref to a code"; ### Setup: *$self->{Pos} = 0; ### seek position *$self->{CR} = $ref; ### code reference $self; } sub print { my $self = shift; *$self->{CR}->(@_); 1; } package main; use strict; use warnings; use Benchmark qw/timethese timeit timestr/; use IO::Scalar; my $count = 100_000; my $tied = Tied->new( sub { print @_ } ); my $scalar = IO::Scalar->new( \my $buffer ); tie *STDOUT, 'IO::Scalar', \my $out; my $t_tied = timeit $count => sub { print $tied "hoge\n"; }; my $t_scalar = timeit $count => sub { print $scalar "hoge\n"; }; my $t_normal = timeit $count => sub { print "hoge\n"; }; untie *STDOUT; print "tied :", timestr($t_tied), "\n"; print "scalar:", timestr($t_scalar), "\n"; print "normal:", timestr($t_normal), "\n";