海野秀之(うんのひでゆき)の外部記憶
Twitter (twilog) / RSS / アンテナ / ぶくま
なぜ、いも焼酎なのか。 そういえば、妻のお母さん(わたしから見ると「お義母さん」)がくるので、 ビールを買っておこうと言ったら (僕はお酒を飲まないので、うちにはお酒がない)、 もう寒くなってきたので、ビールよりいも焼酎かもと妻。 いも焼酎といっても何を買ってくればいいのか?というのが目下の プチ懸案事項だったのでした。
それより、スペースシャトル。 スペースシャトルそのものよりも、 この手の巨大プロジェクトにつきまとう「嘘」について。 はたして、おおきな予算に嘘はつきものなんでしょうか?
原理的に不可分であるはずはないと思うのですが、 嘘を排除するのはとても難しそうにも見える。 どうしてなんでしょうね。
間違いが許されていないことが一因かな、とは思う。 やってみないとわからないことだってあるはずなのだから、 やってみたけどまずかったという事が後から判明するのは当然のことだと思う。 むしろ、そういう知識が増えてこそ、やってみた価値がある。
けど、実際は、たくさんのお金を費したプロジェクトに失敗はゆるされず *1、 「やってみたけど、まずかった」事などなかったことにされることが多い。 それでは、やってみないとわからないことは、いつまでもわからないままになってしまう じゃないか。
そういうやりかたの行き着く先は、2つしかない。
いずれも、進歩には結びつかないですね。
技術的に真っ当な予想が当たるほど世の中は甘くない とか言われてしまっていますが、そんな世の中いやだよ。
*1 多くの場合、途中で方針変更することも許されない。
変分法についてまとまった資料がないかなぁと探していたら、 次のふたつを発見。
単一のソースから、 html, PDF ということなる文書形式に変換されているらしいところも ポイントが高い。 html ソースをみると、 docutils なるものが用いられているのがわかる。
趣味でしこしこ書いているプログラムがある。 たくさんのデータを読んできて、いろいろ分析してみようというもの。 いろいろ遊べそうで、わくわくしている。
元のデータは人間が読むための形式で、機械で読むのはちょっと重い。 フォーマットが人にやさしい(機械に厳しい)だけでなく、 各要素が文脈非依存にはなっておらず、 モデルをシミュレートしながら読まないと確定しないのだ。 (これで通じるとは思っていません、ご心配なく?)
そこで、人間にはまったく読めたものではないが、機械で読むのは簡単な形式 (それでも、やっぱりテキストファイル)に一旦変換してしまうことにした。
変換にかかった時間は 20 分、 変換したファイルを読んで、オブジェクトメモリを再構築するのにかかった時間は 2 分。
うーん、劇的に早くはなったが、まだストレス・フルな時間だなぁ。
Ruby で書いている以上、過剰な最適化はむしろ負けなのでやりたくないのだが、 いろいろこのデータで実験して遊びたいのに、その都度 2 分のオーバー・ヘッドは ありがたくない。
読み込みループは、こんな感じ。
db = Array.new file.each_line do |s| db << SomeClass.new.unserialize(s) # SomeClass は仮の名前ですよ、念のため。 end
Serialize/unserialize とは Ruby らしくない という意見は当然おありでしょうが、それはまた別の話ということにしておいてください。
ここで、ループ回数は約 6,500 回。 unserialize メソッドは、そこそこ重いかもしれないんだが、 進捗を print させるコードを差し込んでループの進み方を見ていると、 数百回程度するする〜と進むかと思うと、しばらくだんまり、とか、そんな感じにみえた。
これは、あれだな、push (<<) にともなって配列を拡張しなければならないときに、 realloc が発生して、そこが足踏に見えているんですな、きっと。
利用者(僕)は、データベースのサイズを知っているんだから、 最初の Array.new のところで
db = Array.new(hint_db_size)
してやればいいわけですね (もちろん、それに伴ってループ内部のコードも変わります)。
GC がお出ましになるのを抑止するためには、Array.new のときに SomeClass の方も new してしまった方がいいかも知れません。 今夜、両方やってみよう。
さて、2 分がどれくらいに縮まるでしょうか。
#プロファイラを使えという声が聞こえて来そうな気がス。
Array.new(size) してみましたが、べつに速くはならず、むしろ少し遅くなってしまった。
Array.new(size) した場合、要素の追加だったものが代入に変わる(下を参照)ので、 間接参照が増えてしまうのが不利なのかな、とか思ったり。
db = Array.new の場合 : ループ内は db << SomeClass.new.unserialize(s) db = Array.new(size) : db[i] = SomeClass.new.unserialize(s) db = Array.new(size){SomeClass.new} : db[i].unserialize(s) db = Array.new(size); db.clear : db << SomeClass.new.unserialize(s)
いろいろ試してみましたが、測定誤差に埋もれてしまいそうな差しか出ませんでした。だいたい 1 分 50 秒前後。 file.new.each_line より file.new.read.each_line (一旦全部読んでしまう)とかも 試してみたんですが。
ううむ。あてがはずれた。
と、いうわけで、観念して(?) Marshal を利用してみました。 なにをかくそう、Ruby では serialize のことを marshal と呼んでいるらしいということは 最近知った。で、Marshal.load いっぱつ (db = Marshal.load(dumpfile)) にしてみたところ、所用時間は 1 分となりました。
自前 serialize/unserialize は、やめときましょう。遅いし、拡張性ないし。 もともと自前で用意したのは、Marshal のことをよく知らなかったからというだけだったし。
ちなみに、自前 serialize の吐くファイルにくらべ、Marshal.dump ファイルは倍くらいのサイズでした。 自前版では型とかいろいろ決め打ちで出し入れできるのに比べ、Marshal は汎用なので、 クラス名に相当する情報とかいろいろ入ってますからね。
a = Array.new よりも a = [] の方が、ちょっとだけ速かった。 百万回やって、1 秒が 0.9 秒になるくらい。
C++(ぼそ
いや、必要にせまられれば (C++ ではなく) C 言語にします。<br><br>なぜなら、僕の脳内用語集には次のようなエントリがあってですね。<br><br> Ruby 以前: 「C++ もありだな、っていうか仕方ないよね」と思っていた時期のこと