ノード内並列

まずは,内積計算プログラムが1つのRaspberry Pi上の複数のCPUコアを利用できるよう に拡張しましょう.これを,ノード内並列化と呼びます.

OpenMPとは

複数のCPUコアを活用するには様々な方法がありますが,スパコンにおいて広く 用いられているのは,OpenMPと呼ばれるAPIです. OpenMPに対応するコンパイラを用いると,ソースコード中にディレクティブと 呼ばれるコメントのようなものを挿入するだけで, コンパイラが自動的に並列化しててくれます.

下記がOpenMPを用いて並列化した内積計算プログラムです. 逐次版のソースコードに追加したのは, #pragma omp parallel for reduction(+:sum)という1行だけです. このディレクティブを挿入することにより,直後のforループが自動的にコアの数に分割され, 並列に実行されます.

例えば,ARRAY_SIZEが100でCPUが4コアだとすると,

  • コア0がi=0〜24
  • コア1がi=25〜49
  • コア2がi=50〜74
  • コア3がi=75〜99

という風に4つのコアがループを均等に分担して実行します.

#include <stdio.h>

#define TRIALS (1000)
#define ARRAY_SIZE (1000 * 1000)

double a[ARRAY_SIZE];
double b[ARRAY_SIZE];

void compute()
{
    double sum = 0;

    #pragma omp parallel for reduction(+:sum)
    for (int i = 0; i < ARRAY_SIZE; i++) {
        sum += a[i] * b[i];
    }
}

int main(int argc, char *argv[])
{
    for (int i = 0; i < ARRAY_SIZE; i++) {
        a[i] = 1.0;
        b[i] = 2.0;
    }

    for (int i = 0; i < TRIALS; i++) {
        compute();
    }

    return 0;
}

OpenMP版プログラムのコンパイル・実行

このプログラムをgccコンパイラでコンパイルします. OpenMPを使用するプログラムするためには,-fopenmpというオプションを指定する必要があります.

$ gcc -fopenmp -o dot_omp dot_omp.c

このプログラムを実行します.

$ time ./dot_omp

OpenMPを使用しない場合の26.886秒に対して,7.372秒に実行時間を短縮できました. Raspberry Pi 3B+は4つのCPUコアを搭載しています.4つのCPUコアを活用することで, 3.6倍の高速化を達成できたことになります.

real	0m7.372s
user	0m29.175s
sys	0m0.040s

OpenMPはデフォルトでは全てのCPUコアを用いて計算を行いますが, OMP_NUM_THREADSという環境変数を設定することにより,使用するコア数を制限することができます. 例えば,2コアを使用して計算を行うためには,次のようにします.

$ time OMP_NUM_THREADS=2 ./dot_omp

やってみよう

  • OMP_NUM_THREADSを使用して使用するコア数を1から4まで変更し, プログラムの実行時間がどのように変化するか調べてみましょう.
  • ARRAY_SIZETRIALSをより大きな値に変更して,実行時間を測ってみましょう.