目次
- 目次
- はじめに
- パッケージのロードをsysimageで高速化する
- 関数の初期実行をsysimagesで高速化する
- REPL用のデフォルトパッケージをロードしたsysimageを作る便利関数
- 参考資料
- MyEnigma Supporters
はじめに
これまでPythonでやってきたことを、
Juliaで置き換えようとすると、まず初めにぶつかるのが、
Juliaのコードにおけるライブラリのロードや、
初めて使う関数の計算が遅いなど、
いわゆる初回実行が遅い問題が気になってきます。
(英語ではこの問題をlatencyと呼んでいるそうです
)
Juliaは基本的に初回実行時にコードをJITコンパイルして実行するため、
動的言語でありながら、高速な実行を実現できているので、
この初回実行が遅いのはある程度はしょうがないのかなと思います。
しかし、
毎回使うライブラリのロードや
変更が不要な関数が、
毎回遅いのはなんとかならないのかなと思っていました。
そこで、この問題の一つの解決策がPackageCompiler.jlです。
概要はこちらの動画を参照していただけるとわかると思いますが、
sysimageと呼ばれるJuliaのセッション保存機能を使って、
いつも利用するライブラリをロードした状態や、
関数のJITコンパイル結果を保存することにより、
そのsysimageを使って、juliaコードを実行したときに、
初回実行を高速化することができます。
(Julianのsysimageに関しては、こちらを参照ください
今回の記事では、このPackageCompiler.jlとsysimagesを使うことで、
Juliaのコードの初回実行を高速化する方法を簡単に紹介したいと思います。
パッケージのロードをsysimageで高速化する
今回は、自分が最も使用頻度の高いグラフPlotパッケージである
PyPlotのロードを高速化してみたいと思います。
まずはじめに、特になにもせずに
Juliaのセッションを立ち上げて、PyPlotをロードすると、
下記のように5秒ほど時間がかかりました。
続いて、sysimageを作成してみます。
まずはじめにPackageCompiler.jlをインストールして
using Pkg;Pkg.add("PackageCompiler");
続いて、PackageCompilerのcreate_sysimageという関数を使って、
sysimageのファイルを作成してみます。
using PackageCompiler create_sysimage([:PyPlot];sysimage_path="sysimage_plots.dylib")
1つ目引数のリストに、
sysimageに含めたいパッケージのsymbol
(パッケージ名の前に:をつけたもの)を入力します。
今回はPyPlotのみを含めるため、:PyPlotのみですが、複数含めることも可能です。
2つ目のsysimage_pathには、作成するsysimageファイルのパスを指定します。
自分はMac OSを使っているので、.dylibファイルを生成していますが、
Linuxの場合は.so, windowsの場合は.dllファイルを指定する必要があります。
他にもデフォルトのsysimageを置き換えたりできます。
詳細は上記のドキュメントを参照ください
このコードを実行すると、自分の環境では2分ぐらいかかって、
sysimageのファイルができました。
(ちなみにサイズは166MBありました)
続いて、このsysimageを使って、Juliaのセッションを立ち上げてみます。
ターミナルからjuliaコマンドでREPLを起動するときに、
--sysimageで先程のファイルのパスを指定するだけです。
$ julia --sysimage sysimage_plots.dylib
また、-Jでも同じようにsysimageを指定できます。
$ julia -Jsysimage_plots.dylib
そして、先程と同じようにPyPlotをロードしてみると、
1万倍以上速くパッケージをロードできていることがわかります。
これからREPLを起動するときに、
毎回のsysimageを指定して起動するだけで、
同じ効果が得られます。
関数の初期実行をsysimagesで高速化する
上記の方法で、パッケージのロードは高速化されましたが、
ロードしたパッケージの関数を実行すると、
残念ながら、初期実行はまだ遅いままです。
これはパッケージのロード情報はsysimageに含まれていますが、
各関数のコンパイル情報はsysimageに含まれていないからです。
PackageCompiler.jlを使うと、よく使う関数のコンパイル情報も
sysimageに含むことができます。
まずはじめに、よく使う関数を実行するjulia scriptを作成します。
今回は、PyPlotのplotを高速化したいので、
plotのみを実行する下記のようなシンプルな
Julia script (precompile_pyplot.jl)を作成しました。
using PyPlot plot(rand(10),rand(10))
続いて、PackageCompilerのcreate_sysimage関数で、
先程の入力に加えて、
precompile_execution_fileという引数で先程のscriptを指定して
sysimageを作成します。
using PackageCompiler create_sysimage([:PyPlot];sysimage_path="sysimage_plots.dylib", precompile_execution_file="precompile_pyplot.jl")
これにより、PyPlotのロード情報だけでなく、
指定したJulia scriptを実行した結果として生成される
plot関数のコンパイル情報も含まれたsysimageが生成されます。
(ちなみに、precompile_execution_file以外にも、
precompile_statements_fileという引数もありますが、
この違いに関しては下記を参照ください
そして、先程と同様に、生成されたsysimageを使って、
PyPlotをロードして、plotを実行すると、
先程の結果を比べて、一回目の実行が3倍ほど高速化されたのがわかります。
ただ残念ながら、おそらく何かしらの初回実行処理がまだ必要なため、
一回目から、二回目の実行と同じぐらい速く実行するのはまだ難しそうです。
REPL用のデフォルトパッケージをロードしたsysimageを作る便利関数
自分の場合、REPL使ってJuliaのコードを開発するときは、
常に、
は、常にいつでも使える状態にしておきたいので、
これらのモジュールをロードした状態でのsysimageを
簡単にデフォルトで利用したいと思いました。
そこで、下記のようなコードをstartup.jlに追加し、
install_default_pkgs()
とすると、指定したデフォルトパッケージをインストールし、
generate_default_sysimage()
とすると、
簡単にそれらのパッケージをロードしたsysimageを作成し、
デフォルトのものに置き換えるようにしました。
atreplinit() do repl @eval using Pkg DEFAULT_PKGS = [:OhMyREPL, :Revise, :BenchmarkTools, :PyPlot] function install_default_pkgs() println("Installing default packages to latest ones...") for pkg in DEFAULT_PKGS Pkg.add(String(pkg)) end end function generate_default_sysimage() println("Updating default packages to latest ones...") for pkg in DEFAULT_PKGS println("Updating $(String(pkg))") Pkg.update(String(pkg)) end create_sysimage(DEFAULT_PKGS;replace_default=true) println("Done!!. If you want to restore default sysimage, run `restore_default_sysimage()`") end end
これで、たまに時間があるときに、generate_defalut_sysimage()
を呼んで、
sysimageを更新し、あとは高速に開発用のデフォルトパッケージを利用できます。
参考資料
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。