Devel::Cycleがいい感じ。

id:yumatsumoさんのところで触れられているTemplate::Plugin::Filterのメモリリーク問題ですが、Devel::Cycleをためそうとして諸事情(?)によりためせなかったのですが改めて試してみるといい感じです。

まずはリークするソース

package Template::Plugin::Leak;
use strict;
use base 'Template::Plugin::Filter';

sub init {
    my $self = shift;

    # first arg can specify filter name
    $self->install_filter($self->{ _ARGS }->[0] || 'leak');

    return $self;
}


sub filter {
    my ($self, $text, $args, $config) = @_;

    return uc $text;
}

1

利用側のコード

use strict;
use Template;
use lib 'lib';
use Devel::Cycle;

my $template =<<__TEMPLATE__;
[% USE Leak %]
[% FILTER leak %]
lower case characteres
[% END %]
__TEMPLATE__

my $tt = Template->new();
$tt->process( \$template ) or die $tt->error;

find_cycle( $tt );

実行結果

Cycle (1):
             $Template::A->{'SERVICE'} => \%Template::Service::B        
        $Template::Service::B->{'CONTEXT'} => \%Template::Context::C        
        $Template::Context::C->{'LOAD_FILTERS'} => \@D                           
                               $D->[0] => \%Template::Filters::E        
        $Template::Filters::E->{'FILTERS'} => \%F                           
                          $F->{'leak'} => \&G                           
                     $G variable $self => \%Template::Plugin::Leak::H   
        $Template::Plugin::Leak::H->{'_CONTEXT'} => \%Template::Context::C        

Cycle (2):
             $Template::A->{'SERVICE'} => \%Template::Service::B        
        $Template::Service::B->{'CONTEXT'} => \%Template::Context::C        
        $Template::Context::C->{'LOAD_FILTERS'} => \@D                           
                               $D->[0] => \%Template::Filters::E        
        $Template::Filters::E->{'FILTERS'} => \%F                           
                          $F->{'leak'} => \&G                           
                     $G variable $self => \%Template::Plugin::Leak::H   
        $Template::Plugin::Leak::H->{'_STATIC_FILTER'} => \&G    

最後の2行あたりでG <-> Hで循環しているというのがよくわかります。ちゃんと{_STATIC_FILTER}も検出してくれていますし。
全体的に見るにはDevel::Leak::Objectで、場所を特定するのにDevel::Cycleでと使い分けると便利かも。

Devel::Cycleをテストハーネスに組み込んでくれるモジュールhttp://search.cpan.org/~petdance/Test-Memory-Cycle-1.04/もあるらしく、これを習慣付けるといいなぁと思って試してみると以下のエラーがでていることに気づく。(もともと出ていたみたいだったけど望みどおりの結果で満足してスルーしていた・・・)

Not a SCALAR reference at /usr/local/share/perl/5.8.8/Devel/Cycle.pm line 124.

調べてみるとバグレポートがでていて、クロージャがらみのエラーのようだ。
http://rt.cpan.org/Public/Bug/Display.html?id=25360

パッチが添付されてされていたので試してみたけど

Undefined subroutine &Devel::Cycle::_find_cycle_ called at /usr/local/share/perl/5.8.8/Devel/Cycle.pm line 98.

といって怒られた。
もう眠いので今日は断念。

[Perl]更新の有無をチェックするならstatよりファイルテストが高速

キャッシュ更新のためとかでファイルの更新日時をチェックする時はstatよりファイルテストの方がかなり高速

use strict;
package Bench;
my $file = '/tmp/hoge';

sub bench_stat {
        my $last_update = (stat $file)[9];
}

sub bench_file_test {
        my $day = -M $file;
}

sub bench_stat_with_size {
        my ( $size, $last_update ) = (stat $file)[7,9];
}

sub bench_file_test_with_size {
        my ( $day, $size ) = (-M $file, -s _);
}

package main;
use Benchmark qw(timethese cmpthese);
use Class::Inspector;
my $meth = Class::Inspector->methods('Bench');

my $b = timethese( 500000, {
        map { $_ => Bench->can($_) } grep /^bench_/, @$meth
});
cmpthese($b);

>|shell||
Benchmark: timing 500000 iterations of bench_file_test, bench_file_test_with_size, bench_stat, bench_stat_with_size...
bench_file_test: 1 wallclock secs ( 0.23 usr + 0.81 sys = 1.04 CPU) @ 480769.23/s (n=500000)
bench_file_test_with_size: 2 wallclock secs ( 0.40 usr + 0.86 sys = 1.26 CPU) @ 396825.40/s (n=500000)
bench_stat: 2 wallclock secs ( 1.14 usr + 0.86 sys = 2.00 CPU) @ 250000.00/s (n=500000)
bench_stat_with_size: 3 wallclock secs ( 1.26 usr + 0.92 sys = 2.18 CPU) @ 229357.80/s (n=500000)
Rate bench_stat_with_size bench_stat bench_file_test_with_size bench_file_test
bench_stat_with_size 229358/s -- -8% -42% -52%
bench_stat 250000/s 9% -- -37% -48%
bench_file_test_with_size 396825/s 73% 59% -- -17%
bench_file_test 480769/s 110% 92% 21% --
|

[JavaScript][Ext]フォームコントロールと入力チェック

htmlでデザインしたページにExtのリッチコントロール機能を付加するサンプル。簡単な解説はこちらでしています・・・usuilog-プログラミングメモ: [JavaScript][Ext]Ext.jsのフォームコンポーネントと入力チェック
htmlソース

<html>
	<head>
	<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
	<title>Form Control Example</title>
<script src="ext-1.1-beta1/adapter/yui/yui-utilities.js" type="text/javascript" charset="utf-8"></script>
<script src="ext-1.1-beta1/adapter/yui/ext-yui-adapter.js" type="text/javascript" charset="utf-8"></script>
<script src="ext-1.1-beta1/ext-all-debug.js" type="text/javascript" charset="utf-8"></script>
<script src="ext-1.1-beta1/source/locale/ext-lang-ja.js" type="text/javascript" charset="utf-8"></script>
<script src="formsample.js" type="text/javascript" charset="utf-8"></script>

<link rel="stylesheet" href="ext-1.1-beta1/resources/css/ext-all.css" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="ext-1.1-beta1/resources/css/ytheme-aero.css" type="text/css" media="screen" charset="utf-8" />
</head>
<body>
<form id="form1" name="form1">
<fieldset id="fxtest">
  <legend>Ext form control example</legend>
    <dt><label for="name-input">お名前</label></dt>
    <dd>
	  <input type="text" id="name-input" name="name" />
	</dd>
    <dt><label for="url-input">URL</label></dt>
    <dd>
	  <input type="text" id="url-input" name="url" />
	</dd>
    <dt><label for="mail-input">メールアドレス</label></dt>
    <dd>
	  <input type="text" id="mail-input" name="mail" />
	</dd>
    <dt><label for"blood-select">血液型</label></dt>
    <dd>
      <select name="blood" id="blood-select">
      	<option value="">選択してください</option>
      	<option value="A">A</option>
      	<option value="B">B</option>
      	<option value="O">O</option>
      	<option value="AB">AB</option>
      </select>
	</dd>
    <dt><label for"birth-input">生年月日</label></dt>
    <dd>
    	<input type="text" id="birth-input" name="birth" />
   	</dd>
    <dt><label for"income-input">希望年収</label></dt>
    <dd>
    	<input type="text" id="income-input" name="income" />万円
   	</dd>
    <dt><label for"carrier-area">経歴</label></dt>
    <dd>
    	<textarea id="carrier-area" cols="50" rows="10" name="carrier"></textarea>
    </dd>
    <dt><label for"promotion-area">自己PR</label></dt>
    <dd>
    	<textarea id="promotion-area" cols="50" rows="10" name="promotion"></textarea>
    </dd>
	<div id="submit-button"></div>
</fieldset>
</form>
</body>
</html>

JavaScriptソース

Ext.onReady(function(){
	// 入力エラー時のツールチップを有効にする
	Ext.QuickTips.init();
	
	var name = new Ext.form.TextField({
	 	allowBlank: false
	});
	name.applyTo('name-input');

	var url = new Ext.form.TextField({
	 	allowBlank: false,
	 	vtype: 'url' // SEE ALSO Ext.form.VTypes
	});
	url.applyTo('url-input');

	var mail = new Ext.form.TextField({
	 	allowBlank: false,
	 	// Custom validation
	 	validator: function(fvalue) {
	 		// Ext.form.VTypesより。@の前の.も許可
	 		var email = /^([\w]+)(.[.\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
	 		if (email.test(fvalue)) {
	 			return true;
	 		}
	 		return String.format('{0}はメールアドレスとして正しくありません。', fvalue)
	 				+ Ext.form.VTypes.emailText;
	 	}
	});
	mail.applyTo('mail-input');

	// 数字だけしか入力できない(automatic keystroke filtering
	// 日本語は防げないみたい
	var income = new Ext.form.NumberField({
		allowBlank: false
	});
	income.applyTo('income-input');

	var date = new Ext.form.DateField({
		allowBlank:false,
		format: 'Y/m/d'
    });
    date.applyTo('birth-input');

	var combo = new Ext.form.ComboBox({
        typeAhead: true,
        triggerAction: 'all',
        width:135,
        forceSelection:true,
		transform: 'blood-select'
	});

	var carrier = new Ext.form.TextArea({
		grow: true, // 入力量に応じてテキストエリアが拡張される
		growMax: 200
	});
	carrier.applyTo('carrier-area')
	
	var promo = new Ext.form.HtmlEditor({
		width: 600,
		height: 300
	});
	promo.applyTo('promotion-area');
	
	var submit = new Ext.Button('submit-button', {
		text: '送信'
	});
	submit.on('click',
		function(e,t) {
			var str = Ext.Ajax.serializeForm('form1');
			alert(str);
		}
	);
	submit.show();
});

splitの高速化

配列代入のコスト - マツモブログ

splitも中で配列を作ってるんじゃなかろうかと思いまして正規表現でパースしたのと比較してみました。

・・・中略・・・

微妙な差ですな。

訂正 2007/06/25


うちの環境では結構差がでました。
ちなみに正規表現を最小マッチにしたらもう気持ち早くなりました。

sub parse_regexp_min_match {
    $_[0] =~ /^(.+?)\t(.+?)\t(.+?)\t(.+)/;
    return ($1, $2, $3, $4);
}
Benchmark: timing 1000 iterations of regexp, regexp_min_match, split...
    regexp:  6 wallclock secs ( 6.86 usr +  0.00 sys =  6.86 CPU) @ 145.77/s (n=1000)
regexp_min_match:  6 wallclock secs ( 6.03 usr +  0.00 sys =  6.03 CPU) @ 165.84/s (n=1000)
     split: 15 wallclock secs (14.21 usr +  0.00 sys = 14.21 CPU) @ 70.37/s (n=1000)
                   Rate            split           regexp regexp_min_match
split            70.4/s               --             -52%             -58%
regexp            146/s             107%               --             -12%
regexp_min_match  166/s             136%              14%               --

コメントで頂いたとおり結果がまるで出鱈目だった為再計測しました。
ついでに第3引数指定した場合のsplitも追加。これはあまり変わりませんでした。
正規表現との比較では普通の正規表現ではsplitの方が高速ですが、最小マッチにすると正規表現が上回りました。
環境はperl5.8.8 Celeron1.80GHzです。

#!/usr/local/bin/perl

use strict;

use Benchmark qw(:all);

chomp (my @list = map { s/ +/\t/g; $_ } <DATA>);
# 運命の5,000王子
my @large_list = (@list) x 1000;

sub parse_split {
    return split "\t", $_[0];
}

sub parse_split_3 {
    return split "\t", $_[0], 4;
}

sub parse_regexp {
    $_[0] =~ /^(.+)\t(.+)\t(.+)\t(.+)/;
    return ( $1, $2, $3, $4);
}

sub parse_regexp_min_match {
    $_[0] =~ /^(.+?)\t(.+?)\t(.+?)\t(.+)/;
    return ( $1, $2, $3, $4);
}

my $res = Benchmark::timethese(1000, {
    "split"  => sub { parse_split($_)  for @large_list },
    "split_3"  => sub { parse_split_3($_)  for @large_list },
    "regexp" => sub { parse_regexp($_) for @large_list },
    "regexp_min_match" => sub { parse_regexp_min_match($_) for @large_list },
});

Benchmark::cmpthese($res);

__DATA__
マリポーサ  メキシコ    100000000   偽マッスルリベンジャー
アタル  キン肉星    1080000 ナパームストレッチ
ビッグボディ    カナダ  100000000   メイプルリーフクラッチ
スーパーフェニックス    オーストラリア  100000000   マッスルリベンジャー
ゼブラ  ナムビア    100000000   マッスルインフェルノ
Benchmark: timing 1000 iterations of regexp, regexp_min_match, split, split_3...
    regexp: 30 wallclock secs (30.02 usr +  0.01 sys = 30.03 CPU) @ 33.30/s (n=1000)
regexp_min_match: 17 wallclock secs (17.14 usr +  0.00 sys = 17.14 CPU) @ 58.34/s (n=1000)
     split: 21 wallclock secs (20.72 usr +  0.00 sys = 20.72 CPU) @ 48.26/s (n=1000)
   split_3: 19 wallclock secs (19.35 usr +  0.00 sys = 19.35 CPU) @ 51.68/s (n=1000)
                   Rate       regexp         split      split_3 regexp_min_match
regexp           33.3/s           --          -31%         -36%             -43%
split            48.3/s          45%            --          -7%             -17%
split_3          51.7/s          55%            7%           --             -11%
regexp_min_match 58.3/s          75%           21%          13%               --

Spket IDE

http://journal.mycom.co.jp/news/2007/06/20/033/index.html

1.5.11では新しくExtJS 1.1の開発機能が追加された

Demo:http://www.spket.com/demos/extjs.html

コード補完とかAPIドキュメントがポップアップで出たりとかクラス定義に飛べたりとか便利。

Ext.jsでAjaxその2

Ext.element.load。要素を指定して結果で内容を置き換えます。サーバがhtml部品で返してくる場合はこれが一番簡単かな。

var elem = Ext.get('test_elem');
elem.load(
    'test',
    {param1: 'value1'}, // これを渡すとPOST。GETにしたいときはURLにくっつける
    function(elem, success) {
        if (success) {
            Ext.MessageBox.alert('Result', 'Success');
        } else {
            Ext.MessageBox.alert('Result', 'Error');
        }
    }
);
elem.show();

Ext.jsでAjaxその1

ベースライブラリを直接利用するパターン。1.1以降はExt.Ajaxになるみたい。

var ajax = Ext.lib.Ajax.request(
    'GET',
    '/test?' + param_string,
    {
        success: function (res, args) {
            var text = res.responseText;
            // 何らかの処理
        },
        failure: function (res, args) {
            var text = res.responseText;
            // 何らかの処理
        },
    }
);