SparkR 1.5 の開発でできたこと・できなかったこと

spark-logo

こんにちは,アドバンストテクノロジーラボの石川有です. 業務として携わっている Apache Spark™ の最新版 Apache Spark 1.5 が先日リリースされました. 今回の記事では Spark のコンポーネントであるSparkR の 1.5 リリースまでの開発でできたこと・できなかったことを書きたいと思います. なにがサポートされたとか,どういう使い方ができるようになったなどの情報はほかでも入手できると思うので,本稿では自分が携わった SparkR の開発について書きます また Spark や Spark の DataFrame がなんであるかなどは,割愛しますのでご留意ください.

Spark 1.5 の開発の中で,SparkR で関わった部分は大きく分けると2つあります.
1. コーディング規約の策定と静的解析ツールの適応
2. DataFrame まわりの整備

コーディング規約の策定と静的解析ツールの適応

Spark 1.4 でマージされた SparkR のソースを読んでみると,パッと見た感じで未熟な印象を受けました. その理由のひとつが,コーディング規約が未策定だったこと静的解析ツールによる自動テストが未対応だったことです.

まず Spark 開発は基本的に Github 上で Pull Request (以下 PR)ベースで行われています. ある人が PR を送ると,公式の Jenkins で自動テストが実行されます. その実行には単体テストだけでなく静的解析も含まれ,Scala と Python については,scalastylePEP8 がそれぞれ実行される仕組みになています. このような仕組が 1.4 のリリースで SparkR は対応できていませんでした.

Spark 1.5 開発でできたことのひとつが,SparkR のコーディング規約の策定と静的解析ツールの適応です. 最終的に SparkR 開発のコーディング規約は,基本的に Advanced R のコーディング規約 に準拠することになりました. SparkR のコミッタと議論した中でもうひとつ候補となっていたのは,Google’s R Style Guide です. Advanced R の規約を踏襲した理由は,Advanced R に準拠した静的解析ツール jimhester/lintr の使い勝手が良さそうだという理由でした.

コーディング規約についてはコミッタと同意がとれたので,実際に静的解析ツールを適応したことについて書きます. 静的解析ツールを適応するに向けては,いくつかの作業が必要でした.

  • lintr に不足している機能を lintr に要望& PR を送る
  • SparkR の改修
  • 公式 Jenkins で実行するためのスクリプト作成
  • 公式 Jenkins の設定変更依頼
  • lintr コミュニティと Spark コミュニティの調整

なにかが良さそうだと思ってうまくいかないのは世の常で,採用を決めた lintr も SparkR に対して完全に要望を満たすものではありませんでした. そこで足りない機能で自分ができそうなものについては PR を送り,難しそうなものについては lintr の作者に要望を出すことで改善しました. 作者の方は非常にいい人で,迅速に我々の提案に対してコードの改修を行ってくれました.

規約に対して満足するライブラリを整備したあとは,静的解析を実行するスクリプトを書いて公式 Jenkins で動作するようにするための設定をする必要がありました. CRAN 上に存在する lintr は古いバージョンで今後もアップデートする予定がないので,最新版を使うためには GitHub から直接 clone する必要がありました. Jenkins 管理者とは,lintr の導入やそれにまつわる設定変更の調整をしました.

Spark 1.5 開発でできなかったことは,lintr での設定項目がまだ完全に満足行くものになっていないことです. まだ静的解析でチェックしきれていない規約があるので,1.6 開発で対応していきたいと思います.

DataFrame まわりの整備

Spark には DataFrame という API があります. そしてその API はコアの Scala だけでなく,もれなく Python, Java そして R でもサポートしていきます. 異なる言語間でユーザの使い勝手を可能な限り統一するのは,非常に困難です. たとえば SQL でいうところの IN に相当する関数をつくろうとすると,Python では in が予約語なので使えないとか…. そこで SparkR の DataFrame まわりの整備で 1.5 開発でできたこと・できなかったことについて書きます.

Spark 1.5 開発の中でできたことは,SparkR の DataFrame 用関数を 100 以上サポートできたことです. Spark の DataFrame に関する minmax などの関数は,Spark 1.5 で 100 以上追加されました. そのすべての関数を基本的に Scala 以外の言語にも移植する必要があります.

たとえば Python の場合,クラスのメソッドに __doc__というアトリビュートがあります.__doc__ アトリビュートを利用すると動的にメソッドを定義した場合でもドキュメントを差し込めます. その仕組みを利用して「そこそこ効率的に」Python に移植することはできたようです.

しかし,R には動的に関数やメソッドを定義する仕組みはあっても,それに対してドキュメントも同時に差し込むということができません. つまりドキュメントを整備するために,煩悩を数えるがごとく Scala に実装されている関数を R に移植しました. Scala のリフレクションの機能などを使ってある程度は半自動的に関数定義を起こせるのですが,R のオブジェクト指向の機能がイケてないので最後は 目grep で確認しながら手作業でした. またドキュメントを生成するためのパッケージ roxygen2 の作者にも助けていただきながら,ドキュメントの整備もこなすことが出来ました.

逆に Spark 1.5 開発の中でできなかったことは,すべての DataFrame 向け関数を移植できなかったことです. ウィンドウ関数や複雑な型を扱うための仕組みが対応できていません. その理由は,Scala と R のタイプコンバージョンに改善の余地があるからです.

コアの部分は Scala なので,それを単純に R から Scala の結果を取り出すことはできません. そのためバイナリレベルで Scala と R のデータのやりとりと型変換を行う機構を作っています. ここを改善しないとウィンドウ関数や複雑な型の変換を行うことができません. これは Spark 1.6 でリリースできるように頑張りたいと思います.

雑感

SparkR に限らず,Apache Spark は非常に多くの外部ライブラリに依存しています. 今回の記事で上げた R の静的解析ツール lintr やドキュメント用パッケージ roxygen2 などもそのうちのひとつです. 外部ライブラリの作者たちに質問やアドバイスを求めると,快く応じてくれたのも本当に助かりました. 単純に PR を送るだけでなく,外部ライブラリとの調整を行いながら貢献するスタイルを経験できたのは非常に良かったです.