Apache ArrowのPMC(Project Management Commitee、プロジェクト管理チームみたいな感じ)のメンバーの須藤です。
みなさんはApache Arrowを知っていますか?聞いたことがないとか名前は聞いたことがあるとかコンセプトは知っているあたりがほとんどで、触っている人はあまりいないのではないでしょうか。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。データ処理界隈に興味がある人は知っておくと役に立つはずなので2018年9月現在の最新情報を紹介します。
私は、PMCの中では唯一の日本人で、コミット数は3番目に多いので、日本ではApache Arrowのことをだいぶ知っている方なはずです。日本語でのApache Arrowの情報があまりないので日本語で紹介します。ちなみに、英語ではいろいろ情報があります。有用な情報源はApache Arrowの公式ブログや公式メーリングリストやそれぞれの開発者のブログ・発表などです。この記事ではそれらの情報へのリンクも示しながら最新情報を紹介するので、ぜひ英語の情報も活用してください。
Apache Arrowが実現すること
Apache Arrowが目指していることはメモリー上でのデータ処理を効率化することです。
「効率化」には2つの観点があります。1つは「速度」です。速いほど効率的ということです。もう1つは「実装コスト」です。実装コストが低いほど効率的ということです。
速度
まず「速度」の方から説明します。
Apache Arrowは大量のデータを処理するケースを想定しています。そのため、速度のことを考えるときも大量のデータを処理する前提で考えます。
大量のデータを処理するときにネックになる部分を解消し、高速化できる部分を最適化していくことで速く処理できるようになります。
Apache Arrowが注目したネックになる部分はデータ交換の部分です。
Apache Arrowが注目した高速化できる部分は並列処理の部分です。
順番に説明します。
データ交換
ここではネックになる部分の解消について説明します。対象となる処理はデータ交換処理です。
大量のデータを扱う時は複数のシステムが連携してデータを処理します。たとえば、データを集めるシステム・データを永続化するシステム・データを前処理するシステム・データを集計するシステムなどです。それぞれのシステム間でデータを交換する必要がありますが、そのときのデータのシリアライズ・デシリアライズ処理にかかるコストが結構かかるのです。たとえば、JSONでデータ交換するとした場合、データをJSONにフォーマットするコストとJSONをパースしてプログラムで扱える形式にするコストが結構かかるということです。
そこで、Apache Arrowは各種システムで共通で使えるシリアライズ・デシリアライズコストがすごく低いデータフォーマットの仕様(Apache Arrowフォーマット)を作っています。基本的なアイディアは「パースしない」です。プログラムから直接扱えるフォーマットでデータを配置することでパースせずにそのままデータを扱えます。これはデータをシリアライズするときも効率的です。プログラムで扱っているデータをそのまま書き出せばよいのでシリアライズコストがほとんどありません。なお、パースしないデータフォーマットの実現にはFlatBuffersを使っています。
Apache Arrowフォーマットで一番うまみがでるのは、各システムすべてがApache Arrowフォーマットに対応しているときです。Apache Arrowフォーマットに対応していないシステムがあるとそのシステムとのデータ交換時にApache Arrowフォーマットを使えないからです。つまり、そのシステムとのデータ交換コストが高くなります。
データ交換コストを下げることによる速度向上の実現にはApache Arrowフォーマットを広く使える状況になっていなければいけません。そのため、より多くの言語でApache Arrowフォーマットを扱えるように開発を進めています。現在Apache Arrowフォーマットを読み書きできる言語は次の通り(アルファベット順)です。
-
C
-
C++
-
Go
-
Java
-
JavaScript
-
Lua
-
MATLAB
-
Python
-
R
-
Ruby
-
Rust
完成度が高いのはC++とJavaです。C++のバインディングとして実装されているC、Lua、Python、Rubyも完成度が高いです。MATLABとRもC++のバインディングですがまだ開発が始まったばかりで完成度はそれほどではありません。JavaScriptとGoとRustはバインディングではなくその言語で1から実装しています。これらの言語の実装は、主な型(たとえば数値型)はサポートしているがまだサポートしていない型もある、という状況です。Juliaの実装も進んでいます。各種言語での実装状況の詳細は後述します。
並列処理
ここでは高速化できる部分の最適化について説明します。対象となる処理は並列処理です。
大量のデータを高速に処理するには次の両方またはどちらかを実現します。
-
1つのデータをより速く処理する
-
同時に複数のデータを処理する(並列処理)
Apache Arrowのデータフォーマットはできるだけデータを局所化する配置になっています。局所化するとCPUのキャッシュメモリーを活用しやすくなり「1つのデータをより速く処理する」ことにつながります。局所化した上でデータの境界を64bitに揃えるとSIMDを有効活用しやすくなります。SIMDを活用できると1回の命令で複数のデータを処理できるため「同時に複数のデータを処理する」ことにつながります。
Apache ArrowのデータはOLTP(OnLine Transaction Processing)(ECサイトで購買するような処理)よりもOLAP(OnLine Analytical Processing)(データから探索的に知見を探し出すような処理)で性能がでるように設計されています。OLTPとOLAPでは処理の内容が違うので、データの配置の仕方で実現しやすさが変わってきます。RDBMSを知っている人向けな説明をすると、OLTPでは行単位の処理(行の追加・削除など)が多くて、OLAPでは列単位の処理(COUNT
やGROUP BY
など)が多いです。OLTPでは行単位でデータが固まっていた方が性能がでやすく、OLAPでは列単位でデータが固まっていた方が性能がでやすいです。Apache ArrowはOLAP向けの設計なので列単位でデータをまとめています。このようなデータの持ち方を「カラムナー」と言います。
「速度」のまとめ
Apache Arrowのデータフォーマットは速く大量のデータを処理するために次のことをうまくできるような仕様になっています。
-
データ交換
-
並列処理
データ交換のためにシリアライズ・デシリアライズコストが小さくなるように設計されています。また、より多くの言語で読み書きできるようにライブラリーの整備を進めています。
並列処理のためにデータの配置方法を工夫しています。最近のCPU・GPUで高速に処理できる配置になっています。
実装コスト
Apache Arrowはメモリー上でのデータ処理を効率化することを目指しています。「効率化」のために「速度」と「実装コスト」に着目しています。ここまでで「速度」について説明したので次は「実装コスト」について説明します。
メモリー上でのデータ処理するソフトウェアはたくさんあります。速度が重要なソフトウェアも多いです。速度が必要なソフトウェアでは速くするためにいろいろな工夫を実装します。これにはそれなりの時間がかかります。
Apache Arrowは高速に大量のデータをメモリー上で処理するときに必要な機能をライブラリーとして提供しようとしています。Apache Arrowをインフラとして使うことで同じような工夫をしなくて済むようにしたい、多くのソフトウェアで重要な部分は協力してみんなでよいものを開発して共有したい、ということです。
最近では次のような処理をApache Arrowに取り込む動きがあります。
-
LLVMを利用してJITコンパイルしてデータ処理をより高速化する処理
-
Apache DrillのRowSet機能(行指向データをいい感じに扱う機能)
なんでもかんでも取り込むとメンテナンスしにくくなって開発が停滞する危険性もあるのですが、そのあたりはバランスを取りながら判断していくはずです。
Apache Arrowが実現することのまとめ
Apache Arrowは効率的に大量のデータをメモリー上で処理することを目指しています。そのためにしていることは次の通りです。
-
データ交換・高速処理しやすいApache Arrowフォーマットの仕様を定義
-
各種言語用のApache Arrowフォーマットを読み書きするライブラリーを開発
-
大量のメモリー上のデータを高速処理するためライブラリーを開発
Apache Arrowの位置付けの説明はこのくらいにして、どのような用途に向いているか、現状はどうなっているか、今後どうなっていくかについて説明します。
Apache Arrowが向いている用途
Apache Arrowはどんな用途にでも向いているわけではありません。得意なこと不得意なことがあります。用途にあわせて使い分けましょう。
Apache Arrowは複数のシステムが協調して大量のデータを処理するユースケース向けに設計されています。そのため、次の用途に向いています。
-
大量データの交換
-
メモリー上での大量データの分析処理
シリアライズ・デシリアライズコストの低いデータフォーマットな点が「大量データの交換」に向いています。
高速処理に向いたデータ配置・高速な処理をライブラリーで提供している点が「メモリー上での大量データの分析処理」に向いています。
もし、ファイルサイズが気にならないなら「処理結果の一時的なキャッシュ」にも有用です。ファイルサイズが気になるならApache Parquetフォーマットの方が向いています。
一方、永続化には向いていません。たとえば、ログをApache Arrowフォーマットで保存するというのは向いていませんし、データベースのデータをApache Arrowフォーマットで保存するというのにも向いていません。
永続化用のデータフォーマットではサイズが要重なポイントになるケースが多いです。サイズが小さい方がたくさんのデータを保存できますし、データを読み書きするときのI/Oが減るからです。Apache Arrowはシリアライズ・デシリアライズコストを低くすることを重視しているので、サイズを小さくすることに関してはそれほどがんばれません。たとえば、Zstandardなどで圧縮するとApache Arrowのメリットの一部が薄れてしまいます。そのメリットとは「ゼロコピー」です。
ゼロコピーはデータをコピーしないことです。Apache Arrowフォーマットではデータをコピーせずにそのまま扱えるように設計されています。具体的には次のように設計されています。
-
データをプログラムから直接効率よく扱える配置にしている
-
データは基本的にリードオンリーで扱う
プログラムから直接効率良く扱える配置にしてあるとそのままデータを扱えます。システムが提供するメモリーマップ機能を使えばゼロコピーでデータを扱えます。しかし、直接扱えるデータでも圧縮すると伸張しなければ使えません。つまり、「直接効率よく扱える配置」ではなくなってしまいます。そのため、新しい領域を用意して伸張したデータを格納する必要があります。コピーのような挙動です。大量データを処理する想定なのでこのような伸張するコストが無視できないことも多いです。ただ、ネットワーク越しにデータ交換する時のようにネットワーク上のデータ送受信コストの方が圧縮・伸張コストよりも高いことがあります。このような場合は圧縮した方が割に合うので、用途によって使い分けます。(まだ圧縮機能はサポートされていませんが、ZstandardやLZ4を扱うための部分は実装済みです。ARROW-300 [Format] Add buffer compression option to IPC file format参照。)
リードオンリーとゼロコピーについても説明します。データが変更される可能性があると、安全にデータを処理するためにはデータをコピーするか排他制御をして扱わないといけません。大量データを処理する想定なのでコピーするコストは無視できません。排他制御をすると並列度が下がりがちで速度が犠牲になります。そのため、Apache Arrowはリードオンリーでデータを扱う設計になっています。
このようにApache Arrowはゼロコピーを大事にした実装になっています。Apache Arrowではゼロコピーはよくでてくるキーワードなので覚えておいてください。
参考:
-
-
Apache Arrowフォーマットと他の類似のデータフォーマットの違いを説明した記事
-
タイトルは「すでに類似のデータフォーマットがあるのにApache Arrowは必要なの?」みたいな少し煽った感じになっているけど、結論は「Apache Arrowは必要だよ」
-
続編としてDBMS Musings: An analysis of the strengths and weaknesses of Apache Arrowがあるので↑が面白かった人は読んでみてもよいかも
-
-
Some comments to Daniel Abadi's blog about Apache Arrow - Wes McKinney
- Apache Arrowの開発者の1人による↑の記事への補足
Apache Arrowが向いている用途は次の用途だということを説明しました。
-
大量データの交換
-
メモリー上での大量データの分析処理
つづいて、現状はどうなっているか、今後どうなっていくかについて説明します。
Apache Arrowの現状
2018年9月現在、Apache Arrowのデータフォーマットの仕様と実装がどのようになっているかを説明します。
データフォーマットの仕様は今年中には固める方向で進んでいます。仕様が固まったらApache Arrow 1.0がリリースされる予定です。現時点での最新バージョンは0.10.0です。9月中に0.11.0がリリースされる予定です。
実装は、データ交換のための実装(Apache Arrowフォーマットを扱うための実装)がだいたい完成してきていて、Apache Arrowフォーマットのデータを高速に処理するための実装に比重が移ってきています。
それでは詳細を説明していきます。
扱えるデータ
まずはApache Arrowが扱えるデータを説明します。
現在Apache Arrowが扱えるデータは次の通りです。
-
データフレーム
-
密な多次元配列
それぞれ説明します。
データフレーム
Apache Arrowはもともと「データフレーム」なデータを扱うために開発が始まりました。「データフレーム」は表形式のデータです。RDBMSのテーブルのようなものです。1つのテーブルには1つ以上のカラムがあり、それぞれのカラムは違う型にできます。
現在、Apache Arrowがサポートしている型は次の通りです。これらの型のデータを扱えるということです。
-
真偽値(1bit)
-
整数
-
8bit非負整数(リトルエンディアン)
-
8bit整数(リトルエンディアン)
-
16bit非負整数(リトルエンディアン)
-
16bit整数(リトルエンディアン)
-
32bit非負整数(リトルエンディアン)
-
32bit整数(リトルエンディアン)
-
64bit非負整数(リトルエンディアン)
-
64bit整数(リトルエンディアン)
-
-
浮動小数点数
-
16bit浮動小数点数
-
32bit浮動小数点数
-
64bit浮動小数点数
-
-
小数(精度・スケールを指定)
-
可変長UTF-8文字列
-
バイナリーデータ
-
可変長バイナリーデータ
-
固定長バイナリーデータ
-
-
日付
-
UNIXエポックからの経過日数(32bit)
-
UNIXエポックからの経過ミリ秒数(64bit)
-
-
タイムスタンプ(64bit整数)
-
UNIXエポックからの経過秒数
-
UNIXエポックからの経過ミリ秒数
-
UNIXエポックからの経過マイクロ秒数
-
UNIXエポックからの経過ナノ秒数
-
-
時刻
-
深夜0時からの経過秒数(32bit整数)
-
深夜0時からの経過ミリ秒数(32bit整数)
-
深夜0時からの経過マイクロ秒数(64bit整数)
-
深夜0時からの経過ナノ秒数(64bit整数)
-
-
リスト(0個以上の同じ型の値を持つ型)
-
構造体(1個以上のフィールドを持つ型で、各フィールドは別の型にできる)
-
共用体(1個以上のフィールドを持つ型で、各フィールドは別の型にでき、どれか1つのフィールドの値のみが設定されている)
-
辞書
-
統計っぽい説明:名義尺度なカテゴリーデータ
-
実装よりの説明:各値に整数でIDを割り当て、数値で値を表現する型
-
scikit-learnを知っている人向けの説明:
sklearn.preprocessing.LabelEncoder
のtransform
結果を値として使う型
-
なお、どの型でもnull
を設定できます。
多次元配列
Apache Arrowはデータフレームを想定して開発が始まりましたが、多次元配列も扱えるとよさそうというフィードバックがありApache Arrow 0.3.0から多次元配列をサポートしました。
参考:ARROW-550 [Format] Add a TensorMessage type
データフレームと多次元配列の違いは次の通りです。
-
次元数:
-
データフレームは2次元
-
多次元配列はn次元
-
-
型:
-
データフレームはカラム毎に違う型を使える
-
多次元配列の要素の値はすべて同じ型
-
Pythonでいうと、pandasが実現しているのがデータフレームで、NumPyが実現しているのが多次元配列です。
多次元配列には密(dense)な多次元配列と疎(sparse)な多次元配列があります。密な多次元配列は要素の値をすべて持っている多次元配列で、疎な多次元配列は0
以外の要素の値だけを持っている多次元配列です。
たとえば、[0, 0, 0, 1, 0]
という多次元配列(1次元配列)があった場合、5要素全部の値を保持しているのが密な多次元配列で、「4番目の要素の値が1でそれ以外は0」という情報だけ保持しているのが疎な多次元配列です。
Apache Arrowは現在は密な多次元配列のみサポートしています。疎な多次元配列もサポートする予定があります。現在は妥当なデータフォーマットの仕様を検討する人を募集している状態です。疎な多次元配列の知見がある人はぜひ取り組んでみてください。
参考:ARROW-854 [C++] Support sparse tensor
データ処理部分の実装
Apache Arrowは各種データ処理ツールが共通で使える高速なデータ処理機能の開発も重視しています。これまでデータフォーマットの方に注力していたためまだあまり進んでいませんが、現時点できることを紹介します。
データ処理部分の実装に着手しているのはC++、JavaScript、Goでの実装だけなので、それぞれの言語での実装ごとに紹介します。
なお、データ処理部分が実装されていない言語あるいは実装が始まっている言語でも未実装の処理はできないかというとそうでもありません。読み込んだApache Arrowフォーマットのデータを他の既存のライブラリーで扱えるように変換することで処理できます。コピーやデータの変換が必要になるので多少非効率ですが、Apache Arrowフォーマットによりデータ交換部分のコストがすごく効率化されるので割に合うことが多いです。
たとえば、Apache Sparkではデータ交換部分でApache Arrowを使うことにより大きく速度向上しています。Apache Sparkには処理の一部をPythonで実装できるPySparkという機能があります。Apache Spark本体はScalaで実装されているので別プロセスで動いているPythonにデータを渡す必要があります。このときにソケット経由でApache Arrowデータを渡しています。従来はPythonに標準で含まれているpickle
を使ってデータを転送していました。Apache Arrowフォーマットで受け取ったデータはpandasのオブジェクトに変換し、データ処理はpandasの機能を使います。ちなみに、Apache Arrowのデータからpandasのオブジェクトへの変換は、型によってはゼロコピーで実現できます。この場合はさらに効率的にデータ交換できます。
参考:Speeding up PySpark with Apache Arrow
それでは現時点で実装されているデータ処理機能を各言語毎に紹介します。
C++
C++で実装されている処理は次の通りです。
-
キャスト(型の変換)
-
例:16bit整数を32bit整数へ変換
-
辞書型への変換も実装済み
-
-
要素ごとの論理否定
-
要素ごとの論理積
-
要素ごとの論理和
-
要素ごとの排他的論理和
実装が予定されている処理のリストはApache Arrowのプロジェクト管理ツールであるJIRAを「kernel」で検索してみてください。実装してみたい処理があったらぜひ取り組んでみてください。
JavaScript
JavaScriptで実装されている処理は次の通りです。
-
要素ごとの論理否定
-
要素ごとの論理積
-
要素ごとの論理和
-
要素ごとの排他的論理和
-
要素ごとの
==
比較 -
要素ごとの
<
比較 -
要素ごとの
<=
比較 -
要素ごとの
>
比較 -
要素ごとの
>=
比較 -
値ごとの出現数のカウント
- 辞書型のみサポート
Go
Goで実装されている処理は次の通りです。
- 全要素の合計値の計算
まだ1つしか実装されていないんですが、SIMDを活用するために面白い実装になっているので少し説明します。
SIMDを使うためにはCPUがSIMDをサポートしていないと使えません。最近のC/C++コンパイラーはCPUがSIMDをサポートしているかどうかで自動的にSIMDを使ったコードを生成できるくらい賢くなっています。しかし、Goのコンパイラーはまだその最適化はできません。そこで、事前にClangを使ってSIMD対応のアセンブリを出力してそれをGoに組み込んでいます。ただ、この方法だとCPUが対応していないSIMDもGoに組み込まれています。そこで、使えるSIMDを実行時に検出して適切なSIMD実装の処理を呼び出すようにしています。詳細は以下の参考URLを参照してください。Go実装を書いたInfluxDataの人が書いたブログです。
参考:InfluxData Working on Go Implementation of Apache Arrow | InfluxData
ちなみに、InfluxDataは時系列データベースInfluxDBを開発している会社です。InfluxDBはGoで実装されているのでInfluxDBで活用するためにApache Arrowの開発に参加しています。
データ処理部分の実装のまとめ
データ処理部分はあまり実装されていなくてがっかりしたかもしれません。少し将来の話もしておきます。
Apache Arrowでは各要素の処理だけでなく、複雑な条件を高速に処理する実行エンジンの実装も視野に入れています。その実装として使えそうなモジュールが近いうちにApache Arrowに入る予定です。
このモジュールにはGandivaという名前がついています。Gandivaはどのように処理を実行すればよいかを実行時に計算して、LLVMを使って実行時にコンパイルしてから実行します。コンパイルしているので事前にコンパイルしたときと同じくらい高速になりますし、LLVMはSIMD用の最適化機能もあるので、SIMD対応の処理にもなります。詳細はGandivaの開発者が書いた以下のブログを参考にしてください。
参考:Introducing the Gandiva Initiative for Apache Arrow - Dremio
「近いうちにApache Arrowに入る予定」で今はどんなステータスなのかについても触れておきます。
外部のモジュールがApache Arrowに取り込まれるには次のステップを踏む必要があります。
-
PMC(プロジェクト管理チームみたいなやつ)が承認する
-
IPクリアランス(知的財産侵害を心配しなくても使えることを確認する)
-
取り込み
Gandivaは今は「PMCが承認する」を完了したステータスです。
参考:[RESULT] [VOTE] Accept donation of Gandiva to Apache Arrow
次はIPクリアランスなのですが、そのためにはApache Arrowのリポジトリーにpull requestを作る必要があるのですが、まだそれはできていません。
0.11.0のリリースが今月中の予定なので、それには間に合わなそうですが、数ヶ月以内には入るんじゃないかと私は思っています。
Plasma
Apache Arrowには同一マシン内でオブジェクトを共有する機能もあります。それがPlasmaという名前のモジュールです。Apache Arrowにはデータフレームなデータを低いシリアライズ・デシリアライズコストで交換するための仕組みがあります。それを活用して実装しています。Plasmaはデータフレームなデータを交換するための機能ではなく、メモリー上の生データを交換するためのもっと低レベルな機能であることに注意してください。
PlasmaはもともとRayの一部として開発していた機能ですが、広く有用そうだということでApache Arrowに移動しました。Rayは大規模な機械学習・強化学習用の高速な分散処理フレームワークです。カリフォルニア大学バークレイ校のRISELabが開発しています。
Plasmaを使うとCPU上のデータもGPU上のデータもゼロコピーで共有できます。Plasmaでデータを共有することで複数プロセスで分散処理できます。
Plasmaを使うためにはサーバープロセスを起動する必要があります。各プロセスがサーバープロセスに接続して、共有したいデータを置いたり、共有されているデータを参照したりして処理を進めます。
参考:Plasma In-Memory Object Store
各言語での実装の完成度
次は各言語での実装の完成度を説明します。
Apache Arrowフォーマットはより多くの環境で使えるほどメリットが大きくなります。そのため、Apache Arrowフォーマットを使える言語を増やすべく開発しています。現在は次の言語で使えます(再掲)が、その完成度は様々です。
-
C
-
C++
-
Go
-
Java
-
JavaScript
-
Lua
-
MATLAB
-
Python
-
R
-
Ruby
-
Rust
特に完成度が高いのがJava、C++、Python、C、Ruby実装です。
最初にJava、C++実装から開発が始まっているのでこれらの言語の実装は完成度が高いのです。Python、C、Ruby実装も完成度が高いのはこれらはC++実装のバインディング(C++実装の機能を他の言語でも使えるようにしている)からです。Lua実装も(間接的に)C++実装のバインディングなのですが、APIの使い勝手という面で少し完成度が落ちます。
MATLAB、R実装もC++実装のバインディングですが、まだ開発が始まったばかりで完成度はそれほどではありません。
Go、JavaScript、Rust実装は1から実装しているのでまだJava、C++実装ほどの完成度にはなっていません。ただ、対応しているデータフレームの型に関してはJavaScript実装の完成度は高いです。
まだApache Arrowのリポジトリーには入っていませんが、Juliaの実装も進んでいます。Julia実装は1から実装しています。
それではアルファベット順に各言語の実装の概要と完成度を紹介します。
C
C実装はC++実装のバインディングです。これは私が作りはじめたモジュールです。今は主に私と@shiro615が開発しています。
GLibというC言語用の便利ライブラリーを使っているのが特徴です。GLibを使った理由は「各種言語のバインディングを自動生成できる」からです。念のため補足しますが、「C++実装のバインディングを自動生成できる」のではなく、「C++実装のバインディングであるC実装のバインディングを自動生成できる」です。
この機能を使ってRubyとLuaのバインディングが動いています。GLibにはC言語でオブジェクト指向プログラミングをするためのライブラリーGObjectも含まれています。GObjectを使ってオブジェクト指向なAPIを実現しています。そのため、自動生成されたRuby・Lua用のバインディングもオブジェクト指向なAPIになっていて使いやすいです。通常、Cのライブラリーのバインディングを自動生成するアプローチは生のCの関数をそのまま使えるようなAPIになります。そうすると使いやすくするために手動で使いやすいAPIをラップするレイヤーを実装する必要があるのですが、C実装で使っている自動生成機能ではその手間は必要ありません。
なお、C実装で使っている自動生成する仕組みはGObject Introspectionです。
C実装ではPlasma以外のすべてのC++実装の機能を使えます。近いうちにPlasmaも使えるようになる予定です。
C++
C++実装は1から開発しています。主に@wesm、@xhochy、@pitrou、@pcmoritz、@cpcloudが開発しています。
C++実装はC++11を使っています。そのため、CentOS 6に標準で入っている古いg++
ではビルドできません。CentOS 6でビルドしたい時はdevtoolset-6あるいはdevtoolset-7パッケージをインストールして新しいg++
を用意します。
C++実装はデータフレームのすべての型も多次元配列もサポートしています。
PlasmaのサーバープロセスはC++で実装されています。もちろん、Plasmaクライアント機能も実装されています。
C++実装には他のフォーマットとの相互変換機能がいくつか含まれています。現時点で実装されているフォーマットは次の通りです。
-
-
PythonとR間でデータ交換するためのフォーマット。
-
Apache Arrowで置き換えるため現在は非推奨。
-
-
-
Hadoop用のカラムナーなフォーマット。
-
現時点では読み込みのみ。簡単な書き込み機能はサポート予定。
-
-
-
Hadoop用のカラムナーなフォーマット。
-
Apache ArrowのC++実装ではなく、Apache ParquetのC++実装の方に実装されている。
-
メモリー上ではなくGPU上にデータを置く機能もあります。まだあまり活用されていない機能ですが、今後より活用されていくはずです。というのは、Apache ArrowフォーマットのデータをGPU上で処理するライブラリーが開発されているからです。
たとえばlibgdfというライブラリーです。libgdfはC言語用のデータフレームライブラリーです。CUDAを使って実装しています。値の比較処理だけでなくにグループ化機能やジョイン機能といった高度な機能も実装されています。
libgdfはGoAi (GPU Open Analytics Initiative)という団体が開発しています。この団体のミッションは「GPUを活用してデータ分析できるプラットフォームを構築する」です。
他にもMapDというGPU上で動くデータベースがApache Arrowフォーマットに対応しています。MapDではSQLを使ってデータを処理できます。MapDを開発している会社もGoAiに参加しています。
Python連携用のモジュールもC++実装に含まれています。Python連携用のモジュールではC++のApache ArrowオブジェクトとPythonのpandas・NumPyオブジェクトを相互変換する機能を提供しています。Pythonバインディングの方に入れずにC++実装の方に入れているのはいろいろなライブラリーでこの機能を共有したいからです。当初の想定の1つのpandas 2.0ではまだ使っていませんがすでに活用している例があります。それはRed Arrow PyCallという同一プロセス内でRubyとPythonを両方動かしてRubyとPython間でApache Arrowデータを共有するためのライブラリーです。Apache ArrowデータとPythonオブジェクトを変換するためにPython連携用のモジュールを使っています。
参考:ARROW-341 [Python] Making libpyarrow available to third parties
2018年9月にApache ParquetのC++実装がApache Arrowのリポジトリーに移動しました。理由は現時点では両者がすごく密に連携をして開発を進めているので同じリポジトリーにあった方が開発を進めやすいからです。将来的に、APIが安定したらまた分離するかもしれません。
参考:
-
Apache Parquet C++の開発チームでの議論: [VOTE] Moving Apache Parquet C++ development process to a monorepo
-
Apache Arrowの開発チームでの議論: [DISCUSS] Solutions for improving the Arrow-Parquet C++ development morass
-
pull request: ARROW-3075: [C++] Merge parquet-cpp codebase into Arrow C++ codebase by wesm · Pull Request #2453 · apache/arrow
Go
Go実装は1から開発しています。InfluxDataの人たちが開発しました。Apache Arrowのリポジトリーに入ってからは@sbinet(InfluxDataの人ではない)が開発しています。
対応しているデータフレームの型は次の通りです。
-
真偽値(1bit)
-
整数
-
8bit非負整数(リトルエンディアン)
-
8bit整数(リトルエンディアン)
-
16bit非負整数(リトルエンディアン)
-
16bit整数(リトルエンディアン)
-
32bit非負整数(リトルエンディアン)
-
32bit整数(リトルエンディアン)
-
64bit非負整数(リトルエンディアン)
-
64bit整数(リトルエンディアン)
-
-
浮動小数点数
-
32bit浮動小数点数
-
64bit浮動小数点数
-
-
バイナリーデータ
- 可変長バイナリーデータ
-
タイムスタンプ(64bit整数)
-
UNIXエポックからの経過秒数
-
UNIXエポックからの経過ミリ秒数
-
UNIXエポックからの経過マイクロ秒数
-
UNIXエポックからの経過ナノ秒数
-
-
リスト(0個以上の同じ型の値を持つ型)
-
構造体(1個以上のフィールドを持つ型で、各フィールドは別の型にできる)
多次元配列はまだサポートしていません。
Plasmaクライアント機能もまだサポートしていません。
他のフォーマットとの相互変換機能もまだサポートしていません。
Java
Java実装は1から開発しています。主に@julienledem、@BryanCutler、@StevenMPhillips、@siddharthteotia、@elahrvivaz、@icexellossが開発しています。
Java実装はデータフレームの型をほとんどサポートしています。サポートしていないのは次の型だけです。
-
浮動小数点数
- 16bit浮動小数点数
多次元配列はまだサポートしていません。
Plasmaクライアント機能をサポートしています。
JDBCで取得したデータをApache Arrowオブジェクトで返す機能が実装されているので、簡単にRDBMSのデータをApache Arrowオブジェクトとして扱えます。
JavaScript
JavaScript実装は1から開発しています。主に@trxcllntと@TheNeuralBitが開発しています。
TypeScriptで開発しています。Webブラウザー上でもNode.js上でも動きます。
MapDで処理した結果をWebブラウザー上でビジュアライズするために活用できます。MapDはApache Arrowに対応しているのでWebブラウザー上のJavaScriptと低いコストでデータ交換できるのです。
参考:Supercharging Visualization with Apache Arrow
JavaScript実装はデータフレームのすべての型に対応しています。
多次元配列はまだサポートしていません。
Plasmaクライアント機能もまだサポートしていません。node-plasmaというNode.js用モジュールを作ってサポートしたいという構想があります。
参考:Connecting JS to modern GPU and ML frameworks: Update from Nvidia GTC 2018
Julia
公式のJulia実装はまだありませんが、@ExpandingManがArrow.jlの実装を進めています。公式実装にするための議論も進んでいます。
参考:collaboration with Apache Arrow org · Issue #28 · ExpandingMan/Arrow.jl
Julia実装が対応しているデータフレームの型は次の通りです。
-
真偽値(1bit)
-
整数
-
8bit非負整数(リトルエンディアン)
-
8bit整数(リトルエンディアン)
-
16bit非負整数(リトルエンディアン)
-
16bit整数(リトルエンディアン)
-
32bit非負整数(リトルエンディアン)
-
32bit整数(リトルエンディアン)
-
64bit非負整数(リトルエンディアン)
-
64bit整数(リトルエンディアン)
-
-
浮動小数点数
-
32bit浮動小数点数
-
64bit浮動小数点数
-
-
可変長UTF-8文字列
-
日付
- UNIXエポックからの経過日数(32bit)
-
タイムスタンプ(64bit整数)
-
UNIXエポックからの経過秒数
-
UNIXエポックからの経過ミリ秒数
-
UNIXエポックからの経過マイクロ秒数
-
UNIXエポックからの経過ナノ秒数
-
-
時刻
-
深夜0時からの経過秒数(32bit整数)
-
深夜0時からの経過ミリ秒数(32bit整数)
-
深夜0時からの経過マイクロ秒数(64bit整数)
-
深夜0時からの経過ナノ秒数(64bit整数)
-
-
リスト(0個以上の同じ型の値を持つ型)
-
辞書
-
統計っぽい説明:名義尺度なカテゴリーデータ
-
実装よりの説明:各値に整数でIDを割り当て、数値で値を表現する型
-
scikit-learnを知っている人向けの説明:
sklearn.preprocessing.LabelEncoder
のtransform
結果を値として使う型
-
多次元配列はまだサポートしていません。
Plasmaクライアント機能もまだサポートしていません。
Lua
Lua実装はC実装とlgiを使って自動生成しています。特にパッケージにはしていなくて、以下のようにすれば使えるという状態です。
local lgi = require 'lgi'
local Arrow = lgi.Arrow
参考:arrow/c_glib/example/lua at master · apache/arrow
C実装で使える機能はすべて使えます。
MATLAB
MATLAB実装はC++実装のバインディングです。2018年9月10日現在ではまだ1コミットだけですが、主に開発をしている人は@kevingurneyです。MATLABを開発しているMathWorksの人です。
現在はFeatherフォーマットのデータを読み込む機能だけがあります。Apache Arrow形式のデータはまだ読めません。
Python
Python実装はC++実装のバインディングです。主に開発をしている人たちはC++実装を開発している人たちとだいたい同じです。
C++実装で使える機能はすべて使えます。単に使えるだけではなく、Pythonからより便利に使えるようにAPIが整備されています。
Cythonを使って実装されています。
pandas・NumPyのオブジェクトに簡単に相互変換できる機能も実装されていて、既存のライブラリーとシームレスに使えるようになっています。
R
R実装はC++実装のバインディングです。2018年9月10日現在ではまだ1コミットだけですが、主に開発をしている人は@romainfrancoisです。↓の最初のpull requestにR関係の人が何人かコメントしたりJIRAに新しくissueを作っている人もいるので、今後開発に参加する人は増えていきそうな気配がします。
現在はC++実装の簡単に機能を使えるだけでApache Arrowフォーマットのデータを読めたりはしません。ただ、開発環境の整備は進んでいます。すでにTravis CIでテストできるようになっています。
R実装の進め方はGoogle Docsで議論しています。ある程度まとまったらJIRAのチケットにブレイクダウンされる予定です。
参考:Apache Arrow for R - Initial Roadmap - Google Docs
Ruby
Ruby実装はC実装とgobject-introspection gemを使って自動生成しています。主に開発している人たちはC実装を開発している人たちです。
C実装で使える機能はすべて使えます。単にバインディングを自動生成しているだけではなく、Python実装のように、より便利に使うためのAPIも整備してあります。
既存のライブラリーとシームレスに使えるようにするための機能もあります。依存関係を増やしたくないのでRuby実装自体には入っていないのですが、関連ライブラリーとして次のライブラリーがあります。
-
- Ruby/GSLと相互変換用のライブラリー。
-
- NMatrixと相互変換用のライブラリー。
-
- Numo::NArrayと相互変換用のライブラリー。
-
- PyCall経由でPython実装のオブジェクトと相互変換するライブラリー。
また、Apache Parquetフォーマットのデータを読み込むためのRed Parquetもあります。
Rust
Rust実装は1から開発しています。主に@andygroveが開発しています。
Rust実装が対応しているデータフレームの型は次の通りです。
-
真偽値(1bit)
-
整数
-
8bit非負整数(リトルエンディアン)
-
8bit整数(リトルエンディアン)
-
16bit非負整数(リトルエンディアン)
-
16bit整数(リトルエンディアン)
-
32bit非負整数(リトルエンディアン)
-
32bit整数(リトルエンディアン)
-
64bit非負整数(リトルエンディアン)
-
64bit整数(リトルエンディアン)
-
-
浮動小数点数
-
16bit浮動小数点数
-
32bit浮動小数点数
-
64bit浮動小数点数
-
-
可変長UTF-8文字列
-
リスト(0個以上の同じ型の値を持つ型)
-
構造体(1個以上のフィールドを持つ型で、各フィールドは別の型にできる)
多次元配列はまだサポートしていません。
Plasmaクライアント機能もまだサポートしていません。
各言語での実装の完成度のまとめ
Apache Arrowの各言語での実装をそれぞれ説明しました。
サポートしている型という観点では、数値型と真偽値型はどの言語の実装でもカバーしています。時間関連の型はGo、Rust実装以外はカバーしています。複合型は共用体以外ならどの言語の実装でもカバーしています。C++、Java、JavaScript実装およびC++実装のバインディングはすべての型に対応しています。
他のフォーマットとの相互変換はC++実装ファミリーが進んでいます。Apache ParquetやApache ORC、Featherフォーマットと相互変換できます。
GPUサポートもC++実装ファミリーが進んでいます。
Plasmaを使いたい場合はC++、Java、Python実装を使うことになります。近いうちにC実装ファミリーでも使えるようになる予定です。
Apache Arrowデータを高速に処理するという観点ではどの実装もまだカバー範囲は狭いです。
Apache Arrowの今後
ここまででApache Arrowの概要、現状を説明しました。最後に今後のことを説明します。
実行エンジンGandivaの取り込み
現状ではApache Arrowデータを高速に処理するところがまだ弱いです。そのあたりをカバーするモジュールがGandivaです。このGandivaが近いうちに取り込まれ、Apache Arrowデータを高速処理できる基盤となるでしょう。
GandivaはC++で実装されているので、C++実装ではすぐにGandivaを使えるようになります。
Gandivaを開発しているDremioは自社で開発しているサービスでApache Arrowを利用しています。このサービスはJavaで実装しているので、C++で実装したGandivaをJavaから使うためのバインディングも開発しています。そのため、Java実装でもすぐにGandivaを使えるようになります。
C++実装ベースのPython実装、C実装ファミリーもすぐに使えるようになるでしょう。
CSVパーサーの追加
世の中では今でもCSVがよく使われています。そのため、CSVをパースしてApache Arrowオブジェクトを作成するパーサーの開発を進めています。
参考:Building a fast Arrow-native delimited file reader (e.g. for CSVs) - Apache Mail Archives
これはC++実装での話です。実装されたらC++実装ファミリーでもすぐに使えるようになるでしょう。
Apache Arrow対応クライアントの追加
Java実装にはJDBCで取得したデータをApache Arrowオブジェクトで返す機能があります。それと同様な機能がC++実装にも入る予定です。
まずは、Apache Hive・Apache Impalaからデータを取得してApache Arrowオブジェクトで返す機能が入りました。
他にもPostgreSQLからデータを取得してApache Arrow形式で返す機能の実装も計画しています。
参考:Developing native Arrow interfaces to database protocols - Apache Mail Archives
同様のことをしているPythonライブラリーがあります。それがturbodbcです。これはODBCで取得したデータをApache Arrowオブジェクトで返すことができます。もしかしたら、今後、この機能がApache Arrowの方に移動して、turobdbcはそれを使うという関係になるかもしれません。
まとめ
Apache Arrowの概要、2018年9月時点での現状、今後のことについて説明しました。Apache Arrowは数年後にはデータ処理界隈で重要なコンポーネントになっているだろうプロジェクトです。日本でもApache Arrowのことを知っている人が増えるといいと思うので日本語でまとめました。Apache Arrowを使う人が増えるといいなぁと思います。さらに言えば開発に参加する人も増えるといいなぁと思います。
この説明では省略しましたが、Apache Arrowの生い立ちについて面白い読み物があります。PMC(プロジェクト管理チームみたいな感じ)のチェアー(1番偉い人)が書いたものです。興味がある人はこちらも読んでみてください。
-
The Origin & History of Apache Arrow - Dremio
-
「Arrow」という名前の由来についての話もある。
-
私が知っていることはまとめたつもりですが、もしかしたらカバーできていない話があるかもしれません。もし、「○○についても知りたい!」という方がいたらRed Data Toolsのチャットで声をかけてください。この記事に追加します。
Apache Arrowについて講演して欲しいという方はお問い合わせフォームからご連絡ください。
私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。