昨年は Developer Day の一部として ハッカソン(Hackathon:Hack と Marathon を合成した造語、昨年はコードラボと記していました)を開催しましたが今年は Google Developer Day 2009 Japan 開催の直後、6 月 10 日と 6 月 11 日に開催いたします。これは、昨年の参加者やサポーターの方からの「ハッカソン に参加すると他のセッションに参加できないのが残念」というご要望に応えたためです。

ハッカソン 開催日程
テーマ: Geo
 ハッカソン: 6 月 10 日(水)/富士ソフト アキバプラザ
 事前ミーティング: 6 月 1 日(月)/Google オフィス(渋谷)

テーマ: Android
 ハッカソン: 6月 10 日(水)/富士ソフト アキバプラザ
 事前ミーティング: 6 月 2 日(火)/Google オフィス(渋谷)

テーマ:OpenSocial
 ハッカソン: 6 月 11 日(木)/富士ソフト アキバプラザ
 事前ミーティング: 6 月 3 日(水)/Google オフィス(渋谷)

テーマ: Google App Engine
 ハッカソン: 6 月 11 日(木)/富士ソフト アキバプラザ
 事前ミーティング: 6 月 4 日(木)/Google オフィス(渋谷)
ハッカソンはいずれの日も 10 時から 18 時、事前ミーティングはいずれも 19 時から 21 時となっています。

ハッカソンへの応募は以下で受け付けています。ハッカソン には Google Developer Day のために来日した米国の Google エンジニアも参加しますので、奮ってご参加ください。
Google Developer Day 2009 Japan ハッカソン 応募受付

アイデアソン のご紹介

さて、ハッカソン に参加される方には、ハッカソン のおよそ 1 週間前におよそ 2 時間開催される「事前ミーティング」への参加も強くお勧めしています。毎回異なるメンバーが集まる ハッカソン にとって、事前ミーティングはとても重要なミーティングとなります。今回は「事前ミーティング」とそのプログラムの 1 つである「アイデアソン」の魅力と詳細について、2 月に京都で開催された OpenSocial ハッカソン の「事前ミーティング」を例にご紹介しましょう。「事前ミーティング」の詳細は ハッカソン を自ら開催されるデベロッパーの方にも大いに参考となるはずです。

事前ミーティングのアジェンダは主に 3 つに分けられます。
  • 諸連絡
    • ハッカソン を効率よく行うためのツールや環境設定の紹介
    • コードを共有するためのリポジトリサーバの説明
    • Tips
    ハッカソン のテーマによって連絡すべきツールや環境設定は異なりますが、登録や設定に時間がかかるものについては事前ミーティングでの説明が必須となります。 OpenSocial は検証のために SNS の Sandbox の登録を必要としますが、その登録に 2 ~ 3 日かかる場合があります。そのため、ハッカソン 当日に登録ができていないと 1 日が無駄になってしまいかねません。こうした事態を回避するために、事前ミーティングでの連絡が必要となるのです。

    またグループで開発をするには、コードをシェアし、バージョン管理を行うためのリポジトリを用意し、その使い方を全員が理解しておく必要があります。リポジトリを用意すれば、ハッカソン の終了後も開発を続けることができます。前回の ハッカソン では code.google.com のプロジェクトホスティングを利用しました。このプロジェクトホスティングにはコードの共有以外にも利点があります。プロジェクトホスティングにアップロードしたファイルには固定URLでアクセスすることができるため、SNS などにアプリケーションを登録する際に、プロジェクトホスティングの URL を利用できるのです。

    時間があれば、ハッカソン 初心者のために Tips を紹介することも有効です。今回の ハッカソン では Tips に従い、ペンタブレットを持参している参加者もいました。 3D CG のゲームを作成するために大活躍していたようです。


  • グループ分け
    ハッカソン ではグループに分かれてコーディングを行います。グループ分けは ハッカソン 当日でも可能です。しかし、グループ分けとその後の自己紹介で時間を費やすため、これを避けるためには、事前ミーティングでグループ分けを行うことが望ましいでしょう(事前ミーティングに参加できなかった人は ハッカソン 当日にグループ分けを行うことになります)。なお、グループは 6 人以下が適切です。それ以上の人数ならば、グループを分割するべきでしょう。

    グループ分けのポイントは主催者側であらかじめグループを設定しておくことです。今回の ハッカソン では参加申し込み時に開発したいアプリケーションのアイデアを記入していただき、それを参考にグループを設定しました。

  • Ideathon
    アイデアソン は聞きなれない言葉でしょうが、ハッカソン と同じく Idea と Marathon を合わせた造語です。同じグループのメンバー同士が打ち解ける機会でもあり、事前ミーティングにあわせて 1 時間程度の アイデアソンを行います(通常、アイデアソン は ハッカソン と同様に1日を費やすことが多いのですが、事前ミーティングでは時間の都合上 1 時間としています)。

    アイデアソン ではグループごとに、ハッカソン のテーマとなった技術についてのブレインストーミングと発表を行います。単なるブレインストーミングとは異なり、発表のためにアイデアをまとめる必要があります。短時間でブレインストーミングを行い、その中で提示されたアイデアを評価および統合し、簡易で魅力的なプレゼン方法の検討が必要となります。これらを1時間で行うのですから、雰囲気としては短距離走に近い集中力が求められます。



    発表方法は特に決まっていません。アイデアをまとめるために用意したスケッチブックや模造紙を用いても良いですし、口頭での発表でもかまいません。今回、投票でもっとも優れたアイデアと評価されたグループは模造紙による発表でした。



    アイデアソン は密度の濃いコミュニケーションとなるため、自然とグループは打ち解けつつ、お互いの技術的力量を把握することもできます。また、グループで開発するアプリケーションは アイデアソン で発表したアイデアに沿ったものとなることが多いようです。この場合、ハッカソン 当日に何を開発するかを検討する時間が節約できることになりますし、ハッカソン 当日までにグループで連絡を取り合い ハッカソン 当日に向けて十分に準備することも可能となります。



このように「事前ミーティング」を行うことにより、ハッカソン 当日を限りなくコーディングだけに費やすことができるようになります。
Google Developer Day に併せて開催されるハッカソンに参加を希望される方は、ぜひ「事前ミーティング」への参加も検討いただけるようお願いします。


C や C++ でプログラムの書いたことのある多くの人は、「プログラムを高速化したいけれど、どこが一番のボトルネックか分からない」とか、「メモリリークがあるようだけれど、どこで発生しているか分からない」といった問題で苦しめられた経験が一度くらいはあると思います。もちろん、こうした問題の解決策として、アドホックにプログラムをチューニングしたり、目でプログラムを追ってメモリリークの発生箇所を突き詰めることも可能ですが、時間がかかってしまうことが多々ありますし、あまり楽しい作業でもないと思います。

そんなときに役に立つのが、Performance tools です。具体的には、このソフトウェアは以下の 4 つのツールで構成されています。
  • TCMalloc: マルチスレッドプログラムに適した、高速な malloc の実装
  • Heap Checker: メモリリークを検出するライブラリ
  • Heap Profiler: 各関数におけるメモリの使用量を計測・表示するツール
  • CPU Profiler: サンプリングによって各関数の実行時間を計測・表示するツール

これらのツールを使って、プログラムの高速化やバグの検出などを行なうことができます。例えば、Heap Profiler を使えば、プログラム中のどこでどれだけメモリが使用されているかを、簡単に把握することができます。下の図のような、ノードが各関数の中で確保されているメモリの量を、エッジが関数の呼び出し関係を表す有向グラフとして、メモリの使用状況を可視化することができます。



同様に、CPU Profiler を使えば以下のような重みつきコールグラフを出力することができます。このグラフを見ながらプログラム中のボトルネックを特定するといったこともできるでしょう。



Performance toolsの使い方

使い方は簡単です。基本的には、どのツールもTCMalloc のライブラリをリンクするだけで使用する事ができ、 ソースコードの変更は必要はありません。例えば gcc を使ってプログラムをコンパイルしている場合でしたら、"-ltcmalloc" をコンパイルオプションに付け加えるだけで、 TCMalloc の提供する malloc/free がプログラム中で呼ばれるようになります。例えば、

gcc [...] -ltcmalloc

とするだけです。また、Heap Profiler であれば、

gcc [...] -o myprogram -ltcmalloc
HEAPPROFILE=/tmp/profile ./myprogram


と実行することで、メモリの使用状況のログを取ることができます。この場合ですと、定期的に(例えば 1 GB メモリが割り当てられる度に)、/tmp 以下に profile.0001.heap、...、profile.0100.heapといったログファイルが生成されます。このログファイルを以下のように pprof に与えると、先に述べたようなグラフが表示されます。

pprof --gv myprogram profile.0100.heap

TCMalloc

以上に述べたツールの中で、ここでは特に、TCMalloc (Thread-Caching Malloc) について少し詳しく解説します。これは高速な malloc の実装で、例えば glibc 2.3 malloc (ptmalloc2) との比較で、ptmalloc2 で(サイズの小さなオブジェクトの)malloc/free に約 300 ナノ秒かかったところ、TCMalloc では約 50 ナノ秒しかかからなかったという実験結果があります(2.8 GHz の P4 上での測定)。

TCMalloc は、大きく言って 2 つの特長を持ちます。1 つは、マルチスレッドプログラムにおけるロックの競合を削減しているという点です。サイズの小さなオブジェクト (<= 32K) の malloc/free においては、ロックの競合はほとんど発生せず、大きなオブジェクトにおいても、細粒度で効率的な spin lock を用いるようにしています。もう 1 つの特長は、サイズの小さなオブジェクトをメモリ効率の良い方法で表現するということです。単純な malloc/free の実装だと、各オブジェクトごとに(例えば 4 バイトの)ヘッダを用意し、そこにオブジェクトのサイズなどの情報を格納しておく必要があります。これは、オブジェクト解放時に、そのオブジェクトのアドレスから解放すべきメモリサイズを計算するためです。それに対してTCMalloc では、例えば 8 バイトのオブジェクトを N 個確保するのに約 8N * 1.01 バイトしか必要としません。

それでは、TCMalloc がどう実装されているかについて、簡単に説明します。TCMalloc によって管理されるメモリは、各スレッドごとに用意された「スレッドキャッシュ」と、全スレッドによって共有される「中央ヒープ」から成ります。基本的には、サイズが 32K 以下のオブジェクトはスレッドキャッシュ上に確保され、それより大きなオブジェクトは中央ヒープに確保されます。スレッドキャッシュ上ではロックを必要とせずにオブジェクトを確保することができるため、高速化が図れます。



スレッドキャッシュは、各オブジェクトをそのサイズに応じて「クラス」に分類することによって、効率的なメモリ管理を実現しています。例えば、サイズが 1 バイトから 8 バイトのオブジェクトはクラス 0 に、サイズが 9 バイトから 16 バイトのオブジェクトはクラス 1 に分類されます。そして、このクラスごとに、空きオブジェクトを管理するための連結リストが用意されています。



例えば、サイズが 14 バイトのオブジェクトを確保する際には、クラス 1 の空きリストから先頭の要素を取り出して、それを使うようにします。また、オブジェクトを解放する際には、そのオブジェクトのアドレス(ページ番号)から、クラスを計算できるようにしておきます。こうすることで、各オブジェクトごとにヘッダを用意して、サイズを記憶しておく必要がなくなります。

まとめ

Google が公開している Performance tools、特に TCMalloc について説明しました。もし少しでも興味を持った方は、是非使ってみてください。より詳しい情報を知りたい方は、Wiki(英語)ソースコードなどを参照して下さい。


このように、 core ファイルは有用なのですが、いちいちデバッガを立ち上げるのは少々面倒です。そこで、 Google 社内では、プログラムがセグメンテーション違反などを起こしたときはスタックトレースを表示してから終了するように工夫しています。ちょうど、 Python や Java などのプログラムでエラー終了時にスタックトレースが表示されるのと同じ感じです。

このエラー終了時にスタックトレースを表示する機能は、オープンソースで公開している google-glog という C++ 用のログライブラリに含まれています。この機能は ELF というバイナリフォーマットを採用した Linux などの OS、または Mac OS X 10.5 以降で利用できます。

google::InstallFailureSignalHandler() という関数を呼ぶと、 SIGSEGV, SIGABRT といったシグナルに対して google-glog が提供するシグナルハンドラーがセットされます。プログラムがそれらのシグナルを受け取ると、次のようなスタックトレースを表示してから終了します。


*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
PC: @ 0x412eb1 TestWaitingLogSink::send()
@ 0x7f892fb417d0 (unknown)
@ 0x412eb1 TestWaitingLogSink::send()
@ 0x7f89304f7f06 google::LogMessage::SendToLog()
@ 0x7f89304f35af google::LogMessage::Flush()
@ 0x7f89304f3739 google::LogMessage::~LogMessage()
@ 0x408cf4 TestLogSinkWaitTillSent()
@ 0x4115de main
@ 0x7f892f7ef1c4 (unknown)
@ 0x4046f9 (unknown)


このスタックトレースを見るといろいろなことがわかります:
  • プログラムは TestWaitingLogSink::send() という関数の中の 0x412eb1 というアドレスでクラッシュしている (PC: の行を参照)。
    関数が大きい場合、関数の中のどの行でクラッシュしているか知りたいときがあります。デバッガを使わなくても、バイナリにデバッグ情報が残っていれば、GNU Binary Utilities の addr2line というツールを使えばアドレス (この場合は 0x412eb1) からファイル名と行番号を取得できます。この方法は core ファイルを紛失しているときに役立ちます。
  • この関数にいたるには main() → TestLogSinkWaitTillSent() → google::LogMessage::~LogMessage() → google::LogMessage::Flush() → google::LogMessage::SendToLog() → TestWaitingLogSink::send() という経路を辿っている。
  • 0x0 というアドレス (おそらくは NULL ポインタ) をアクセスしたことによって SIGSEGV が発生している (SIGSEGV (@0x0) を参照)。
  • エラーが発生したのは UNIX 時間で 1225095260、つまり Mon Oct 27 17:14:20 JST 2008 である。
  • シグナルはプロセスID 17711 が受け取った。スレッドID は 0x7f893090a6f0 である。
  • シグナルはプロセスID 0 から送られてきた (自分自身)。この情報は SIGTERM の場合など、他のプロセスからシグナルが送られてきているときに役立ちます。

これだけの情報がデバッガを立ち上げなくても得られるのはなかなか便利ではないかと思います。ただし、関数名はシンボル名が削られているバイナリでは表示されません。

スタックトレース表示の仕組み

上のようなスタックトレースを表示するために、 google-glog 内では次のような処理をシグナルハンドラの中で行っています。
  1. スタックトレースを取得する。
    この処理はアーキテクチャごとに異なります。x86 の場合はフレームポインタをたどることによってスタックトレースを取得できます。フレームポインタなしでコンパイルされた x86_64 のプログラムの場合は複雑な処理が必要なため、 google-glog では libunwind というライブラリに任せています。
  2. アドレスを関数名に変換する。
    ステップ 1 で取得したスタックトレースは生のアドレスのリストです。アドレスだけでは不便なので、アドレスを関数名に変換してやります。この処理はバイナリフォーマットに依存します。 google-glog では、 ELF というバイナリフォーマットの場合は自力でシンボルテーブルをスキャンして変換、 Mac OS X の場合はシステムライブラリが提供する dladdr() という関数を使って変換を行います。
  3. C++ の関数名を読みやすくする。
    C++ の関数名はバイナリの中では Foo::Foo() → _ZN3FooC1Ev のように変換された形で記録されています (この変換はマングリングと呼ばれます)。_ZN3FooC1Ev は読みにくいので Foo::Foo() に変換してやります (この変換はデマングリングと呼ばれます)。名前を変換する方式にはいろいろありますが、 google-glog では GCC バージョン 3 から使われている Itanium C++ ABI にのみ対応しています。


シグナルハンドラ内で処理するという性質上、できるだけすべての処理を async signal safe (たとえば malloc は使わない) に行うようにがんばっています。将来的にはステップ 4 として「アドレスからファイル名と行番号を取得する」を追加したいと考えています。

まとめ

C++ のプログラムのデバッグを楽にする方法として、 google-glog のスタックトレース表示機能を紹介しました。C++ のプログラムのデバッグは難しいケースも多いのですが、簡単なケースのデバッグはスタックトレース表示によって効率化できるのではないかと思います。

Google Summer of Code は、いくつかのオープンソース / フリーソフトウェアの開発プロジェクトに指導者(メンター)になってもらい、学生のみなさんに実践的なプログラミングを経験してもらおうという企画です。最終的に成果をあげたプロジェクトには Google から 5000 US ドル(学生に 4500 US ドル、メンター組織に 500 US ドル)が支払われます。
2005 年に始まったこの企画は、全世界から 2500 人ほどの学生と 250 人ほどのメンターが参加してきました。
今年も 400 もの組織から応募があり、150 の組織にメンターとして参加してもらうことになりました
学生の皆さんからの応募受付が本日から始まりました。それぞれのメンター組織がアイデアリストをあげているので、それを見ておもしろそうなプロジェクトにぜひ挑戦してください。

Google Summer of Code はプログラミングコンテストではないので、あたえられた課題をこなせるかどうかを問われているのではありません。
メンターや関連プロジェクトの開発者と一緒に、プロジェクトを完遂することが目標となります。成功しているプロジェクトは、チームで協力しあってソフトウェアの開発をすすめているものです。わからないからできないではなく、わからないところはメンターやプロジェクト開発者に聞いて目標を達成するというのが重要です。どのような目標を設定するかも、メンター組織の人と相談しつつ決めていくとよいでしょう。アイデアリストにはなくても、そのメンター組織に関連したすばらしいアイデアがあればぜひそれを提案していってください。今までの Google Summer of Code で成功したといわれる学生は自らプロジェクトアイデアを提案してきていたそうです。

質問などがあれば、Internet Relay Chat(IRC) [英語] メーリングリスト[英語] で聞いてみてください。英語はちょっと苦手という人は google-summer-of-code-discuss-jaメーリングリスト のほうを使ってみてください。

メンターの指導をうけながら意義のあるプロジェクトにかかわることで、ソフトウェアの世界で楽しい経験をしてもらい、すばらしいコードを作成して、ひいてはオープンソース / フリーソフトウェアの開発者、貢献者として育っていただくというのがこの企画のねらいです。
学生応募の締切は 4 月 3 日午前 4 時 ( 日本時間 ) となっています。ちょっとしたプログラムを書くことはできるけれど、実際のソフトウェアプロジェクトでどのようにしていくのかがわからないという学生の人は是非チャレンジしてみてください。たくさんのご応募をお待ちしています。

Happy Hacking!