目次
- 目次
- 注意 (2020年8月追記)
- はじめに
- Juliaにおけるコンパイル
- Juliaのコードをコンパイルするスクリプトjuliac
- JuMPの最適化コードをjuliacでコンパイルして高速化してみる
- juliacを使う時の注意点
- 参考資料
- MyEnigma Supporters
注意 (2020年8月追記)
この記事は非常に古く、Julia 1.0以降では利用できません。。。
現時点で単一バイナリを作る方法はなくなってしましましたが、
Juliaのランタイムも含めて一つのディレクトリにまとめるのは、
PackageCompiler.jlで実現可能です。
はじめに
最近、Juliaというプログラミング言語にハマっているのですが、
Juliaを使って、
高計算パフォーマンスなシステムを構築したい時に
やはりコンパイルできたらいいなと思ってしまいます。
Juliaは動的な言語なので、
実行時にJITコンパイラを使ってコンパイルをしながら
コードを実行するのですが
すでにコードがfixされたコードであっても、
実行するたびにコンパイルする必要があるため、
その分、実行が遅くなりがちです。
しかし最近、下記の記事や、
下記のリポジトリのライブラリなどのように、
Juliaをコンパイルする取り組みが始まっているので、
今回はJuliaをコンパイルし、実行する手法を紹介したいと思います。
Juliaにおけるコンパイル
下記の記事をベースに
Juliaにおけるコンパイルについて概要を説明したいと思います。
Juliaをコンパイルしたい場合、
原理的には、eval, マクロ, generated functionを
使わないJuliaのコードであれば静的にコンパイルすることができます。
加えて、Juliaの基本モジュールの大半は、
型安定性を考慮して設計されているため、
静的にコンパイルし易い形で構成されています。
従って、ある特定のJuliaのコードであれば、
特別なトリック無しで静的コンパイルが可能であるということです。
例えば、シンプルな方法だと、
下記のコマンドで, test.jlのjuliaのコードを
juliaのinterfaceファイルに変換し、
$ julia --output-jl interface.jl test.jl
続いて、下記のコマンドでinterfaceファイルから
オブジェクトファイルを生成できます。
$ ./julia --output-o sys.o --sysimage inference.ji --startup-file=no sysimg.jl
最後に生成されたオブジェクトファイルを
C言語のコンパイラでコンパイルすれば、
静的コンパイルが可能になります。
$ cc -o julia-app sys.o repl.c -ljulia
Juliaのコードをコンパイルするスクリプトjuliac
下記のGithubリポジトリに、
前述の手法を使ってJuliaのコードを静的にコンパイルするための、
公式ライブラリが公開されています。
使い方としては、こちらのリポジトリにある通り、
リポジトリをcloneしてきて、
リポジトリ内のjuliac.jlというスクリプトを使って、
下記のようにコンパイルしたいコード(下記の場合はhello.jl)を指定するだけです。
julia juliac.jl hello.jl
うまくいくと、
builddirというディレクトリができて、
その中に実行ファイルや、オブジェクトファイル、
ダイナミックライブラリのファイルなどが生成されます。
(サンプルのhello.jlは、UnicodePlotsというライブラリに依存しているので、
事前にPkg.add("UnicodePlots")でインストールする必要があります。)
あとは、builddir/helloを実行すると、
下記のような実行結果が表示されるはずです。
JuMPの最適化コードをjuliacでコンパイルして高速化してみる
Juliaコードにおけるコンパイルの有効性を確認するために、
以前紹介したJuliaの最適化ライブラリであるJuMPを使用した、
ナップサック問題も最適化問題で簡単なベンチマークをとってみたいと思います。
ナップザック問題の概要については下記の記事を参照ください。
今回は下記のようなコードを、
通常の方法でJuliaで実行する方法と、
juliacでコンパイルして実行する方法を比較してみました。
module mystuff using JuMP using Mosek function main() println("JuMP sample1") model = Model(solver=MosekSolver()) m = 10 n = 5 A = [0.832268 0.540747 0.784667 0.355357 0.928707; 0.802036 0.504541 0.430144 0.411091 0.387708; 0.645349 0.0851049 0.71396 0.470207 0.593221; 0.0901346 0.438501 0.871861 0.909309 0.0145537; 0.38814 0.446732 0.0787192 0.411324 0.324244; 0.832039 0.716916 0.345928 0.612039 0.126577; 0.860024 0.302069 0.188148 0.204705 0.9435; 0.426323 0.48622 0.157624 0.737819 0.173054; 0.212276 0.323749 0.50823 0.242557 0.0126005; 0.70361 0.823343 0.116585 0.940804 0.767688] b=[0.976362; 0.0278185; 0.16024; 0.250936; 0.730025; 0.414424; 0.468235; 0.272898; 0.253627; 0.824444] println(A) println(b) @variable(model, 0.0<=x[1:size(A,2)]<=1.0) @objective(model, Min, sum((A*x-b).^2)) println("The optimization problem to be solved is:") println(model) # Shows the model constructed in a human-readable form status = solve(model) println("Objective value: ", getobjectivevalue(model)) println("x:",getvalue(x)) end Base.@ccallable function julia_main(ARGS::Vector{String})::Cint main() return 0 end if contains(@__FILE__, PROGRAM_FILE) main() end end
https://github.com/AtsushiSakai/JuliaSamples/blob/master/juliac/sample1/hello.jlgithub.com
下記が通常のjuliaコマンドで実行した結果です。
今回はbashのtimeをjuliaコマンドの前につけて、
JITコンパイルを含めた計算時間計測しています。
下記のターミナルのスクリーンショットを見るとわかるとおり、
プログラム起動から終了まで、通常のjuliaコマンドでは11秒かかっているのがわかります。
一方、下記の結果はjuliacでコンパイルした実行ファイルを
実行した場合の結果です。
最適化の結果は同じですが、
0.8秒でプログラム実行から終了までが完了しており、
非常に高速に実行できていることがわかります。
juliacを使う時の注意点
自分がハマった注意点をまとめておきます。
現在の所、コンパイルするjuliaのコードはhello.jlという名前でないとCコードを変更する必要がある
コンパイルするjuliaのファイル名を
サンプルプログラムのhello.jl以外にしたい場合は、
下記のコードのライブラリファイル名のhelloの部分を
ファイル名に変更する必要があります。
(PRを出すべきか。。)
PyCallが含まれたコードはコンパイルできない
当たり前ですが、
PyCallで呼ばれたコードはJuliaのコードではないので、
コンパイルすることができません。
自分はPyCallでmatplotlibをよく使うので、
Julia製のmatplotlibがあれば、
乗り換えるチャンスかもしれません。
JuMPのコードをコンパイルする時は、配列のインデックスの始まりは1にする
もし、JuMPのコードをコンパイルしたい場合、
マクロ内の配列のインデックスは、1始まりにする必要があります。
通常のJuliaの実行方法では0始まりのコードを書けますが、
juliacでコンパイルしようとすると、
The applicable method may be too new: running in world age 26123, while current world is 27569. Closest candidates are: _to_cartesian(::Any, ::Any, ::Any...) at /Users/atsushisakai/.julia/v0.6/JuMP/src/JuMPArray.jl:54 (method too new to be called from this world context.)
というようなエラーが出てコンパイルできません。
インデックスを1始まりにすると治ります。
JuMPの非線形最適化のマクロがあるとコンパイルできない
JuMPは非線形最適化も解くことができますが、
現状、コンパイルはできますが、
実行するとセグフォで落ちてしまいます。
コードの中で、@NLconstraintが入っているとだめなようです。
参考資料
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。