(ソースコードメモ)GCCでのOpenMP/OpenACC実装を見てみる


1. はじめに

OpenMPおよびOpenACCは、CやFortranでディレクティブ(指示文)により、コンパイラに対して並列化を指示するものである。例えば、OpenMPの場合、以下のように#pragma omp parallelという指示文で指示する。これにより、明示的にマルチスレッドプログラミングをしなくても、コンパイラ側で処理が行われる。OpenACCの場合は、他デバイスの処理コード生成(NVIDIA GPUやAMD GPU等)が行われる。

 #pragma omp parallel
  {
    body;
  }

2. OpenMP/OpenACCの処理

OpenMP/OpenACCのディレクティブ(指示文)の処理は、フロントエンドだけでなく、GIMPLEのパス上でも行われている。そして、最終的にビルドイン関数を含んだライブラリを介して実行する。項目として示すと以下通りである。

  • ディレクティブを検出しビルトイン関数に置き換える処理(コンパイラ)
  • GCCのビルドイン関数(例:GOMP_parallel)として処理するlibgompに分かれる。(ランタイム)

2.1. GCCでのコンパイル

コンパイルパス

コンパイラの中では、パーサの後、パス上でOpenMP/OpenACCの処理が行われている。
まず、パーサーは、C言語の場合、c/c-parser.c、C++言語の場合、cpp/parser.cである。(余談:githubのコードは、1MBを超えると行指定しずらいのでコードのみリンクを張っている。)
その後、gimple形式での最適化を行う。ここでpass_xxxxが、gimpleの最適化関数である。関連するパス上の関数を上げると以下のとおりである。

ここで、組み込み関数(builtin function)は以下で定義されている。

組み込み関数(builtin)関数の生成

コンパイル時は、pass_omp_expand処理の延長で、BUILT_IN_GOMP_PARALLEL等の組み込み(builtin)関数の出力を行う。そして、DEF_GOMP_BUILTINマクロでlibgompの関数であるGOMP_parallel等へのマッピングを行う。

関連しているコードは以下である。

2.2. OpenMPのランタイム(libgomp)

libgomp側

組み込み(builtin)関数の実体が、libgompにある。例えば、GOMP_parallel等が定義されている。また、libgompは、環境変数により、OpenMPのスケジューリングを設定できる。このため、libgomp内には、parse_scheduleや、gomp_schedule_type等の関数がある。
ここでは、GOMP_parallel関数を見ていく。同関数では、gomp_team_startを呼び出して、スレッドを生成する。そして、中では、gomp_thread_start_data構造体で、スレッドを管理する。最終的に、GOMP_parallel_end関数およびその先の、gomp_team_end関数で同期して終了する。

void
GOMP_parallel (void (*fn) (void *), void *data, unsigned num_threads,
           unsigned int flags)
{
  num_threads = gomp_resolve_num_threads (num_threads, 0);
  gomp_team_start (fn, data, num_threads, flags, gomp_new_team (num_threads),
           NULL);
  fn (data);
  ialias_call (GOMP_parallel_end) ();
}

関連ソースコードは、主に以下のファイルである。

A. 参考資料

A.1. 概要

A.2. GCC

A.3. libgomp

A.4. OpenMP

A.5. OpenACC

A.6. gccのコンパイル手順

ubuntu 19.10でのgcc9.xのコンパイル手順は、以下のとおりである。なお、makeで並列化オプション(-j)をつけないと非常に遅くなる。ここでは、8コアでコンパイルしているので8を入れている。5分程度かかる。一方このオプションを外すと20分以上かかる。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install gcc
$ sudo apt install make
$ sudo apt install build-essential
$ sudo apt install flex
$ git clone https://github.com/gcc-mirror/gcc/
$ cd gcc
$ git checkout releases/gcc-9.3.0
$ ./contrib/download_prerequisites
$ mkdir build && cd build
$ ../configure --enable-languages=c,c++ --prefix=/usr/local --disable-bootstrap --disable-multilib
$ make
$ make install
$ /usr/local/bin/gcc -v
$ /usr/local/bin/g++ -v

この手順は、gccのmaster(c5bac7d127f288fd2f8a1f15c3f30da5903141c6)(2020/04/18)に対して行ったが、コンパイルできた。

参考資料

A.7. GCCのコンパイルパス

gimpleレベルでの最適化を行うコンパイルパスは、以下の通りである。コンパイルパスは、passes.defで定義されている。

openmpに関するソースコードは以下の通りである。

参考文献