2012年2月12日日曜日

RからCのコードを呼び出すと

このエントリーをはてなブックマークに追加
Clip to Evernote
Pocket

ちょっと速いRのコードの書き方」へのコメントで、「大して速くもならないコード高速化に時間を費やす、という本末転倒に到れば立派なR使い」と言うのがあったので、Rから外部コードを呼び出して、どの程度の高速化が可能かを確認してみよう。

まず参考文献だが「R の拡張を書く」の「4 システムと他言語間のインタフェイス」の部分とRinternals.hファイルが頼りだ。ヘッダーファイルを見ないといけないのは、網羅的な型などの説明が無いためだ。今回は参照しなかったが、公開されているパッケージの中身も参考になると思う。そこそこ日本語の解説もあるようだ(RjpWiki)。Fortranやextern C {...}を使ってC++でも記述ができるが、今回はCを用いる。

作成手順はJavaのJNIやPerlのXS言語と大差は無く、むしろ気軽に記述できる。データ受け渡しの規約に従いDLLを作成し、それをRのソースコードから.C()/.Call()/.External()で呼び出すだけだ。Rは動的型付でCは静的型付なので、型変換に気をつける必要はあるが、大した問題では無い。呼び出し方法が3種類あるのは、データ連携レベルが異なるためだ。.Call()/.External()では、Rオブジェクトを作成して戻り値とする事ができ、特に.External()は可変長引数を取れる。C側からRの変数にアクセスしたり、表現式を評価したり、ビルトイン関数等を呼び出す事もできるので、適用範囲は広い。

外部言語との連携方法の詳細はリファレンスを参照してもらうとして、ベンチマークの説明を行いたい。今回は最小公倍数を計算するコードで、RとCの比較をしている。ソースコードはMercurialのリポジトリで公開しているが、要素が32767あるベクターx、yを引数に取り最小公倍数を返す関数をRとCでそれぞれ記述し、経過時間を計測している。内部的にループ演算で最大公約数を計算するルーチンがあり、Rで実行するのは不利な演算だ。

実行結果はRが1.193秒、Cが0.005秒の経過時間がかかり、その速度差は238.6倍となった。R側のコードは行列化してapply関数を用いるなどで、さらに高速化は可能な見込みだが、その差は100倍を切る事は無いであろう。RはCに対して圧倒的に遅い。その主な理由はリスト構造を解釈・実行する関数型プログラミング言語であるため、将来的にもこの差が大きく縮まることは無いであろう。

プログラマ的にはCと連携しだしてからがパフォーマンス・チューニングと言う感じであるが、費用対効果が大きいケースは稀であろう。そういう意味では、ちょっと速いコードが書ければ十分で、RとCを混ぜて使おうと言うのはユーザー・レベルでは最終手段に近い発想だとは思う。ましてやモテ要素になるわけでもなく、腹筋や腕立て伏せでもしている方が建設的だ。

0 コメント:

コメントを投稿