〜2004年5月中旬〜
Rubyの拡張ライブラリの仕組みは簡単だ。 require 'foo'を実行すると
ということが行なわれる。
だから一番簡単な何もしない拡張ライブラリは
Init_foo(){}
だけいい。
% echo 'Init_foo(){}' > foo.c % ruby -rmkmf -e 'create_makefile("foo")' creating Makefile % make gcc -fPIC -Os -fPIC -I. -I/usr/local/lib/ruby/1.9/i386-linux \ -I/usr/local/lib/ruby/1.9/i386-linux -I. -c foo.c gcc -shared -L'/usr/local/lib' -o foo.so foo.o \ -lruby -ldl -lcrypt -lm -lc % ruby -r./foo -e 'p $"' ["./foo.so"]
何にも依存してないんだから、もっと簡単に
% gcc -shared foo.c -o foo.so % ruby -r./foo -e 'p $"' ["./foo.so"]
で十分。
もうちょっと実用的なものとしては
% cat >binmode.c #include <fcntl.h> #include <stdlib.h> Init_binmode(){_fmode = O_BINARY;} % gcc -mno-cygwin -s -shared binmode.c -o binmode.so
なんてのが考えられる(msvcrt用)。
% /c/mingw32/bin/ruby -e 'open("foo.txt","w"){|f|f.puts}' % od -c foo.txt 0000000 \r \n 0000002 % /c/mingw32/bin/ruby -r./binmode -e 'open("foo.txt","w"){|f|f.puts}' % od -c foo.txt 0000000 \n 0000001
このようにrubyの変数や関数を参照しない場合は非常に簡単。 でも、普通はクラスを作ったりするのでこんなに簡単には済まない。 特にWindowsのDLLのような場合は。 [ruby-talk:99770]がcrashするのはそのあたりに原因がある。
とここまで書いてて眠くなった。つづく(かも)。
Ruby 1.6.8にはsort_byはないけど、 shimにも含まれているように、sort_byは
def sort_by ary = map { |i| [yield(i), i] } ary.sort! { |a, b| a[0] <=> b[0] } ary.map! { |i| i[1] } end
と書けます。これはPerlのSchwartzian Transformです。 これでなぜ速くなるかというと、 時間がかかるget_post_time()を各ファイルに対して1度しか呼ばなくなるからです。
cacheという意味では
tcache = {} @storys.sort! {|f1,f2| (tcache[f1] ||= get_post_time(f1)) <=> (tcache[f1] ||= get_post_time(f2)) }
のようにするのがお手軽です。 たしかEffective Perlでシャチ泳ぎという名前で紹介されていた手法です。 こっちのほうがわかりやすいですね。
それと あのpatchに含まれている、lily.cgiのRUBY_VERSIONと、 plugin/trackback.rbの//nの部分に関してはRuby 1.6でも有効なので、 できれば取り込んでください。
とうとう100000を越えた。 90000からは4ヶ月。
先月GCCを3.4.0に上げたけど、その直前にカバレッジを測定したデータファイルがすでに使えなくなっていることがわかった。
% gcov main main.gcno:cannot open graph file
GCC 3.3.xまではmain.daだったんだけどなあ。 GCCのlibgcc2.cを見ると3.2と3.3の内部構造も違う。 minorが上がるたびに何かいじってるのか。
lcovはどうかなと見に行ってみると、早速lcov 1.3でGCC 3.4.0に対応してるようだ。 素早い。
Changes:
This release fixes a bug which could result in dnsmasq losing track of DHCP leases under certain very specific circumstances. It also fixes interoperability with the Linux kernel's built-in DHCP client, and has the usual small crop of configuration enhancements.
ruby_1_8 branchのversion.hが1.8.2になった。 configureの実行も忘れずに。
Downtime This Evening
Bloglines will be down this evening starting at 7pm Pacific Time. We've grown so much recently that we have run out of room to add more machines at our hosting provider, so we need to move to a larger area. We expect the move to take 4 hours.
またオーバーオールのおやじが登場。 しかし、Bloglinesが見られないとニュースを見るのも面倒なことに。 かなり依存してるなあと感じる今日この頃。
試すデータがなければ作ればいいわけで、10000個ほど用意してみた。
% mkdir log/tmp % ruby -e '10000.times{|i| open("log/tmp/data%06d.txt" % i, "w"){|f|f.print "foo\nbar\n"}}' % ruby httpd.rb & % time wget -q -O /dev/null http://localhost:10080/lily.cgi wget -q -O /dev/null http://localhost:10080/lily.cgi 0.01s user 0.00s system 1% cpu 0.689 total
結構速い。
でも、sort_byはrespond_to?か何かで調べて、なければ定義するほうがいいでしょう。
unless [].respond_to? :sort_by module Enumerable def sort_by ... end end end
実はさっき間違えてデータをlog/に作ってしまったんだが、ちょっと困ったことになった。 単純にmvすればいいやとmv *.txt tmpとしたら
zsh: argument list too long: mv
となってしまうのだ。まあ、そうだよな。10000個もあれば。 この場合は消してやりなおせばいいんだけど、消せないときはどうすればいいのか?
最初に思い浮かぶのはfindとxargsの組み合わせだが、
% find . -name '*.txt' -maxdepth 1 | xargs -i mv '{}' tmp
では、'{}'はファイル1個1個に展開されるようで時間がかかりすぎる。 というか、何のためのxargsだ。それならfindの-execでいい。
mv --helpを見ると
--target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY
というオプションが見つかった。おぉ。流石GNU fileutilsだ。cpにもあるね。
% find . -name '*.txt' -maxdepth 1 | xargs mv --target-directory=tmp
でいいわけだ。
まさか(S)DBMを使う処理がそのまま取り込まれるとは思ってなかったので、今までの.entrydateを使うバージョンの高速化を考えてみる。
一番時間がかかっているのは.entrydateの読み込みの部分。 この部分を最初に1回だけ読んで、Hashで取っておけばよさそう。 そうすればsort_byを使う必要もない。
def get_post_time(file) unless @done @ptimes = {} begin File::open("#{@datadir}/.entrydate","r"){|f| f.readlines.each{|pts| file, time = pts.split(/@@/) @ptimes[file] = Time.rfc2822(time) } } rescue end @done = true end @ptimes[file] ||= begin mtime = File::stat(file).mtime @entrydate ||= File::open("#{@datadir}/.entrydate","a") @entrydate.puts "#{file}@@#{mtime.rfc2822}" mtime end end
これでsortも元に戻して実行。
% time wget -q -O /dev/null http://localhost:10080/lily.cgi wget -q -O hoge.html http://localhost:10080/lily.cgi 0.01s user 0.00s system 1% cpu 0.889 total
まずまずか。でもこうするなら読み込みはinitializeでやるべきだな。
Perlには__DATA__があるが、Rubyにはない。 __END__と__DATA__の違いは簡単に言うと対象となるファイルが$0と__FILE__との違いと思えばいい。 main::DATAとPACKNAME::DATAという違いもあるか。
Rubyで書いてみると
DATA = open($0) DATA.gets("__END__\n")
と
DATA = open(__FILE__) DATA.gets("__DATA__\n")
かな。とlily.cgiを見てて思ったり。
つまり、lily.cgiをlily.rbと名前を変えたとしても
% ruby -rlily -rcgi -e 'Lily.new(CGI.new, {})' </dev/null ./lily.rb:81:in `initialize': uninitialized constant Lily::DATA (NameError) from -e:1:in `new' from -e:1
となってしまうので
open(__FILE__) do |data| data.gets("__END__\n") eval data.read end
としたほうがいいのかもしれない。というか、Rubyにも__DATA__が必要か。
vimを使ってRubyスクリプトを編集しているときに気になるのが、#を先頭でタイプしたとたんに勝手にインデントすることだ。 元々コメント文字である#をインデントするスタイルを取らないというのもあるが、 一時的なコメントであることが多いので、 それを強調するためにも#は先頭に残しておきたい。
:help 'indent*'あたりから調べてみると、どうもindentkeysがあやしい。
"0#" if you type '#' as the first character in a line
つまり"0#"をindentkeysから取り除けばいいわけだ。~/.vimrcで
au FileType ruby setlocal indentkeys-=0#
としてみた。おぉ、ビンゴ。快適快適。
disp_*もplugin/へ追い出してしまえば、かなり好きなようにいじれるんじゃないだろうかと、ふと思った。
すばらしい。早速明日現実逃避しなきゃ。
ここ1年ぐらい全然読んでないので講読を止めた。 cygwin-announce MLだけで十分かな。情報が欲しければ アーカイブも公開されてるので、googleで検索できるし。
まあ、要するに制限なしってことだよなあ。 実際に1TBも使うやつはいないだろうし。
まずは肝である The Google File System(PDF)を読んでみるか。
やばいので更新した。 ProxyCommand拡張パッチは1行だけのずれだったので、全然問題なし。 socks5を試すの忘れてたけど、http proxyが問題なく動いてるからいいや。
バグって表現もなんか面白い。でも、1TBは無理だとしても10GBぐらいならありうる話だなと思ってみたり。 それぐらいの差別化はしてくるんじゃないだろうか?
いまここにある2,3年分のメールが900MBほどあるので、1GBじゃすぐ足りなくなる予感。 wormやらspamやら取っておくなという話ではあるが。 あ、そういえば半年ぐらい前からそいつらは別の場所に移動させたんだ。 500Mほど増えた。まじで消せって。