D-7 <altijd in beweging>

Day to day life of a Perl/Go/C/C++/whatever hacker. May include anything from tech, food, and family.

2008年03月

今更菓子パン関連のエントリを読んだ。

全然amachang本人とは関係ないんだけど、一言言わせていただきたい。

まずひとつ。コンビニで売ってる物を食べ続けて健康でいれるわけがない。あ、検証なんてなんもしてません。でも製造過程が不明なものをそこまで信用できん。俺が作った物は(当たり前だが)着色料も保存料も使ってないし塩分も油分も自分のさじ加減でちゃんとコントロールしてるもん。どちらかを信用しろと言われたら自分で作った物を信用する。農薬云々はこの際ほっとけ。どうせコンビニも似たような、もしくは俺が使ってるより悪い材料使ってる。

あともう一つ。飯に金を使わないのはそれもチョイスだ。だけれども、それによって現在の日本で経験できる様々な味(=文化)を享受しないのはもったいない。世界でもこれだけ多様な味が(しかも完成度の高い形で)てに入れられる国なんてそうそうないのに。

ちなみにこのエントリは酔っぱらって書いた。文句言うな。
    このエントリーをはてなブックマークに追加 mixiチェック

映画は以前に酷評した気がするが、この原作はひじょうにおもしろかった。

映画はまぁあれはあれでいいのかもしれんけど、これを読んだ後だと、ディテールをはしょりすぎだろ。 こちらの原作は素直に面白かったです。
    このエントリーをはてなブックマークに追加 mixiチェック

本屋に寄って、特に何を買おうとも思ってなかったのだけれども目についたのが「ライドバック」。封がしてあって中身はチェックできなかったのでジャケ買いで買った・・・らおもしろかった!

これはちょっといいかも。今8冊出ているのだけれども、多分明日揃える。

なんか最近の漫画は絵が荒いほうがおもしろいなぁ。

追記:あ!作者さんははてなユーザーだ!
    このエントリーをはてなブックマークに追加 mixiチェック

天気もいいので朝9時くらいに九段下へ。千鳥ヶ淵で桜鑑賞。そのまま靖国神社に向かって、軽く散歩。靖国神社の裏の池があるところではらはらと落ちる桜をながめる。

そのあと折角でばってきたので、そのまま神保町へ歩いて行ったら、ちょうど古書市だかなんだかをやっていてちょこちょこ本を見ながら神保町を散策。

ちょうどお昼の時間になったので人形町まで出て「キラク」へ。すこぶる久しぶりに暫定日本一のポークソテーを食べた。やっぱりうまいなぁ。サラダ大盛りとかできるってしらんかった。今度はそうしてみよう。人形町も桜さいてた。

以上で今日は帰宅。結構あちこちの桜ば見れた。
    このエントリーをはてなブックマークに追加 mixiチェック

はてブでチラ見した知識だけでプログラマー同士の会話を妄想してみる。
http://neo.g.hatena.ne.jp/llpp/20080325#p1

俺は渋谷界隈のバーに出没しまくりですが、プログラマー二人なんて組み合わせの客はほとんどいねぇっすよ。俺とcharsbarさんが来るとか、そういうくらいじゃないの?と思ってしまいますが、笑いました。

ところでプログラマーは洋酒好きがあまりいないんでしょうか。みんな居酒屋に流れちゃうよな。まぁバーは騒ぐところでもないからかな・・・

静かにゆっくりが好きです。
    このエントリーをはてなブックマークに追加 mixiチェック

9時半くらいに東急本店へとさしかかる道をスペリオールを読みながら歩いていたら、突然視界に警官が。テープはってる。

「すみませんがあちら側(車道)を通ってください!」

ん?なんだ?工事?と思ってたら地面に一面の血。ぎゃーす!

担架がある。でも人はいない。血だまりになってるところはほぼビルの入り口のところなのでそのまま視線を上にもっていくと、そこに警官に抱えられた顔が血みどろの男。ぎゃーす!

正直何がおきたんだか全然わからないが、とにかくビビった。

いったい何が起きたんだ。渋谷怖いよー。
    このエントリーをはてなブックマークに追加 mixiチェック

前のエントリのC++でmecab動かすと文字化けする??の件、id:download_takeshiさんのところでやってたプログラムが動いたとの事でコピペしてみたら、動く!違いを見るとg++の代わりにcc使ってる事くらいかと思ったけど、よーくみるとmecab-config --libs|--cflagsを明示的に指定している。俺はこれは適当に手で-I -L等を指定していたのだ。

するとよく考えると俺は今までこのコマンドを実行していた
g++ -lmecab mecab.cpp
ん・・・まてよ・・・mecab-config --cflagsと--libsの値は・・・?
daisuke@beefcake $ mecab-config --cflags -I/usr/local/include daisuke@beefcake $ mecab-config --libs -L/usr/local/lib -lmecab -lstdc++
あああ、ひょっとして・・・
daisuke@beefcake $ ls -l /usr/local/lib/libmecab.* /usr/local/lib/libmecab.1.0.0.dylib /usr/local/lib/libmecab.dylib /usr/local/lib/libmecab.1.dylib /usr/local/lib/libmecab.la /usr/local/lib/libmecab.a daisuke@beefcake $ ls -l /usr/lib/libmecab.* /usr/lib/libmecab.1.0.0.dylib /usr/lib/libmecab.dylib /usr/lib/libmecab.1.dylib
なああにいいいい。二つコピーがある・・・orz orz orz

っていうことは、去年の9月?くらいにコンパイルしたとおぼしきバージョンにはこの問題があるのかもしれん。もしくはなんかが根本的に違うのか。ともあれ、リンクしているライブラリのバージョンが違った!という落ちでした。はぁがっかり

ちなみにid:download_takeshiさんのプログラムを最初のg++ -lmecab takeshi.cppみたいにして動かすと、ハングしてました。明らかになんかおかしかったんだね。
    このエントリーをはてなブックマークに追加 mixiチェック

ただ単純にC++でmecabでパースした文字列を表示したいのですが、文字化けします。なんでなんで?Perlに関してはえらそーに語る事もありますが、C++初心者っす・・・誰か教えてください

#include #include using std::cout; using std::endl; using std::hex; int main(int argc, char **argv) { char input[1024] = "もももすももももものうち"; char *args[] = { "", "-Owakati" }; const char *parsed; MeCab::Tagger *t = MeCab::createTagger(2, args); parsed = t->parse(input, strlen(input)); cout << "original input = " << input <<< "parsed string = " << parsed << strlen(input); i++) { cout << "input[" << i << "] = " << hex << (unsigned int) input[i] << endl; cout << "parsed[" << i << "] = " << hex << (unsigned int) parsed[i] << endl; } delete t; return 0; }
最初のoriginal inputとparsed stringのところでcoutが化けるのでバイト単位でcoutしてみたんだけど、これだとparsedのほうがほとんどのところで最後の1バイトが抜けてる感じ。何が間違ってるんでしょうか・・・
    このエントリーをはてなブックマークに追加 mixiチェック

gungho-poe-engine.jpgこれ、前にも書いた気がするけど、enscriptメモ。なにもせずに動いた気がするのでOS Xにはデフォルトで入っているのかな?一般的に1ページに2カラムで表示したものをプリントする場合
enscript -2uG filename.txt

これをどっかのpsファイルに保存しておく場合。これだとプリントプレビュー的な事ができる。
enscript -2uG -p example.ps filename.txt

ちなみにOS XのPreviewならこの一枚一枚をJPEGとかに保存することも可能。別名で保存を使うだけ



    このエントリーをはてなブックマークに追加 mixiチェック

YAPCで話す話の内容をうっすら考えている。一応Perl界隈で俺の得意分野?というと多分XS使ったりなんだりってほうだと思ってたので最初はCache::Memcached::libmemcachedを作る過程と織り交ぜてXSの使い方みたいなのを話そうかと思ったんだが、最近のエントリの食いつきを見ていると「テストの書き方/リリースサイクルの自動化」について話したほうがいいのか?と思い始めている。

どうなんだろうね、そこ。迷い中。
    このエントリーをはてなブックマークに追加 mixiチェック

金曜日は家に帰って鶏肉、油揚げ、もやし、その他残り物野菜を炒めてあんかけ風味にして。

土曜日は横浜でバーゲンやってたので行ってきた。期待して行ったんだけど、あんまりなかったのでいつものごとく消耗品のシャツとか靴下買ってきて終了。あとはユニクロでちょいと買い物。ひさしぶりに服買ったなぁ。昼は日吉のくるくる寿司。夜は蕎麦。卵を落とした。

今日は多分一枚残ってるえぼし鯛の干物かな。
    このエントリーをはてなブックマークに追加 mixiチェック

080302_1941~0001.jpg
ホワイトデーなんちゃらの関係でグランプラスというところの「ペカンナッツショコラ」を手に入れた。これがう、う、う、うまーーーーーーーーい。こいつ本当にうまいよ。口の中で溶ける。そして後にしつこい甘みが残らない。ペカンの香ばしい味。うまいー。ウィスキーともあいそうだわあ。おススメ。品川のエキュートに店があるっす
    このエントリーをはてなブックマークに追加 mixiチェック

ネタですが、実話です。

  • use Module; 宣言を書く時、全部ABC順にしないとイライラする。
  • s///とかをs{}{}って書くとエスケープいらないのに、s/\\\/\\\/\\\\/g s/\\\/\\\//\\\\/gとか見るとイライラする。(追記:なんか一個抜けてた)
  • qw// をqw()と書き直さずにはいられない。
  • for(my $i = 0; $i < 10; $i++) とかを見ると for my $i (0..9)に書き直さずにはいされない
  • 他人からもらったファイルを開ける前はとりあえず perl -i -pe 's/\r\n/\n/g'
  • if ( ... ) elsif (...) elsif (... ) else ... みたいなのを見るととりあえずディスパッチテーブルを書きたくなる
  • if ($foo eq 'XXX' || $foo eq 'YYY')を if ($foo =~ /^(XXX|YYY)$/) に書き直したくなる(追記:ベンチ取ったけど、比較する対象の数が6個になると正規表現のほうがはやかった。4個まではeqのほうが速い)
  • 配列かハッシュを宣言する時に最後の要素でも必ずカンマを入れないと気が済まない
なんかもっとありそうだな。

僕と一緒に書いてる方達、別にこれをやらないと僕が怒るわけじゃないです。
    このエントリーをはてなブックマークに追加 mixiチェック

今回作った夙川アトムモジュール。モジュール自体は実にアホなモジュールなわけですが、まぁドキュメントにも書いた通り0.00001なんて全然変換が効いてませんでした。そこで「パッチはいらん、テストをくれ」と書いたわけですね。

そしたらまずtypesterさんがテストをTest::MoreからTest::Baseにしてくれて、otsuneさんがどんどんテストを足してくれたらどんどんアトム君が賢くなってきた。

今はAcme::Shukugawa::Atomのテストはt/01_basic.tにいわゆる最終テストのような、いわゆる文章を変換するようなものを置いて、それ以外のt/02_shisu.t t/03_waiha.t t/04_kuribitsu.tでそれぞれ内部で使ってる基本の変換である「シースールール」「ワイハールール」「クリビツルール」を単体テストしている感じ。やっぱり単純なソフトでもこの単体テストと最終テストの2段階があると問題点がすぐに浮き彫りになって実にやりやすい。

こんなモジュールでさえその恩恵を受けられるのだからテスト、愛しいよテスト。

というわけで段々賢くなってきているアトム君ですが、先ほど0.00004をリリースしました。このバージョンから板付き語(固定変換ルールが決まっている言葉)をshareファイルに抜き出して、モジュールとは別に変更できるようにしたり、デフォルトの板付き語ルールの他にオブジェクトインスタンスで指定できるようにしてあります。あと、棒(ー)が二つついちゃったりしてるような問題も結構直ってると思いますヨ。
    このエントリーをはてなブックマークに追加 mixiチェック

去年の終わりくらいからそれまで数年間MacBookでメインで使ってたFireFoxの調子が悪くなってよく落ちるようになってきた。最初はまぁいいやーぐらいで思ってたんだけどJavascript実行中に止まったりなんだりして段々うざくなってきたので先月からSafariに乗り換えてみた訳です。ちょうどその直後にdolipoも出たり、Javascriptの実行も速いし、なによりページのロードが速い!と、いい感じだった。が。

今日FireFox3 Beta4 を入れてみたら、なんかすげぇ速くなってる!gmailもサクサク、LDRもサクサク。まだ使い倒してないのでエラーとかそのあたりがどうなってるかわからないけど、これはいけるかも・・・と迷ってしまっている。もともとFireFox使いだしな。ただ、今のところFireBugが使えないというイヤンな状態。

さて、どうしようかな。
    このエントリーをはてなブックマークに追加 mixiチェック

この間のめちゃいけを見て夙川アトムに惚れた人も多いと思いますが、なんか今日急にルー語ばりに日本語を夙川アトム風に変換するモジュールを書きたくなったので書いてみた。そして先ほど無謀にもCPANにリリースしてみた

正直まだ全然変換自体には自信がないんだけれども、せっかくcodereposに入れたので、パッチをくれとは言わん。せめてテストを足してくれ!どういう変換が望ましいのか、いっぱい台詞をいれてくれ。なるたけそれに添えるようにテストしてみるよ。

(追記 2008/03/15: @bayashiさんのバージョンがとまったらしいので暫定版をうちのサーバーにあげておいたよ
    このエントリーをはてなブックマークに追加 mixiチェック

前のエントリで書いたメモリリークを修正したText::CSV_XS 0.37がリリースされたみたいですよ

メンテナのMerijnと何回かメールのやりとりをした。このメモリリークの根源は前のエントリで書いた通りだったのだけれども、それと連動する形でgetline()を使った時にmeta_info()が仕様通りに動かないというバグがあった模様。これを直すとメモりリークも直ったとの事。

このフィックスを見てはたと気づいたのだが、sv_2mortal()しないでも、new(SV|AV|HV|RV)した変数は他のコンテナに関連づけると(格納すると)メモリ解放が自動的に行われるのだったな。

例えば
my %h = ( a => 1, b => 2, c => 3 );

こんな状況の場合、値側の1, 2, 3はそれぞれSV なわけだが、%hが自分の子要素として1, 2, 3が存在するのを知っている状態なので、%hのリファレンスカウントを--した時(SvREFCNT_decが呼ばれた時)に同時に1, 2, 3にもそれぞれリファレンスカウントがひとつ下げられる。その時にメモリ解放が行われる。

これらのSVを作る時にnewSV()しただけでsv_2mortal()もせず、%hにも格納しなければ、これらのSVはリークする。なぜなら、newSV()した時点でREFCNTは1で、それをSvREFCNT_dec()しないとメモリ解放されないのに、mortal化もされてないからだ。これがText::CSV_XS 0.36の状態。

Text::CSV_XS 0.37の場合はこのリークしていた変数に対して明示的な解放が行われるケースと、$selfに該当するハッシュに格納されるケースが追加された。これで必ずメモリ解放が行われるのでリークもない、ということだ。

Happy CSV parsing.
    このエントリーをはてなブックマークに追加 mixiチェック

今日は帰ったら冷蔵庫に晩飯になるものが何も無い。ご飯。豚肉。タマネギ。うーむ。

はっ、そうだ!豚丼つくるでぇ!醤油と砂糖1対1、3倍の水。そこにタマネギとニンニクと豚肉をつけこんで揉み揉み。30分くらい漬けて、できあがりぃ。焼く直前に胡麻油を混ぜ込んでフライパンでうわっと。

あとは熱々ご飯にのせてでーきあがり!

ってことで今日は豚丼でした。
    このエントリーをはてなブックマークに追加 mixiチェック

この間もメモリリークを通報してくれたところからまたメモリリークで困っているという通報が来たので、チェック。普段は$csv->parse(); $csv->fields()を使っているのだけれども、どうもパフォーマンス的にgetline()のほうが速いのでそちらを使いたいとのこと。

まず以下のテストコードを動かしてメモリを観察
use strict; use blib; use Text::CSV_XS 0.36; print "My PID: $$\n"; my $line = join("\t", ('a'..'z')) . "\n"; my $csv = Text::CSV_XS->new({ sep_char => "\t" }); my $iter = 10000; my $data = $line x $iter; my $count = 0; while(1) { open(my $io, '<', \$data); while(my $row = $csv->getline($io) ) { } $count += $iter; print "Did $count iterations\n"; sleep(2); }

これを自分のMacBookで見てると外側のwhileループを回す毎に600Kくらいずつメモリ使用量が増えて行く。リークしてるっぽいなぁ。

実はこの問題、ちょっと前にも見た事あったんですが、その時は特にひらめかなかったのです。今回もなんの気無しにText::CSV_XSのソースを見ていたら、リークしないparse()とgetline()を追って行くとどうも1個だけreturnされてるわけでもない、微妙に操作方法が違う変数があることに気づいた。

前のText::MeCabのエントリでも書いたけれども、Perlにはふたつメモリを解放するタイミングがある。そのうちのひとつでいわゆる一時変数が解放されるのだが、Perlでは一時変数も含めて、Perl内でデータとして扱えるもの全てのメモリ管理はリファレンスカウントで管理しているため、その調整を手動してやらないとプログラム終了時まで解放される事は無い。

でも一時変数まで全部律儀にそういうカウント云々していると面倒なので、Perl内部では"mortal"と呼ばれる変数が存在する。これらの変数は「今使い終わったら、次にチャンスがある時にメモリ解放される」という存在だ。これを設定するには、変数を作成後、sv_2mortal()という関数でフラグをつける:
SV *sv = sv_2mortal( newSVpv("Hello!", 6) ); AV *av = newAV(); sv_2mortal( (SV *) av);

これでこのCのセクションから抜けて、Perlインタプレタがチャンスがある時にメモリ解放が行われる。

さて、本題のText::CSV_XSだが、これがどうもある配列にされてなかった模様。以下パッチ
--- Text-CSV_XS-0.36/CSV_XS.xs 2008-03-06 00:26:41.000000000 +0900 +++ Text-CSV_XS-0.36.patched/CSV_XS.xs 2008-03-11 18:19:00.000000000 +0900 @@ -1083,6 +1083,7 @@ CSV_XS_SELF; av = newAV (); avf = newAV (); + sv_2mortal((SV *) avf); ST (0) = xsParse (hv, av, avf, io, 1) ? sv_2mortal (newRV_noinc ((SV *)av)) : &PL_sv_undef;

これでとりあえず先ほどのスクリプト上では問題がなくなったようなので、P5Pにメールを送っておいた。今のところレスポンスはないが・・・

#これだけ偉そうにしておいて間違ってたらどうしようと今軽く焦っている
    このエントリーをはてなブックマークに追加 mixiチェック

例えばCSVなファイルを読み込んで、それをハッシュの中に展開、格納と言った感じの動作をPerlで行いたかったとします。例えば
1,2,3

と言った行を
my %hash = ( 'col1' => 1, 'col2' => 2, 'col3' => 3 );

のようなハッシュに展開する関数が欲しいわけです。皆さんはこれをどういう風に実装しますか?ぱっと思いつくのはforループですよね
my @colums = ('a', 'b', 'c'); my @values = (1, 2, 3); # もちろん実際にはsplit(/,/, $line)とか、CSVパーサーを使う my %h; for (0..$#columns) { $h{ $columns[$_] } = $values[$_]; }

実はこのようなCっぽい書き方はPerlでは大概遅いです。DWIMな言語なので、その辺りはPerl内のオプティマイザに任せるようなコードを書いたほうが俄然性能が良くなるのです。そこで僕は今まで自分の中でHash Magicと呼んでいるものを使ってました。これは、%hashとして定義した物に対して、複数の値を一度に挿入する事ができるものです。
my @colums = ('a', 'b', 'c'); my @values = (1, 2, 3); my %h; @h{ @columns } = @values;

%h から @hの辺りがややこしいかもしれませんが、これがハッシュに対して複数キーを一度に設定する時の書き方です。これはforループに比べると遥かに高速に動きますし、なにより@columnsの中身を一カ所変えるだけで勝手にカラム名の追加/削除/変更に対応してくれるのが嬉しい限りの書き方です。

ですが、どうやらベンチマークを取ってみると妙な事になってきました。

さて、ここからは実際のベンチマークコードをみてもらいましょう。まずは基本のベンチマークコードです。
sub run_bench { my %args = @_; my @columns = @{ $args{columns} }; my @values = @{ $args{values} }; cmpthese(500_000, { hash_magic => sub { my %h; @h{ @columns } = @values; }, forloop1 => sub { my %h; for (0..$#columns) { $h{ $columns[$_] } = $values[$_]; } }, forloop2 => sub { my %h; my $count = 0; $h{ $columns[$count++] } = $_ for @values; }, manual => sub { my %h; $h{a} = $values[0]; $h{b} = $values[1]; $h{c} = $values[2]; $h{d} = $values[3]; $h{e} = $values[4]; }, automanual => do { my $code = "sub {\nmy \%h;\n"; for my $i (0..$#columns) { my $col = $columns[$i]; $code .= "\$h{'$col'} = \$values[$i];\n"; } $code .= "}\n"; eval $code; }, }); }

ご覧のように2パターンのforループ、hash magic、そして手動でカラム名→カラム番号を紐付けるルーチンを用意してます。最後のautomanualは後ほど解説します。

これをまず、キーの文字列が1文字('a', 'b', 'c')と言った1文字だけの場合のベンチを取ってみます。すると結果はこんな感じになりました:
Rate forloop1 forloop2 automanual manual hash_magic forloop1 233645/s -- -20% -58% -59% -62% forloop2 290698/s 24% -- -48% -49% -52% automanual 561798/s 140% 93% -- -1% -8% manual 568182/s 143% 95% 1% -- -7% hash_magic 609756/s 161% 110% 9% 7% --

ぬはっhash_magic超速ス!とか思ってエントリを書きかけたのですが、その後カラムキーの長さを約30文字に変更してみたのです。そうしたらなんと結果が違ってきました:
Rate forloop1 forloop2 hash_magic automanual manual forloop1 201613/s -- -17% -54% -64% -64% forloop2 243902/s 21% -- -44% -56% -57% hash_magic 438596/s 118% 80% -- -21% -22% automanual 555556/s 176% 128% 27% -- -1% manual 561798/s 179% 130% 28% 1% --

今度は手動のほうが速い!ちなみにこの差はだいたいキーの長さが5文字くらいになったところで出始めます。先ほどのrun_bench()を使用してキーの文字列長を変えて行くベンチマーク用コードはこんな感じです:
use strict; use warnings; use Benchmark qw(cmpthese); { # Benchmark short keys my @columns = ('a'..'e'); my @values = (1..5); run_bench(columns => \@columns, values => \@values); } { # Benchmark medium keys my @columns = map { "col-$_" } (1..5); my @values = (1..5); run_bench(columns => \@columns, values => \@values); } { # Benchmark long keys my @columns = map { "col-abcdefghijklmnopqrstuvwxyz-$_" } (1..5); my @values = (1..5); run_bench(columns => \@columns, values => \@values); }

どうも推測するに長い文字列の入った配列を@hに代入する時にその取り出し分のコストのほうが上回ってくるということみたいです。だから固定文字列として指定した'manual'のほうがパフォーマンス的に上回ってくるわけですね。

元々hash magicを使おうと思ったのは、最終的な動作時までカラムの名前や数が分からない場合でも動くものが作りたかったからなんですが、これを見てくるとどうにかその辺をうまくできないかと思いだしたわけです。それが上記run_bench()中のautomanualという項目で指定されている関数です。

この項目、何をやっているかというと、動的に与えられたカラム名を明示指定した関数を作成してます。@columnsの中身によって関数の定義自体を文字列として作成し、evalしてるわけです。これで一応「動的」に「手動/明示指定」な関数が作れる訳です。

でも醜い。美しくない。そもそもこのコード自体がものすごく遅ければ問題ですが、正直それほど効果あるのかなぁ、という疑問も抱いています。要はこのようなトリックを使う事によって起こる可読性の損失と、実際のパフォーマンスの向上のバランスの問題ですね。

今回はこのコードが2000万回ほど呼ばれる予定なので多少差は出ると予測されるので多分使用しますが、僕は元々「いらん最適化は最適化しない場合と比べて改善どころか改悪になる」というスタンスの人間ですし、こういった、Perlビルトインなものに対してのベンチマークというのは結構その結果を鵜呑みするわけにもいかないので正直ちょっと悩みどころです。

まぁ多分もとの@h{@cols} = @valueと言った意図をコメントか何かに残しておいて、その上で最適化する方法を取ると思います。
    このエントリーをはてなブックマークに追加 mixiチェック
カテゴリー
記事検索
メッセージ

名前
メール
本文
アーカイブ

このページのトップヘ