システムイメージの作成
Julia システムイメージの作成
Julia は Base
モジュールが含まれる準備済みシステムイメージ sys.jl
と共に配布されます。このファイルは可能な限り多くのプラットフォームで共有ライブラリ sys.{so,dll,dylib}
へと事前コンパイルされ、これによってスタートアップ時間が大幅に短縮されます。Julia システムに事前コンパイルされたシステムイメージファイルが付属しないときは、Julia の DATAROOTDIR/julia/base
フォルダーにあるソースファイルからコンパイルされたイメージファイルを生成できます。
システムイメージの作成が便利な理由はいくつかあります。ユーザーは次のことが行えます:
- 事前コンパイルしたシステムイメージ (共有ライブラリ) が付属しないプラットフォームでは、ビルドすることでスタートアップ時間を短縮できます。
Base
を改変してからシステムイメージを再ビルドすれば、次に Julia を開始したときに新しいBase
が使われます。- システムイメージにパッケージをインクルードする
userimg.jl
ファイルを用意すれば、スタートアップ環境にパッケージを埋め込んだシステムイメージを作成できます。
PackageCompiler.jl パッケージにはこの処理を自動化するための便利なラッパー関数が含まれます。
複数のマイクロアーキテクチャに対応したシステムイメージ
同じ命令セットアーキテクチャ (ISA) を持つ異なる複数の CPU マイクロアーキテクチャに対応したシステムイメージをまとめてコンパイルすることが可能です。全てのアーキテクチャで共有の関数に挿入された最小限のディスパッチ処理を使って同じ関数の異なるバージョンを呼び出せるようにすることで、ISA 拡張などのマイクロアーキテクチャの機能の活用できます。実行時には利用可能なCPU 機能に基づいて最も優れた性能を出すバージョンが自動的に選択されます。
複数のシステムイメージターゲットの指定
複数のマイクロアーキテクチャに対応したシステムイメージを有効化するには、システムイメージをコンパイルするときに複数のターゲットを指定します。これは make の JULIA_CPU_TARGET
オプションまたはコンパイルを手動で実行するときのコマンドライン引数 -C
で行います。ターゲットを指定する構文は CPU の名前に ,
で区切った機能の名前を並べるというもので、複数のターゲットは ;
で区切ります。LLVM がサポートする全ての機能がサポートされ、先頭に -
を付ければ無効化できます (LLVM の構文に合わせて +
を先頭に付けても構いませんが、無視されます)。また関数のクローンを制御するための特別な機能がいくつかサポートされます:
-
clone_all
デフォルトでは、マイクロアーキテクチャの機能から恩恵を受ける可能性が非常に高い関数だけがクローンされます。しかしターゲットに対して
clone_all
が指定されると、システムイメージに含まれる全ての関数がターゲットごとにクローンされます。否定形-clone_all
を使うと組み込みのヒューリスティックを使ったクローンを完全に無効化できます。 -
base(<n>)
n
は非負の整数を表します (base(0)
,base(1)
, ...)。デフォルトでは、部分的にクローンが行われる (clone_all
でない) ターゲットのクローンされない関数で使われるのはデフォルトのターゲット (指定されなければ最初のターゲット) に対する関数です。この振る舞いを変更するためにあるのがbase(<n>)
オプションです。指定すると、デフォルトの0
番目ではなくn
番目のターゲットがベースターゲットとして使われるようになります (添え字はゼロ始まり)。ベースターゲットは0
またはclone_all
ターゲットである必要があり、clone_all
でないターゲットをベースに指定するとエラーが発生します。 -
opt_size
このオプションを指定すると、対応するターゲットに対する関数が実行時性能に大きな影響がないときサイズに関して最適化されるようになります。これは GCC と Clang における
-Os
オプションに対応します。 -
min_size
このオプションを指定すると、対応するターゲットに対する関数がたとえ実行時性能に大きな影響が出る可能性がある場合でもサイズに関して最適化されるようになります。これは Clang の
-Oz
オプションに対応します。
例えば執筆時点において、https://julialang.org/ からダウンロードできる x86_64
に対する Julia バイナリは次の文字列を使って作成されています:
generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)
この文字列は三つのターゲットを持ったシステムイメージを作成します: 一つは x86_64
プロセッサに対する汎用的なもの、一つは全ての関数を明示的にクローンする (そしてxsaveopt
を用いない) sandybridge
ISA に対するもの、最後の一つは sandybridge
のシステムイメージバージョンをベースとして rdrnd
を除いた haswell
ISA に対するものです。生成されたシステムイメージを読み込む Julia 実装は最初にホストプロセッサを確認して適合する CPU 機能フラグを調べ、最も上位の ISA レベルを有効化します。ベースレベル (generic
) が cx16
命令を必要とすることに注意してください。一部の仮想化ソフトウェアはこの命令を無効化しているので、このシステムイメージを読み込むには手動で有効化する必要があります。この代わりに generic,-cs16
をターゲットとしてシステムイメージを生成すれば互換性が向上しますが、性能や安定性の問題が発生する可能性があります。
実装の概要
実装に関連する様々な要素を簡単に説明します。それぞれの要素についてさらに詳しくはコードのコメントを参照してください。
-
システムイメージのコンパイル
コードのパースと関数のクローンの判断は
src/processor*
で行われます。クローンの判断にはループ・SIMD 命令・その他の数学演算 (fastmath, fma, muladd) の有無が使われ、この情報は実際のクローンを行うsrc/llvm-multiversioning.cpp
に渡されます。クローンとディスパッチスロットの追加がどのように行われるかはMultiVersioning::runOnModule
のコメントを参照してください。これに加えて、このパスはランタイムがシステムイメージの読み込みと初期化を正しく行うためのメタデータの生成も行います。利用可能なメタデータの詳細な説明はsrc/processor.h
にあります。 -
システムイメージの読み込み
システムイメージの読み込みと初期化はシステムイメージを生成するときに保存されたメタデータを
src/processor*
にあるコードでパースすることで行われます。ホストが持つ機能の検出と選択は ISA に基づいてsrc/processor_*.cpp
で行われます。ターゲットの選択では大きなベクトルレジスタサイズや機能の数よりも CPU 名の完全なマッチが優先されます。この処理の概要はsrc/processor.cpp
にあります。