System Image Building

Building the Julia system image

Juliaは、Baseモジュールの内容を含む事前解析されたシステムイメージを持っており、これをsys.jiと呼びます。このファイルは、可能な限り多くのプラットフォームで、sys.{so,dll,dylib}という名前の共有ライブラリに事前コンパイルされています。これにより、起動時間が大幅に改善されます。事前コンパイルされたシステムイメージファイルが付属していないシステムでは、JuliaのDATAROOTDIR/julia/baseフォルダーに含まれるソースファイルから生成することができます。

Juliaはデフォルトで利用可能なシステムスレッドの半分でシステムイメージを生成します。これは、JULIA_IMAGE_THREADS 環境変数によって制御できます。

この操作は複数の理由で便利です。ユーザーは:

  • プラットフォームにプリコンパイルされた共有ライブラリシステムイメージが付属していない場合、スタートアップ時間を改善するためにそれを構築します。
  • Baseを修正し、システムイメージを再構築して、次回Juliaを起動する際に新しいBaseを使用してください。
  • userimg.jlファイルを含めて、パッケージをシステムイメージに組み込み、スタートアップ環境に埋め込まれたパッケージを持つシステムイメージを作成します。

PackageCompiler.jl package には、このプロセスを自動化する便利なラッパー関数が含まれています。

System image optimized for multiple microarchitectures

システムイメージは、同じ命令セットアーキテクチャ(ISA)の下で複数のCPUマイクロアーキテクチャ用に同時にコンパイルできます。同じ関数の複数のバージョンが、異なるISA拡張や他のマイクロアーキテクチャ機能を活用するために、共有関数に最小ディスパッチポイントを挿入して作成される場合があります。最もパフォーマンスが良いバージョンは、利用可能なCPU機能に基づいてランタイムで自動的に選択されます。

Specifying multiple system image targets

マルチマイクロアーキテクチャシステムイメージは、システムイメージのコンパイル中に複数のターゲットを渡すことで有効にできます。これは、JULIA_CPU_TARGET メイクオプションを使用するか、コンパイルコマンドを手動で実行する際に -C コマンドラインオプションを使用することで行えます。複数のターゲットは、オプション文字列内で ; で区切られます。各ターゲットの構文は、CPU名の後にカンマで区切られた複数の機能が続きます。LLVMによってサポートされているすべての機能がサポートされており、機能は - プレフィックスで無効にできます。(+ プレフィックスも許可されており、LLVMの構文と一貫性を持たせるために無視されます)。さらに、関数クローンの動作を制御するために、いくつかの特別な機能がサポートされています。

Note

すべてのターゲットに対して、最初のターゲットを除いて clone_all または base(<n>) のいずれかを指定することは良い習慣です。これにより、どのターゲットがすべての関数をクローンしているか、どのターゲットが他のターゲットに基づいているかが明示的になります。これが行われない場合、デフォルトの動作はすべての関数をクローンせず、関数をクローンしない場合は最初のターゲットの関数定義をフォールバックとして使用することです。

  1. clone_all

    デフォルトでは、マイクロアーキテクチャの機能から最も恩恵を受ける可能性が高い関数のみがクローンされます。しかし、ターゲットに対して clone_all が指定されると、システムイメージ内の すべての 関数がターゲットのためにクローンされます。ネガティブ形式の -clone_all を使用すると、組み込みのヒューリスティックがすべての関数をクローンするのを防ぐことができます。

  2. base(<n>)

    ここで <n> は非負の数のプレースホルダーです(例:base(0)base(1))。デフォルトでは、部分的にクローンされた(すなわち clone_all ではない)ターゲットは、関数がクローンされていない場合、デフォルトターゲット(最初に指定されたもの)から関数を使用します。この動作は、base(<n>) オプションで異なるベースを指定することによって変更できます。n 番目のターゲット(0から始まる)がデフォルトのターゲット(0 番目のもの)の代わりにベースターゲットとして使用されます。ベースターゲットは 0 か、別の clone_all ターゲットでなければなりません。非 clone_all ターゲットをベースターゲットとして指定するとエラーが発生します。

  3. opt_size

    これは、実行時のパフォーマンスに大きな影響がない場合に、ターゲットの関数がサイズの最適化のために最適化されることを引き起こします。これは、-Os GCCおよびClangオプションに対応します。

  4. 最小サイズ

    これにより、ターゲットの関数がサイズ最適化され、実行時のパフォーマンスに大きな影響を与える可能性があります。これは -Oz Clangオプションに対応しています。

この執筆時点での例として、以下の文字列が julialang.org からダウンロード可能な公式の x86_64 Julia バイナリの作成に使用されています:

generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)

これは、3つの別々のターゲットを持つシステムイメージを作成します。1つは一般的な x86_64 プロセッサ用、1つは sandybridge ISA(xsaveopt を明示的に除外)で、すべての関数を明示的にクローンし、1つは sandybridge sysimg バージョンに基づく haswell ISA をターゲットにし、rdrnd も除外します。Julia 実装が生成された sysimg をロードすると、ホストプロセッサの CPU 機能フラグをチェックし、可能な限り高い ISA レベルを有効にします。基本レベル(generic)は cx16 命令を必要とし、これは一部の仮想化ソフトウェアでは無効になっており、generic ターゲットをロードするには有効にする必要があります。あるいは、より互換性を高めるために generic,-cx16 ターゲットで sysimg を生成することもできますが、これにより一部のコードでパフォーマンスや安定性の問題が発生する可能性があることに注意してください。

Implementation overview

これは実装に関与するさまざまな部分の簡単な概要です。各コンポーネントの詳細な実装については、コードコメントを参照してください。

  1. システムイメージのコンパイル

    src/processor* でパースとクローンの決定が行われます。現在、ループ、SIMD命令、または他の数学演算(例:fastmath、fma、muladd)の存在に基づいて関数のクローンをサポートしています。この情報は、実際のクローンを行う src/llvm-multiversioning.cpp に渡されます。クローンを行い、ディスパッチスロットを挿入する(これがどのように行われるかは MultiVersioning::runOnModule のコメントを参照)だけでなく、このパスはメタデータも生成し、ランタイムがシステムイメージを正しくロードおよび初期化できるようにします。メタデータの詳細な説明は src/processor.h にあります。

  2. システムイメージの読み込み

    システムイメージの読み込みと初期化は、システムイメージ生成中に保存されたメタデータを解析することによって src/processor* で行われます。ホスト機能の検出と選択の決定は、ISAに応じて src/processor_*.cpp で行われます。ターゲット選択は、正確なCPU名の一致、より大きなベクタレジスタサイズ、およびより多くの機能を優先します。このプロセスの概要は src/processor.cpp にあります。