LLVM の OpenMP* サポート、参加方法、FAQ

同カテゴリーの次の記事

シングルセル遺伝子解析の高速化

この記事は、GitHub* の LLVM で公開されている 2022年10月19日現在の「Support, Getting Involved, and FAQ」を、インテル社の許可を得て iSUS (IA Software User Society) が翻訳した日本語参考訳です。原文は更新される可能性があります。原文と翻訳文の内容が異なる場合は原文を優先してください。


ディスカッション・フォーラム (ランタイム – OpenMP*) (英語) に問い合わせたり、定例ミーティングに参加することができます。よくある質問については、FAQ を参照してください。

定例ミーティング

LLVM の OpenMP* に関する技術ミーティング

Flang の OpenMP* に関する技術ミーティング

よくある質問 (FAQ)

注意
FAQ は現在作成中であり、ほとんどはまだ公開されていません。皆様のご意見およびご感想をディスカッション・フォーラム (ランタイム – OpenMP) (英語) に投稿してください。

ウェブページやその他の部分に対するパッチを提供するには?

すべてのパッチは、通常 LLVM レビュープロセス (英語) を経て公開されます。

OpenMP* GPU オフロード対応コンパイラーをビルドするには?

OpenMP* オフロード・コンパイラーを効率良くビルドするには、LLVM をビルドする際に CMake に LLVM_ENABLE_RUNTIMES=”openmp” オプションを追加するだけです (LLVM のビルドに関する一般的な情報は、こちら (英語) を参照してください)。OpenMP* のターゲットになるすべてのバックエンドが有効であることを確認します。デフォルトでは、Clang がバックエンドとしてビルドされます。LLVM_ENABLE_RUNTIMES=”openmp” でビルドした場合、OpenMP* は LLVM_ENABLE_PROJECTS をデフォルトで有効にするため、明示的に有効にすべきではありません。

NVIDIA* オフロードについては、「OpenMP* NVIDIA* オフロード対応コンパイラーをビルドするには?」を参照してください。AMDGPU オフロードについては、「OpenMP* AMDGPU オフロード対応コンパイラーをビルドするには?」を参照してください。

注意
オフロードコードを生成するコンパイラーは、OpenMP* デバイスのランタイムをビルドするコンパイラーと同一 (バージョン) である必要があります。OpenMP* ホストランタイムは、異なるコンパイラーでビルドすることができます。

OpenMP* NVIDIA* オフロード対応コンパイラーをビルドするには?

OpenMP* アプリケーションを実行するには、CUDA* SDK が必要です。

ビルドするマシンがターゲットマシンではない場合や、利用可能な GPU の自動検出に失敗した場合には、以下を設定する必要があります。

  • CLANG_OPENMP_NVPTX_DEFAULT_ARCH=sm_XX
    XX は GPU のアーキテクチャー (80 など)。
  • LIBOMPTARGET_NVPTX_COMPUTE_CAPABILITIES=YY
    YY は GPU の計算能力を示す数値 (75 など)。

OpenMP* AMDGPU オフロード対応コンパイラーをビルドするには?

LLVM ツールチェーンのビルドと OpenMP* アプリケーションの実行には、ROCm* (英語) ツールチェーンのサブセットが必要です。ROCm* を CMake の find_package で検出できる場所にインストールするか、必要なサブ・コンポーネント ROCt と ROCr をソースからビルドします。

使用するコンポーネントは、roct (ROCT-Thunk-Interface) と rocr (ROCR-Runtime) の 2 つです。roct は Linux* ドライバーのユーザー空間であり、Linux* カーネルに含まれるドライバーを呼び出します。OpenMP* から見た rocr 実装の詳細です。rocr は HSA (英語) を実装します。

SOURCE_DIR=same-as-llvm-source # 例: OpenMP* の次の llvm-project をチェックアウト
BUILD_DIR=somewhere
INSTALL_PREFIX=same-as-llvm-install

cd $SOURCE_DIR
git clone git@github.com:RadeonOpenCompute/ROCT-Thunk-Interface.git -b roc-4.2.x \
  --single-branch
git clone git@github.com:RadeonOpenCompute/ROCR-Runtime.git -b rocm-4.2.x \
  --single-branch

cd $BUILD_DIR && mkdir roct && cd roct
cmake $SOURCE_DIR/ROCT-Thunk-Interface/ -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \
  -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
make && make install

cd $BUILD_DIR && mkdir rocr && cd rocr
cmake $SOURCE_DIR/ROCR-Runtime/src -DIMAGE_SUPPORT=OFF \
  -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_SHARED_LIBS=ON
make && make install

IMAGE_SUPPORT は clang で rocr をビルドする必要があり、OpenMP* では使用されません。

CMake の find_package が rocr パッケージを検出すると、LLVM は bin/amdgpu-arch ツールをビルドします。このツールは、実行時にローカルシステムで GPU を認識すると、gfx906 などの文字列を表示します。LLVM はまた、rocr にリンクされる共有ライブラリー libomptarget.rtl.amdgpu.so もビルドします。

これらのライブラリーをインストールしてから、LLVM をビルドしてインストールし、次のコマンドを試してください。

clang -O2 -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa example.c -o example && ./example

OpenMP* AMDGPU オフロードの既知の問題は?

libomp.so と libomptarget.so を検出するため、LD_LIBRARY_PATH または rpath/runpath を設定する必要があります。

libc は存在しないため、malloc と printf は使用できません。libm は rocm デバイス・ライブラリーで実装されており、’-lm’ でリンクされると rocm デバイス・ライブラリーが検索されます。

Radeon* VII (gfx906) 用のドライバーの一部のバージョンでは、環境変数 ‘export HSA_IGNORE_SRAMECC_MISREPORT=1’ を設定しないとエラーが発生します。

これは、LLVM に最近追加されたドライバーであり、ROCm や AOMP で従来から出荷されているドライバーとは実装が異なります。初期の採用者はバグに遭遇するでしょう。

オフロードに使用される LLVM コンポーネントは何ですか? それはどこにありますか?

ターゲットオフロード用にコンパイルされた実行ファイルが使用するライブラリーには以下があります。

  • libomp.so はホストの OpenMP* ランタイムです。
  • libomptarget.so は、ターゲットに依存しないターゲットオフロード用の OpenMP* ランタイムです。
  • libomptarget.so によってロードされるプラグイン
    • libomptarget.rtl.amdgpu.so
    • libomptarget.rtl.cuda.so
    • libomptarget.rtl.x86_64.so
    • libomptarget.rtl.ve.so
    • その他
  • これらのプラグインの依存関係 (例: nvptx/amdgpu 用の cuda/rocr)

コンパイルされた実行ファイルは、ホストランタイム (libomp.so) およびターゲット・オフロード・ランタイム (libomptarget.so) と動的にリンクされています。これらのライブラリーは、他のダイナミック・ライブラリーと同様に、実行ファイル向けに rpath または runpath を設定するか、LD_LIBRARY_PATH を設定してシステムの検索パスに追加することで検出できます。

libomptarget.so の rpath または runpath (システムのデフォルト) は $ORIGIN に設定されており、プラグインは隣接して配置されるため、環境変数が設定されていなくてもプラグインを検出できます。LD_LIBRARY_PATH が設定されている場合、どのプラグインを検出してオーバーライドするかどうかは、システムが -Wl-rpathRPATH または RUNPATH として扱うかどうかに依存します。

プラグインは、プラグインの依存関係を検出しようとします。

コンパイラーのビルド時に CMake が CUDA* プラグインを検出すると、libcuda と動的にリンクされます。それ以外は、libcuda.so を dlopen します。rpath は設定されません。

コンパイラーのビルド時に CMake が amdgpu プラグインを検出すると、ROCt とリンクされます。それ以外は、libhsa-runtime64.so を dlopen します。rpath が $ORIGIN に設定されているため、同一ディレクトリーに libhsa-runtime64.so をインストールすることで、環境変数を設定しなくても場所を検出できます。

さらに、deviceRTL というコンパイラー・ランタイム・ライブラリーがあります。これは、ほとんどの共通コードから、アーキテクチャー固有のビットコード・ライブラリー (libomptarget-nvptx-sm_70.bc など) にコンパイルされます。

Clang と deviceRTL のインターフェイスは頻繁に変更されるため、密接に一致させる必要があります。同じ monorepo チェックアウトで両方を使用することを強く推奨します。

環境変数を使用してコンポーネントを選択するホスト側とは異なり、clang の lib ディレクトリーにある deviceRTL が優先されます。deviceRTL が存在しない場合にのみ、LIBRARY_PATH 環境変数を検索して、正しい名前のビットコード・ファイルを検出します。clang に --libomptarget-nvptx-bc-path または --libomptarget-amdgcn-bc-path フラグを渡すことでこれをオーバーライドし、使用するディレクトリーや適切なビットコード・ファイルを指定することができます。

OpenMP* のオフロード機能は、プリパッケージ版の LLVM リリースで動作しますか?

現時点では、答えはいいえです。詳細は、「OpenMP* GPU オフロード対応コンパイラーをビルドするには?」を参照してください。

OpenMP* のオフロード機能は、OS の一部として配布されるパッケージで動作しますか?

現時点では、答えはいいえです。詳細は、「OpenMP* GPU オフロード対応コンパイラーをビルドするには?」を参照してください。

Clang は GPU 上の OpenMP* target で <math.h> と <complex.h> 演算をサポートしますか?

はい、LLVM/Clang は GPU 向けにコンパイルされた OpenMP* target 領域内で、数学関数や複素数演算に対応しています。

Clang は、C の math.h や complex.h、C++ の cmath や complex、または同様のヘッダーがアプリケーションでインクルードされる際に、最初に検出されるラッパーヘッダーのセットを提供します。これらのラッパーは、ターゲットデバイス固有の環境を設定した後、対応するヘッダーファイルのシステムバージョンをインクルードします。インクルードされるシステムヘッダーは、アーキテクチャーやオペレーティング・システムによって異なり、ターゲットデバイスのアーキテクチャーにかかわりなく、target 領域で利用するプリプロセッサー、変数、関数定義が含まれる場合があります。ただし、sin などのように特殊なデバイスのバージョンを必要とする関数や、__umul64hi のように特定のデバイスでしか利用できない機能もあります。各アーキテクチャーで数学演算と複素数演算のネイティブサポートを提供するため、Clang はネイティブ数学関数 (デバイスベンダーが提供するものなど) を OpenMP* の [begin/end] declare variant でラップします。これらの関数は、ホストバージョンの代わりに使用され、ホストのみの変数や関数定義はそのまま利用できます。複素数演算や関数も同様のメカニズムでサポートされます。このサポートは、LLVM/Clang を介してユーザーに公開される OpenMP* の [begin/end] declare variant コンテキスト・セレクターの拡張機能 (英語) を必要とすることに注意してください。

ターゲットデバイスへのメモリー・マッピングのエラーをデバッグする方法はありますか?

そのようなエラーをデバッグする実験的な方法として、リモート・プロセス・オフロード (英語) があります。libomptarget.rtl.rpc.soopenmp-offloading-server を使用することで、ホスト CPU 上のプロセス間でメモリーを明示的に転送し、その間にサニタイザーを実行してエラーをキャッチできます。

アプリケーションを実行すると「Named symbol not found (名前付きシンボルが見つかりません)」と表示されアボートするのはなぜですか?

これは、スタティック・ライブラリーを使用して OpenMP* オフロードを実行しようとしたことが原因である可能性が高いです。スタティック・ライブラリーにはデバイスコードが含まれていないため、ランタイムが target 領域を実行しようとしても検出できず、次のようなエラーが出力されます。

CUDA error: Loading '__omp_offloading_fd02_3231c15__Z3foov_l2' Failed
CUDA error: named symbol not found
Libomptarget error: Unable to generate entries table for device id 0.

現在、この問題の唯一の解決策は、アプリケーションのビルドでスタティック・ライブラリーを使用しないことです。

OpenMP* オフロードで動的にリンクされたライブラリーを使用できますか?

動的にリンクされたライブラリーは、ライブラリーとアプリケーション間でデバイスコードが分離されていない場合にのみ使用できます。共有ライブラリー内のデバイスコードで宣言されたものは、リンクされるとアプリケーションから見えなくなります。

古いホスト・コンパイラーを使用して OpenMP* オフロード対応コンパイラーをビルドするには?

OpenMP* ランタイムを有効にすると、2 段階のビルドが行われます。ホスト・コンパイラーがシステムのコンパイラーと異なる場合、CMake 変数 GCC_INSTALL_PREFIX を設定して、Clang がビルドの第 2 ステージで適切な GCC ツールチェーンを検出できるようにする必要があるかもしれません。

例えば、システムにインストールされている GCC が古すぎて LLVM をビルドできず新しい GCC を使用する場合、CMake 変数 GCC_INSTALL_PREFIX を設定して、第 2 ステージで使用する GCC を Clang に通知します。

CMake プロジェクトに OpenMP* オフロードのサポートを含めるには?

現在、LLVM では OpenMP* target オフロード向けの実験的な CMake 検索モジュールが提供されています。これは、コンパイラー向けに OpenMP* target オフロードのサポートを検出します。OpenMP* target のオフロードに必要なフラグは、成功すると OpenMPTarget::OpenMPTarget_<device> ターゲット、または OpenMPTarget_<device>_FLAGS 変数に読み込まれます。現在サポートされるデバイスは、AMDGPUNVPTX です。

このモジュールを使用するには、CMake のモジュールパスにパスを追加して、find_package を呼び出します。このモジュールは、デフォルトで OpenMP* のインストール時に同時にインストールされます。OpenMP* オフロードのサポートをアプリケーションで有効にするには、わずかな変更で済みます。

cmake_minimum_required(VERSION 3.13.4)
project(offloadTest VERSION 1.0 LANGUAGES CXX)

list(APPEND CMAKE_MODULE_PATH "${PATH_TO_OPENMP_INSTALL}/lib/cmake/openmp")

find_package(OpenMPTarget REQUIRED NVPTX)

add_executable(offload)
target_link_libraries(offload PRIVATE OpenMPTarget::OpenMPTarget_NVPTX)
target_sources(offload PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Main.cpp)

このモジュールを使用するには、CMake バージョン 3.13.3 以降が必要です。 C と C++ 言語に対応しており、今後 Fortran もサポートされる予定です。コンパイラーは Clang が最適ですが、このモジュールは IBM や GNU* などほかのコンパイラーでも動作するはずです。

「エントリー関数のスタックサイズを静的に決定できない」とはどういう意味ですか?

これは、オフロード領域が複雑すぎる場合に、NVIDIA* のツールが出力する警告です。通常、CUDA* ツールは各スレッドのスタックメモリー使用量を静的に決定しようとします。これにより、カーネルが起動した際に、各スレッドは必要とするメモリーのみを保持できます。カーネルの制御フローが複雑で、再起呼び出しや入れ子になった並列処理を含む場合、この方法では失敗する可能性があります。この警告が表示された場合、カーネルが実行中にスタックを使い果たし、クラッシュする可能性があることを意味します。その場合、環境変数 LIBOMPTARGET_STACK_SIZE を使用してスタックサイズを増やすことができます。

関連記事