Cxx.jl を使ってみた感想 + OpenCV.jl, Libfreenect2.jl の紹介
はじめに
Julia Advent Calendar 2015 22日目の記事です。
Julia の C++ FFI (Foreign Function Interface) である Cxx.jl をしばらく使ってみたので、その感想を書きます。加えて、Cxx.jl を使って作った成果物の話も簡単に書こうと思います(冒頭に貼った画像は、OpenCV.jl でテキトーにカメラから画像をキャプチャしてthresholdingしたやつです)。 Cxx.jl の動作原理については、僕の理解が不十分なため簡単にしか紹介できませんが、そもそも使ったことがある人が稀だと思われるので、感想程度でも役に立てば幸いです。
Cxx.jl とは
https://github.com/Keno/Cxx.jl
簡単に説明すると、Cxx.jl とは、Julia から C++ を使用する(e.g. 関数呼び出し、メソッド呼び出し、メンバ変数へのアクセス、etc) ための機能を提供するパッケージです。C++のライブラリを活用したい、あるいはパフォーマンスがシビアな箇所で一部 C++ 使いたい(Cインタフェースを作りたくない1)、といった場合に便利です。
Cxx.jl の原理についてざっくりといえば、clang を用いて C++ から LLVM IR を生成し、llvmcall を用いて(Just in time に)コードを実行する、という方式のようです2
Cxx.jl の原理について知りたい場合は、Cxx.jl のソースコード(+コメント)を、Cxx.jl を使うと何ができるのか知りたい場合は、Cxx.jl の README を御覧ください。
以下、過去を思い出しながら感想を書いてみます
実際に使う前に
Pkg.build(“Cxx”) を成功させることが困難
そもそも使いはじめる前に、ビルドすることが困難でした。Cxx.jl を動作させるためには、
- julia
- llvm
- clang
- lldb
の開発版が必要ですが、ビルドが難しい大きな原因は、動作することが保証された明確な revision が存在しないことにあります。(なんじゃそれ、と思うかもしれませんが、まぁまだ安定版はリリースされていないので、、)
今でこそ、llvm, clang, lldbは、Keno氏の fork の kf/gallium ブランチ使えばいいよと README に書いてありますが、僕が使い始めた二ヶ月ほど前は、開発版のllvmが必要だよ、くらいにしか書いていませんでした(参考: Cxx.jl/README)。何度もllvmをビルドし直すのは、本当に苦行でした…
参考:
今日だけでllvmをn回ビルドしてる(ビルドできたとは言ってない
— 山本りゅういち (@r9y9) October 16, 2015
思考停止の様子:
make -C deps clean-llvm & make -j4
— 山本りゅういち (@r9y9) November 28, 2015
Cxx.jl のビルドはどうするのが一番簡単なのか
さて、さらっと書きましたが、今では llvm, clang, lldb のkf/gallium ブランチを使えば、比較的簡単に、多少の試行錯誤3 で Cxx.jl をビルドして使えます。
開発版 llvm と一緒に Julia をビルドする
Juliaをクローンしたディレクトリで、以下の様な Make.user
ファイルを作成して make します。
override LLDB_VER=master
override LLVM_VER=svn
override LLVM_ASSERTIONS=1
override BUILD_LLVM_CLANG=1
override BUILD_LLDB=1
override USE_LLVM_SHLIB=1
override LLDB_DISABLE_PYTHON=1
override LLVM_GIT_URL_LLVM=https://github.com/JuliaLang/llvm.git
override LLVM_GIT_URL_LLDB=https://github.com/JuliaLang/lldb.git
override LLVM_GIT_URL_CLANG=https://github.com/JuliaLang/clang.git
override LLVM_GIT_VER=kf/gallium
override LLVM_GIT_VER_LLDB=kf/gallium
override LLVM_GIT_VER_CLANG=kf/gallium
一部、LLVM_ASSERSONS を有効にするなど、必ずしも必須でないものも含まれていますが、こちらが現状の推奨のようです。この設定で、僕はubuntu 14.04, osx 10.10 でビルドが通ることを確認しました4
注意:すでに llvm や clang がローカルにクローン済の場合、deps/srccache
以下からクローン済みのソースを消してからビルドすることをおすすめします。すでにクローンされていて、upstream の変更を取り入れたい場合は、
make -C deps update-llvm
とすると便利です。
Cxx.jl のインストール
Pkg.clone("https://github.com/Keno/Cxx.jl.git")
Pkg.build("Cxx")
エラーがでなければ、インストール完了5です。
実際に使ってみたあと
さて、ようやくビルドもできて、ここからは使ってしばらくしての感想です。
Julia 上で C++ の syntax がそのまま使える
まず、簡単に Cxx.jl の機能を挙げると、重要なのは
cxx"..."
icxx"..."
@cxx
の三つです。以下、簡単に例をあげると、cxx"..."
でC++ syntax を評価して:
cxx"#include <iostream>"
cxx"""
namespace test {
void f() {
std::cout << "Hello C++" << std::endl;
}
}
"""
@cxx
マクロで C++ 関数を呼び出す:
@cxx test::f() # Hello C++
cxx"..."
はグローバルスコープで評価されますが、icxx"..."
を使えば、特定のスコープ内で C++ を使用することもできます。
for i in 1:10
icxx"""std::cout << $i << std::endl;"""
end
ccall
のように、返り値、引数の型などを指定して実行するのではなく、C++ のsyntax をそのまま使ってコードが書ける、という点にびっくりしました。
template も使える
cxx"""
template <typename T>
T add(T x, T y) { return x + y; }
"""
こんな感じで特殊化も可能
cxx"""
template <>
int add<int>(int x, int y) { return x + y; }
"""
書いてて気付きましたが、README には template について言及されていませんね。僕は、今のところ問題なく使えています。例には出していませんが、template class ももちろん使えます(例. std::vector<T>
)。
その他雑記
- Cxx.jl で使える C++ には制約がある(はず)だが、ここ二ヶ月使用した限りでは、大きな制約に出会ってないし、快適
- 共有ライブラリの呼び出しは、
ccall
と違ってライブラリだけでなくヘッダーファイルも必要 using Cxx
にはけっこう時間がかかる。僕の環境では約15秒だった- たまに llvm error を吐いて落ちる。デバッグするには llvm, clang についてある程度知識がないと難しそう
- C++ REPL 便利
という感じですかね。書き進むに連れて適当になってすいません、、、
Cxx.jl を使って作った成果物
まとめに入る前に、Cxx.jl を使って遊ぶ過程で作った成果物を、簡単なコメント付きで紹介します。
OpenCV.jl
https://github.com/r9y9/OpenCV.jl
Cxx.jl の学習の題材として作りました。途中から cv::Mat 周りを真面目に作り始めたので、それなりに使えると思います。
デザインポリシーとして、
- cv::Mat を
AbstractArray{T,N}
の subtype として Julia ライクに使えること - cv::Mat と Julia の Array の相互変換をサポートすること
を念頭において作りました。
LibFreenect2.jl
https://github.com/r9y9/LibFreenect2.jl
ちょうど仕事で kinect v2 を触っていたので、遊びがてらやってみました。
まとめ
- Cxx.jl をビルドするのはけっこう面倒ですが、C++を(主観ですが)ほとんど不自由なくJITライクに使えるので、非常に便利です
- 二つほど C++ ライブラリのラッパーを作ってみましたが、簡単にできるので、みなさんもお試しあれ
- レッツ・トライ Cxx.jl!
おまけ
現在 Julia community では、llvm 3.3 から llvm 3.7.1 に移行しようとする動きがあるので(ref: julia/issues/9336, julia/pull/14430)、移行後は、もう少しビルドが楽になるかもしれません。
実は、この記事を読んでも、きっと今は Cxx.jl をビルドできないんじゃないかなと思うんですが、もしどうしてもビルドしたい、ということであれば、僕のローカルの llvm, clang, lldb, julia の revision を調べて教えるので、言ってください。