Hackarade: MRI Internal Challenge

今年1月に入社した技術部の笹田です。Ruby インタプリタの開発をしています。

少し旧聞になりますが、今年3月の終わりに Hackarade: MRI Internal Challenge という、Ruby インタプリタ(MRI, Matz Ruby Interpreter)をハックするという社内ハッカソン企画を行いましたので、その様子をご紹介します。ハッカソンでは、弊社エンジニアが原則全員参加で Ruby インタプリタをいじりました。今回のハッカソンでは、特別ゲストとして 世界ナンバーワンの Ruby コミッタ(コミット数が世界一)である 中田さん(nobu) に参加していただき、様々な助言をいただきました。

Hackarade って?

f:id:koichi-sasada:20170510112942j:plain

クックパッド社内で、技術力の底上げを目的に、エンジニア全員が1日参加するハッカソンを年に3回程度行っていくことになりました。このハッカソンの名前を、Hack + Parade からとって、 Hackarade と名付けました。

MRI Internal Challenge

f:id:koichi-sasada:20170510112328j:plain

最初の Hackarade の企画は、MRI Internal Challenge と題して、Ruby の中身を触るハッカソンになりました。MRI とは、Matz Ruby Interpreter の略で、いわゆる ruby コマンドです。クックパッドでは Ruby を用いて開発しているので、自分たちが使っているものへの理解を深める狙いもあります。

この企画に決まった直接の理由は、MRIの開発を主務とする私が入社したためです。そして、クックパッドには、私以外にも青木さんや福森さん(sora_h)といった MRI のコミッタがいるので、彼らのサポートを得ることができるというのも理由の一つでした。

開催にあたり、次のような目標をたてました。

  • 参加者全員
    • MRI は普通の人間が開発している、ということを思い出すことができる
    • MRI のビルドを各自行うことができるようになる
    • MRI のチケットの読み方に関する文化を知る
    • MRI のチケットを起こすことができる
    • MRI のソースコードのディレクトリ・ソースコードの構造を知る
  • 希望者
    • MRI の改善チケットを実際に起票する
    • MRI の改善チケットを解決するパッチを作成し提案する
    • MRI の中身に興味をもち、後日 開催する MRI Internal workshop に参加する

最初の「MRI は普通の人間が開発している、ということを思い出すことができる」というのは、実際に MRI をいじることで、Rubyは所与のものであるわけではなく、自分たちで変更可能である、ということを思い出して欲しいという気持ちから最初の目標としました。 MRI はオープンソースソフトウェアですから、改善したければ好きなだけ改善できます。知らないとちょっとハードルが高い気もしますが、わかってしまえば意外と簡単です。このハッカソンでは、そのハードルを下げるお手伝いをしたいと思って臨みました。

そして、ハッカソンで開発した成果は、可能なら MRI 開発コミュニティに提案し、貢献することを目標としました。

一日の流れ

プログラムは次のような流れでした。なお、時間はあまりオンタイムには進みませんでした。

  • 10:00-10:10 オープニング
  • 10:10-10:40 (1) MRI 文化の紹介
  • 10:40-11:20 (2) Ruby のソースコード構造の紹介
  • 11:20-11:30 休憩
  • 11:30-12:30 (3) The Edge of MRI internal
  • 12:30-13:30 お昼休憩
  • 13:30-14:30 (4) ハックのアイデアの紹介
  • 14:30-19:00 (5) 各自チャレンジ
  • 19:00- パーティー & 成果の発表

(1)~(4) までが座学で、(5) が実際に自分の好きなテーマで開発するハッカソンでした。

最後は、打ち上げパーティーであり、弊社キッチンで美味しいご飯を食べながら、参加者それぞれの成果を称え合いました。

座学の紹介

f:id:koichi-sasada:20170510112331j:plain

座学の内容を掻い摘んでご紹介します。座学といいますが、学校の教室のように説明を聞いてもらうよりは、資料を片手に手元のコンピューターをいじりながら、自分のペースで進めてもらうというものでした。弊社には日本語ネイティブでないエンジニアも多数在籍しているため、資料は英語で作成しました(その場での説明は日本語でした)。

時間も短いので、ハッカソンに必要になるビルドの方法や、他の資料の紹介が主になりました。

(1) MRI 文化の紹介

ここでは、開発者がどのような手順で開発をしているのか、意思決定はどのように行われているか、Ruby で用いている Redmine のチケットはどのように書くとよいか、それから、MRIの情報はどのように取得すればよいか、などを紹介しました。

チケットの書き方は、Ruby バグレポートガイドライン の内容を紹介しました。また、開発方法も、開発者の手引き を主に紹介しました。

情報の取得については、青木さんの『Rubyソースコード完全解説』サポートページRubyのしくみ Ruby Under a Microscope の紹介を行いました。その他、まつもとゆきひろさんの Twitter アカウント の紹介や、Ruby 関連イベントやワークショップなどオフラインコミュニケーションを紹介しました。また、社内に 3 人も MRI コミッタがいるので、気兼ねなく聞いてください、という話もしました(例えば、弊社 Slack には #ruby チャンネルがあります)。

細かいルール的な話が多くなりがちでしたが、ここで強調したのは、「ハッキングが全てであり、良い成果を出せば多少の形式は不問とされる」ということでした。たいてい、説得力のある成果があればなんとかなります(回りがサポートします)。

(2) Ruby のソースコード構造の紹介

実際にソースコードをいじるために必要になる、Ruby のソースツリーの大まかな概要をお伝えました。例えば、ここはインタプリタのコア(VMやGCのなど)を記述してあり、ここは ArrayString といった組み込みクラスの定義である、ここはテストが収まっている、などです。MRI は、自身のソースコードを生成しながらビルドを進めていくので、そのあたりの概略もお伝えしました。

ソースコードを説明するにあたり、ソースコードをチェックアウトしてもらって、実際に make コマンドを用いてビルドを行ってもらいました。また、C 言語で新しいメソッドを定義するといった、実際に MRI の中身を改造するような演習を行いました。

(3) The Edge of MRI internal

この時間は、私が行ってきた MRI のハックを紹介し、何が楽しくてこのようなことをやっているのか、ということを紹介しました。また、MRI にどのような課題があるか、という紹介をしました。

自分の行った改善が、世界中の利用者の方に影響をあたえるというのはやり甲斐を感じますよね。

(4) ハックのアイデアの紹介

何もない状態で MRI をハックしてください、というのは大変だと思うので、いつかやりたい、と思っていた(比較的軽い)アイデアを提案したり、チケットを紹介しました。

私はあまり残りチケットをまとめていなかったのですが(資料として、未アサインのチケット = 放置チケットへのリンクを提示するのみだった)、青木さんがその場でやりやすそうなチケットをピックアップしてくれました。

ハッカソンの紹介

f:id:koichi-sasada:20170510112744j:plain

ハッカソンは、最終的に40個ほどのアイデアに挑戦し、10個ほどのパッチの投稿という貢献を行いました。いくつかのハックはまだ継続して行われています。

ハックはグループで行っても良いですし、単独で行ってもよい、ということにしました。結果としては、ほとんどの人が単独で挑戦していました。

ここでは、実際にどのようなハックが行われたかをいくつかご紹介します。ここにある以外にも、挙動やテストの修正や、ドキュメントの追加などが行われました。

More flexible GC parameters

環境変数で GC の挙動を変更するためのパラメータを与えられるのですが、実行時に変更することはできません。しかし、原理的には変更可能であるため、変更できるようにする、という話になります。この話の難しいところは、「本当に変更しても大丈夫なの?」というところで、それを確認するのが面倒で手をつけていなかったのですが、今回のハッカソンで提案してもらったところ、興味を持った方に挑戦してもうことができました。

実装して、チケットとして登録するまでやり遂げることができました。 https://bugs.ruby-lang.org/issues/13388

弊社アプリケーションのような、実際のアプリケーションに応用して成果が出るかどうか、確認して取り込みたいと思っています(取り組んでくれた方々に依頼中です)。

再代入不可なローカル変数をつくってみる

Rubyのローカル変数はいつでも代入可能ですが、新しい文法を導入し、代入不可能なローカル変数(?)を作ろうというものです。 最近流行りのテーマですね。foo := expr とすると、foo への再代入を禁止する、という意欲的な挑戦でした。

とりあえず、foo := expr を受理するパーサを作成し、限定的な場面で再代入を禁止する機能を作成することはできたようですが、すべてのローカル変数への代入を網羅するところまでは至らなかったので、パッチはお蔵入りにしたとのこと。取り組んだ方々は、「パーサが理解できてよかった」という感想をもったそうです。

sprintfの精度のバグを直してみる

[Bug #8916 rb_sprintf への精度指定が正しく機能していない] という報告に対して、数人の方々が取り組みました。C のソースコードをかき分け、問題を追っていって、ある程度目星がついたところで、最終的には nobu がさっと直していった、という結末になりました。

感想を伺ったところ、「仕様の理解に時間がかかりすぎてつらい気持ちになったのに、しゅっと直っていて知識不足を痛感しました」とのことでした。半日しかない中ではしょうがないところだと思います。

Net::FTP で明示的に PORT コマンドを送れるようにする

Net::FTP が明示的に PORT コマンドを送れない関係で、NAT を挟んで active mode で通信できないケースがある、という問題に対するパッチの投稿を行いました https://bugs.ruby-lang.org/issues/13382。社内では渋いテーマだと評判でした。

チケットでは、「そもそも要るのか?」というコメントから議論がはじまりましたが、必要性を示すことで理解を得て、現在は最終的な実装を行っているところです。

Process.uid= validation and casting

[Feature #12410] Process.uid= validation and casting で報告されたバグ報告に対する修正を試みました。

バリデーションを入れたパッチを提案しましたが、チケット上での議論で現在の挙動が適当である、という結論が出たため、リジェクトされることになりました。

Module#source_location

Module#source_location という、モジュールが定義された場所を返すメソッドを実装し、提案しましたhttps://bugs.ruby-lang.org/issues/13383。すでに、Method#source_location というメソッドがありますが、その Module 版ということになります。

チケットでは、モジュール定義の追加が行われたとき(つまり、リオープンされたとき)、どうするか、といった問題や、実装に関する意見が出ています。便利な機能だと思うので、ぜひ Ruby 2.5 で導入されてほしいですね。

おわりに

f:id:koichi-sasada:20170510112632j:plain

このような形で、Hackarade: MRI Internal Challenge は無事に終了することができました。MRI のコア部分は C 言語で記述されており、最初はどうなることかと思っていたのですが、予想以上に皆さん楽しそうにハックを進めてくれており、一安心でした。半日のハックなので、最初は大きな成果が出なくて当然なのですが、これを機に興味を持ってくれる人が増えると嬉しいと思っています。

最後に、開催にあたっての反省点です。ハッカソン形式なので、課題をどのように準備するかがキモだと思います。今回は「未着手のチケットの中から自由に課題を選んでもらおう」と思っていたのですが、膨大なチケットの中から着手可能な難易度の課題を見つけることができずに、ハックの時間を十分にとれない参加者がそこそこ居ました。初めて見るチケット群では、目星もつきづらいですよね。事前にもう少し精査してチャレンジしやすいものを用意しておけばよかったと思います。

次回の Hackarade は、エンジニアみんなで機械学習を使おう、という企画をしているとのことで、今から楽しみです。

というわけで、本記事ではクックパッド社内で行われた、エンジニア全員参加のハッカソンイベント Hackarade の、1回目、MRI Internal Challenge をご紹介しました。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/