セキュリティ&プログラミングキャラバン東京2009

午前は寝坊でほとんど聞けず。OS30分ライブも面白かったけど、あれは観ながら盛上がるもの。というか「30日でできる! OS自作入門」は買ったのに積読になっていますごめんなさいごめんなさい。

その後にあった、RubyをネタにしたVMGCの話がすごい面白かったのでメモを残しておく。講師はYARVのささだこういちさん。内容が間違っていたら、残念ながらそれは私が原因だ。

Ruby処理

VMの話。VMのデモを通じて、もっとも原始的なVMが腑に落ちた。

  • 処理系は何をしているか。「プログラムを読む」「読んだプログラムを実行する」。これだけ。あまり難しく考えてはいけない。
  • もう少しいうと、プログラムを読む部分はコンパイラという名前がついており、「パーサ」機能と「コンパイラ」機能が含まれる。実行する部分は評価器という名前がついており、「バイトコードの実行」「オブジェクト管理(GCとか)」「組込クラス・メソッド」の機能を持つ。
  • VMにはStack MachineとRegister Machineの2種類がある。YARVはStack Machine。
  • 仮想マシンについて。なぜ作るかといえば、CPU独立、マシン依存部分を吸収して移植性を含めた開発効率を上げるため。開発効率は実行速度とトレードオフになる。両方が最高に優れたVMは作れないので、どちらをどれだけ優先するかのバランスが大事。お客さんの意見によってバランスは変わる。Rubyでは、まつもとゆきひろさんがRubyを作るときにPeal置換を目指したため、文字列処理に重きを置いてきて、それだけならVMはなくてもよかったが、利用場面が広がってVMが必要になってきた。
  • バランスの例としては、MacRubyMacに特化した環境でのみ動作すればいいので、Mac独自の機能をたくさん使っている。64bitマシン独自の機能をつかっているので、「それじゃ32bit環境では動かない」とMacRubyのメンテナに言ったら「OS Xは64bitだからそれで問題ない」と言われた。ある意味羨ましい。
  • 仮想マシンの命令セットについて。Javaや.NETはStack MachineのVMYARVを作るときになぜStack Machineを選んだかというと、Javaや.NETなどで参考資料が充実していたから。それぞれのVMの特徴は
    • Stack Machine:命令セットが少ないのでコンパイル後のサイズが小さくなる。ネットワークでやり取りしたり、組込機器に載せたりするときに有利になることを狙ってJava VMはStack Machineを選択したのではないか。
    • Register Machine:命令セットが多くなるが、その分簡単に書ける。こちらのほうがコンパイル後のjumpが少なくなり、実行時のオーバーヘッドを減らせる。jumpの多寡は処理速度への影響が大きい。
  • Rubyで書いたStack型のVMと、Register型のVMの実行例をデモ。Stack型はpushとpopにいくつかの命令を追加しただけで、それをwhileしているだけ。Register型はそこがもう少し複雑になるが、whileしているのは同じ。ちょっとしたVMでも命令セット数が倍以上違う。
  • VMといってもやっていることは単純。ただしその単純なことを積重ねていくと出来上がったものは大分変わってくるので、お客さんの要望によって選択は慎重に行なう必要がある。

お勧めの本は「コンパイラとバーチャルマシン」だそうです。とりあえず帰りに紀伊国屋書店で買いました。積読にならないように気をつけないといけない。

Rubyメモリ管理

情報処理学会で発表したもの。GC(ガーベージコレクション))の知識があることが前提なので知らない人は諦めて、といいつつGCの説明もはさんでくれる親切設計。私はたまたま「GCアルゴリズム詳細解説」というサイトで勉強したことがあったので、ある程度ついていけた。以下ではGCの説明は省略するので、Mark&Sweepの図くらいは眺めておくと、メモが読みやすいかも。

  • RubyインタプリタC言語で実装されており、VMGCは保守的Mark&Sweep。最近はRubyの利用用途が広がって処理速度の向上要求が強いので、Rubyを1.9.1から1.9.2にバージョンアップするときにGCも改良して速度向上するように実装中。
  • RubyGCRubyオブジェクトは5wordの固定長であらわされている(文字列などはmalloc()/free()で管理)。これらオブジェクトは、RootオブジェクトはStackに積まれるが、そこから派生するオブジェクトはHeapに確保され、Heap-Slotsで辿れるようになっている。
  • 勝手に実装していいなら苦労はない。すでに多数利用されているRubyのメモリ管理を変更するにはいくつかの制限がある。
    • ソースレベル、バイナリレベル両方での互換性厳守。メジャーバージョンアップのときには互換性を破ってもいい(ただし適合しないプログラムはコンパイルエラーとなるようにする)が、今回はマイナーバージョンアップなので、互換性を破れない。GCだけの問題に見えるが、GCは処理系全体に影響が及ぶので慎重にならないといけない。以前、GCを取りかえ可能にするという案が提案されたことがあって互換性について心配していたが、却下されていた。
    • オブジェクトの移動ができない。つまりコンパクションやコピーを伴うGCを採用できない。
    • ライトバリアもできない。なので世代別GCやインクリメンタルGCを採用できない。
  • GC改善の取っ掛かり。最初はGCのオーバーヘッドが何によって影響されるかを考えた。Heapサイズを大きくすればトータルのGC実行時間は下げられる(当たり前)。ただしHeapサイズを単に大きくすると、他のアプリのHeapメモリが足りなくなる、オブジェクトフラグメンテーションが発生する(コンパクションは禁止)、メモリアクセスの局所性が失われる、といった問題が発生する。
  • 他のアプリのHeapメモリ対策。他プロセスと協調してヒープサイズを自動調整することを考えた。適切なヒープサイズを確認する方法としてOSに訊く方法があるが、いろいろあってそれが本当に適切なサイズなのかはわからない。なのでRubyに対してメモリのページアウトが発生するのを監視することで確保しすぎと判断することにしてみた。
  • フラグメンテーション対策。Heap-Slotsの個々のSlotサイズ(?)をそれまでの16KBから4KBにした。またオブジェクトの寿命を予測して、短寿命オブジェクトには広いHeapを用意することを考えた。ただしこれにはオブジェクトの寿命予測が大事。
  • Sweepの実行時間削減。スキップされた。
  • それらをどうやって実装したか。Heap-Slotsの管理方法にArenaを導入(ちなみにこの実装はセキュリティ&プログラミングキャンプ中に参加者が実装した)。最初に128KBをHeapからまとめて予約して、そこから必要に応じて4KBずつ確保するようにした。Heapの予約は全部のOSに対応しているわけではないが、ほとんどのOSで対応しているのでよしとした。が、おもったよりもOS側でのオーバーヘッドが大きくて効果は微妙。
  • マークカウンタも実装。GC実行時に、Heap-SlotsのSlot(?)ごとにマークしたオブジェクトの数を数えて、カウンタがゼロのSlotでは一括開放を行なうようにした。これは結構効果があった。
  • あと何とかかんとか。実装完了までにあと何箇所か未完成の箇所が残っている。言語系で処理を速くするためには、OSやプロセスの知識も必要、云々。

それ以外

ライトニングトークは省略。最後のフリーディスカッションでひとつだけ妙に記憶に残ったやり取り。確か「使える技術者とはどのようなものか、そういう技術者になるにはどのような勉強をすればいいか」という質問で、講師のひとりが「IT以外で一生懸命勉強しておくと、それはITを考えるときにも役立ちます」という回答をしていた。

あと、これがキャンプもキャラバンも無料というのは素晴らしいのですが、事業仕分けの影響で、セキュリティ&プログラミングキャンプ自体が来年も実施されるかどうか見通しがまだ立っていないみたいです。幸いにして仕分けの網をくぐることができて来年も実施されるようなら、応募資格のある人は全力で応募しましょう。「何でもやります」「やったことがないけど興味があります」という人より「こういうことをやってきました」「こういうことがやりたいです」という人を採用するとのことです。私はすでに応募資格はないです。というか参加者はみんな若かったな。講師をのぞけば、自分はたぶん上位5%くらいの年齢だったんじゃないだろうか。

このキャンプやキャラバンの中心人物であろう人に感謝の念をこめて、トラックバック