LLVM/Clang で OpenMP* 4.0 機能を試してみる

同カテゴリーの次の記事

OpenMP* を使用中に変数アクセスでスレッドがクラッシュしないようにする

この記事は、インテル® デベロッパー・ゾーンに掲載されている「Testing Out OpenMP 4.0 Features in LLVM/Clang」の日本語参考訳です。


LLVM ツールに含まれる Clang コンパイラーには、OpenMP* サポートを追加する特別なブランチがあります。この記事では、Clang の OpenMP* 4.0 機能を試し、その結果について見てみます。

早速、Clang で OpenMP* 4.0 機能を試してみましょう。実際の作業に入る前に、Clang コンパイラーの OpenMP* バージョンをビルドするためのポイントについて説明します。こちらのビデオ (英語) も参考になります。まず、大きなメモリー容量が必要です。ここでは、8GB のサーバーを使用しました。2GB のサーバーも試しましたが、失敗しました。リンカーが正しく動作せず、次のようなエラーメッセージが出力されました。

collect2: error: ld terminated with signal 9 [Killed] (ld がシグナル 9 で終了しました)

仮想マシンのメモリーを増やすことでこの問題は解決しました。(スワップ領域を増やすことでも解決できるようですが、試しませんでした。)

このエラーが発生し、メモリーを増やした場合、次のような別のエラーが出力される可能性があります。

libclang.so: file not recognized: File format not recognized (ファイルを認識できません: ファイル形式を認識できません)

この 2 つ目のエラーは、最初のビルドでリンカーが失敗し、ファイルの一部のみがビルドされたことが原因です。そのため、メモリーを増やした後、既存の libclang.so ファイルを削除する必要があります。このファイルは、build/Debug+Asserts/lib ディレクトリーにあります。

削除後、再度 make コマンドでビルドを行います。(このほかにも、コンパイラー・ターゲットの推測に関するエラーが出力されることがあります。その場合は、build/Debug+Asserts/bin ディレクトリー以下の Clang 実行ファイルを削除しなければならないことがあります。ファイルサイズを確認し、0 の場合はファイルを削除してから、再度 make を実行してみてください。)

コンパイラーのビルドが完了したら、いくつかの OpenMP* 4.0 機能を使用してみましょう。ここでは、4.0 の新機能のみテストします。新機能の一覧は、クイック・リファレンス・カード (英語) で確認できます。4.0 の新機能は、機能名の横に 4.0 と表記されています。

ヒント: 機能が存在しない、あるいはプラグマ名が正しくない場合は直ぐに分かります。-fopenmp コンパイラー・オプションで OpenMP* 機能を有効にした場合、Clang コンパイラーは認識できない OpenMP* プラグマを検出するとエラーを出力します。以下はその一例です。

test1.cpp:6:13: error: expected an OpenMP directive (エラー: OpenMP* 宣言子を指定してください)

SIMD 操作のテスト

以下は、SIMD 宣言子の動作を確認するためのサンプルプログラムです。このプログラムは、この機能が動作するのを確認するためのものであって、(特に cout はスレッドセーフでないため) 有効な 処理は何も行いません。

#include <iostream>
#include <omp.h>
using namespace std;

int main() {
    #pragma omp simd
    for (int i=0; i<10; i++) {
        cout << i << endl;
    }
}

このコードは、問題なくビルドすることができます。SIMD 宣言子の後に for ループ以外のコードを配置すると、次のようなメッセージが出力されます。

test1.cpp:7:5: error: only for-loops are allowed for ‘#pragma omp simd’ (エラー: for ループのみ ‘#pragma omp simd’ で許可されています。)

for ループの SIMD 宣言子を次のように少し変更してみます。

#pragma omp for simd

この場合も、問題なくビルドし、実行することができます。次に、SIMD 関数宣言が動作するか確認します。

#pragma omp declare simd
int test(int x, int y) {
    return x * y;
}

このコードは問題なくビルドし、実行することができます。distribute 節を使用してみます。

#pragma omp distribute simd

これも、そして以下の節も問題なく動作します。

#pragma omp parallel for simd

SIMD 操作はすべて 問題なく動作することが分かりました。

カスタム・リダクション

OpenMP* 4.0 のもう 1 つの重要な機能は、独自のリダクションを宣言できることです。この機能については 2010 年頃から議論されてきました。結論から言うと、機能は実装されていますが、まだ実用レベルではありません。カスタム・リダクションに関する以前のブログで使用したテストプログラムをビルドしてみたところ、ビルドエラーになり、llvm.org へのバグレポートの送信に関する多量のメッセージが出力されました。reduction 節までは問題なくビルドできますが、reduction 節でコンパイラーがクラッシュします。私のコードに問題がある可能性もありますが、仮にそうであったとしても、コンパイラーはクラッシュせずにエラーを出力すべきでしょう。この件に関して新しい情報が入りましたら、皆さんにお知らせします。

まとめ

ここでは、OpenMP* 4.0 の新機能のうち重要なものを取り上げました。このほかにも、簡単に試すことができる機能があります。Clang の OpenMP* 4.0 への対応はほぼ整ったと言えるでしょう。コンパイラーがクラッシュする件については、今後新しい情報が入り次第、皆さんにお知らせします。target teams や cancellation など、クイック・リファレンス・カードに記載されているいくつかの機能を試してみると良いでしょう。何か大きな発見があった場合は、是非情報を共有してください。

関連記事

  • OpenMP* でベクトル化された並列ループを簡単に作成するOpenMP* でベクトル化された並列ループを簡単に作成する この記事は、インテル® デベロッパー・ゾーンに掲載されている「How to Create Vectorized, Multicore Loops in OpenMP with Ease」の日本語参考訳です。 OpenMP* のプラグマ宣言子を使用すると、1 […]
  • OpenMP* を使用中に変数アクセスでスレッドがクラッシュしないようにするOpenMP* を使用中に変数アクセスでスレッドがクラッシュしないようにする この記事は、インテル® デベロッパー・ゾーンに掲載されている「Stop Threads from Clashing Over Variables in OpenMP」の日本語参考訳です。 OpenMP* を使用して、複数のスレッドにコードブロックの複製 (ループや単純なブロック) […]
  • OpenMP* 実行環境関数OpenMP* 実行環境関数 この記事は、インテル® デベロッパー・ゾーンに掲載されている「OpenMP Execution Environment Functions」の日本語参考訳です。 OpenMP* 仕様には、omp.h ヘッダーファイルで宣言されたいくつかのランタイム関数が含まれています。これらのランタイム関数により、OpenMP* […]
  • OpenMP 4.0 を使用してプログラムで SIMD を有効にするOpenMP 4.0 を使用してプログラムで SIMD を有効にする この記事は、インテル® デベロッパー・ゾーンに掲載されている「Enabling SIMD in program using OpenMP 4.0」の日本語参考訳です。 OpenMP 4.0 仕様で導入された主要機能の 1 つは、プログラム中でプラグマを使って明示的にベクトル化/SIMD […]
  • OpenMP* 4.0 API C/C++ シンタックス・クイック・リファレンス・カードOpenMP* 4.0 API C/C++ シンタックス・クイック・リファレンス・カード OpenMP* 4.0 API C/C++ Syntax Quick Reference Card (英語) の日本語版が公開されました。 日本語版はこちらから入手できます。 このクイック・リファレンス・カードを『インテル® C++ コンパイラー 15.0 ユーザー・リファレンス・ガイド』と同じフォルダー内 (デフォルトでは \ […]