Julia - PackageCompiler.jl を使って Plots.jl を早くする

PackageCompiler.jl の README によるとついに Plots.jl がコンパイルできるようになったらしいので試してみました。

github.com

環境

各パッケージのバージョンは最後に記載の Manifest.toml, Project.toml をご覧ください。 Unix系のOSならばこれらのファイルを使って今回の私の結果を再現できると思います。

Plots.jl をコンパイルする

Windows の場合は管理者モードで Julia を起動して ImageMagick も入れる。 macOS の場合は事前に gcc を入れておく。

import Pkg
Pkg.add.(["PackageCompiler", "Plots"]) # mac の場合はこの後に build しておくと良さそうです。
using PackageCompiler
compile_package("Plots", force=false)

無事に終了すると次のように出ます。

All done
┌ Info: Not replacing system image.
└ You can start julia with `julia -J /home/goropikari/.julia/packages/PackageCompiler/oT98U/sysimg/sys.so` at a posix shell to load the compiled files.
"/home/goropikari/.julia/packages/PackageCompiler/oT98U/sysimg/sys.so"

指示に従い

julia -J /home/goropikari/.julia/packages/PackageCompiler/oT98U/sysimg/sys.so

で Julia を起動します。こうすると Plots をコンパイルした system image を読み込んできます。 引数取らずに普通に Julia を起動するとデフォルトの system image で起動します。

スピードを比較

以下のコードを3回実行し、その実行時間を計測しました。

julia> @time begin
           using Plots
           plot(rand(10))
       end

加えて、PackageCompiler を使うと Plots の読み込み等は早くなりますが、Julia 本体の起動が遅くなるので Julia の起動に掛かる時間も計測しました(Linux のみ)。

Linux

Machine Spec

julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i5-4460T CPU @ 1.90GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, haswell)

デフォルト

  • コードの実行時間
31.221949 seconds
28.103826 seconds
30.471024 seconds
平均 29.932 seconds
  • Julia の起動までに掛かる時間

計測方法は以下のよう。

time julia -e 'exit()'

結果

0m0.178s
0m0.182s
0m0.166s
平均 0.175 seconds

PackageCompiler

  • コードの実行時間
0.003226 seconds
0.003102 seconds
0.003285 seconds
平均 0.003204 seconds
  • Julia の起動までに掛かる時間
0m1.418s
0m1.405s
0m1.411s
平均 1.411 seconds

Windows 10

ArchLinux とデュアルブートしている Win7 で検証したかったのですが、そもそもコンパイルに失敗したので別機で検証しました。

Machine Spec

julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i3-3110M CPU @ 2.40GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, ivybridge)

デフォルト

  • コードの実行時間
44.759472 seconds
42.556326 seconds
37.836190 seconds
平均 41.717329 seconds

PackageCompiler

  • コードの実行時間
2.274401 seconds
2.682438 seconds
2.648822 seconds
平均 2.535220 seconds

macOS Mojave

Machine Spec

julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin14.5.0)
  CPU: Intel(R) Core(TM) i7-4771 CPU @ 3.50GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, haswell)

デフォルト

  • コードの実行時間
19.305938 seconds
19.382959 seconds
20.343827 seconds
平均 19.677575 seconds
  • Julia の起動までに掛かる時間
0m0.172s
0m0.185s
0m0.177s
平均 0.178s

PackageCompiler

  • コードの実行時間
1.168776 seconds
1.226039 seconds
1.186678 seconds
平均 1.193831 seconds
  • Julia の起動までに掛かる時間
0m1.098s
0m1.117s
0m1.123s
平均 1.113s

Jupyter Notebook に Kernel を追加する

コンパイル時に force=true にしていないとデフォルトで読み込む system image がコンパイルしたものにならないので、このままでは jupyter を使ったとき Plots の読み込みを早くすることができません。

そこでコンパイルした system image を読み込めるように kernel を追加します。

※注意: ここでは IJulia で Jupyter を入れたとします。 Anaconda 等でも同様にできると思います。

Linux

~/.local/share/jupyter/kernelsjulia-1.1 というフォルダがあるのでこれをコピーします。今回はフォルダ名を julia-1.1-plots としました。

cp -r ~/.local/share/jupyter/kernels/julia-1.1 ~/.local/share/jupyter/kernels/julia-1.1-plots

次に julia-1.1-plots 中の kernel.json-J/home/ユーザー名/.julia/packages/PackageCompiler/oT98U/sysimg/sys.so を以下のように追加します。 ついでに display_name も同じだと分かりづらいので適当に変えておきましょう。

{
  "display_name": "Julia 1.1.0 plot",
  "argv": [
    "/home/ユーザー名/.local/julia/julia-1.1.0/bin/julia",
    "-J",
    "/home/ユーザー名/.julia/packages/PackageCompiler/oT98U/sysimg/sys.so",
    "-i",
    "--startup-file=yes",
    "--color=yes",
    "/home/ユーザー名/.julia/packages/IJulia/4UizY/src/kernel.jl",
    "{connection_file}"
  ],
  "language": "julia",
  "env": {},
  "interrupt_mode": "signal"
}

f:id:goropikarikun:20190220181937p:plain

上手くいくとこのように kernel が追加されます。

Windows

C:\Users\ユーザー名\AppData\Roaming\jupyter\kernelsjulia-1.1 というフォルダがあるのでこれを複製します。今回はフォルダ名を julia-1.1-plots としました。

次に julia-1.1-plots 中の kernel.json-JC:\\Users\\ユーザー名\\.julia\\packages\\PackageCompiler\\oT98U\\sysimg\\sys.dll を以下のように追加します。 ついでに display_name も同じだと分かりづらいので適当に変えておきましょう。

{
  "display_name": "Julia 1.1.0 plot",
  "argv": [
    "C:\\Users\\ユーザー名\\AppData\\Local\\Julia-1.1.0\\bin\\julia.exe",
    "-J",
    "C:\\Users\\ユーザー名\\.julia\\packages\\PackageCompiler\\oT98U\\sysimg\\sys.dll",
    "-i",
    "--startup-file=yes",
    "--color=yes",
    "C:\\Users\\ユーザー名\\.julia\\packages\\IJulia\\4UizY\\src\\kernel.jl",
    "{connection_file}"
  ],
  "language": "julia",
  "env": {},
  "interrupt_mode": "message"
}

f:id:goropikarikun:20190220181937p:plain

上手くいくとこのように kernel が追加されます。

f:id:goropikarikun:20190220181720p:plain 試しにプロットしてみましたが、どうやら Jupyter 通すと実行速度が遅くなるようです。

まとめ

単純にパッケージの読み込み・グラフの描写に掛かる時間のみに着目すると PackageCompiler を使った場合、Linux ではデフォルトよりも9342倍早くなるという結果になりました。 PackageCompiler を使った場合は Julia の起動時間が遅くなりますが、それを考慮しても21倍早くなるという結果になりました。

mac, Windows の場合ではパッケージの読み込み・グラフの描写に掛かる時間のみに着目しても16倍程度しか早くならず Linux と比べるとインパクトが少ないですが、元が元なのでやって損はないです。

Julia の起動が遅いのは地味にストレスなのでプロットするか否かに応じて system image を変える方法で運用していくのが良いかと思います。毎回 Plots.jl を読み込むと言うなら

compile_package("Plots", force=true)

とするのが楽です。

参考

nextjournal.com

jupyter-client.readthedocs.io

Manifest.toml, Project.toml

PackageCompiler_Plots