Mae向きなブログ

Mae向きな情報発信を続けていきたいと思います。

OpenMP(2)

昨日に引き続き、OpenMPについて勉強してみました。Googleで調べてみると、結構、OpenMPについて参考になる文書が検索できます。昨日は、筑波大学のPDFファイルを、今日は、九州大学の以下のPDFファイルを参考にさせてもらいました。

以下は、図10に掲載されているベクトルと行列の積です。外側のforループは並列化されますが、その際、private(j)がないと、jが共有変数扱いになってしまいます。jは、各スレッドで独立してないと困りますので、private(j)と明記するんですね。

matvec.c

#include <stdio.h>
#include <omp.h>

#define N 1000

void matvec(double a[][N], double x[], double y[]);

void matvec(double a[][N], double x[], double y[])
{
  int i, j;
#pragma omp parallel for private(j)  
  for (i = 0; i < N; i++) {
    for (j = 0; j < N; j++) {
      y[i] += a[i][j] * x[i];
    }
  }
}

int main(void)
{
  double a[N][N], x[N], y[N];
  int i, j;
#pragma omp parallel for private(j)  
  for (i = 0; i < N; i++) {
    x[i] = rand();
    for (j = 0; j < N; j++) {
      a[i][j] = rand();
    }
  }
  matvec(a, x, y);
  return 0;
}

「2.1 ループの並列化による効果」が参考になりました。上記のmatvec.cを逐次、並列の場合で計測してみたのですが、逐次の方が早くなりました。スレーブスレッドの生成と廃棄のコストが足を引っ張ったんだろうと思います。
「5 プログラムの書き変えを伴うループの改善」では、ループの繰り返し間の依存関係の除去や性能改善について書かれています。また、メモリの参照順序の話も出てくるのですが、以前、「メモリ階層を意識したソフトウェアの作成」というエントリを書いていたので、納得しながら読むことができました(^^)。

「2.1 別々の仕事を並列に実行させる指示文」では、配列の中から、最大値、最小値、平均値を求める演算を同時に実行するプログラム(図3)が載っています。環境がIntel Core 2 Duoなので、最大値と最小値を求めるものに変更して、実行時間を計測してみました。

maxmin.c

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

#define N 1000000

int main(void)
{
  int i;
  double data[N];
  double min, max;
  srand((unsigned)time(NULL));
#pragma omp parallel for
  for (i = 0; i < N; i++) {
    data[i] = rand();
  }

#pragma omp parallel sections
  {
#pragma omp section
    {
      min = data[0];
      for (i = 1; i < N; i++) {
        if (min > data[i]) {
          min = data[i];
        }
      }
    }
#pragma omp section
    {
      max = data[0];
      for (i = 1; i < N; i++) {
        if (max < data[i]) {
          max = data[i];
        }
      }
    }
  }
  printf("Minimum = %e Maximum = %e\n", min, max);
  return 0;
}

実行結果

これくらいだと、まだまだ逐次の方が早いですね。やはり、スレーブスレッドの生成と廃棄のコストが足を引っ張ったんでしょうか。

$ gcc -fopenmp maxmin.c -o maxmin_omp
$ gcc maxmin.c -o maxmin
$ time ./maxmin_omp <==== 並列の場合
Minimum = 1.722000e+03 Maximum = 2.147484e+09

real 0m0.225s
user 0m0.112s
sys 0m0.240s
$ time ./maxmin <==== 逐次の場合
Minimum = 8.910000e+02 Maximum = 2.147476e+09

real 0m0.052s
user 0m0.024s
sys 0m0.028s

「4.3 自動で並列化できないプログラムの対処」も面白いですね。