JavaScript is required to for searching.
ナビゲーションリンクをスキップ
印刷ビューの終了
Oracle Solaris Studio 12.3: スレッドアナライザユーザーズガイド     Oracle Solaris Studio 12.3 Information Library (日本語)
search filter icon
search icon

ドキュメントの情報

はじめに

1.  スレッドアナライザとその機能について

2.  データの競合チュートリアル

2.1 データの競合チュートリアルのソースファイル

2.1.1 データの競合チュートリアルのソースファイルの入手

2.1.2 prime_omp.c のソースコード

2.1.3 prime_pthr.c のソースコード

2.1.3.1 prime_omp.c および prime_pthr.c でのデータの競合の影響

2.2 スレッドアナライザを使用したデータの競合の検出方法

2.2.1 コードを計測する

2.2.1.1 ソースコードを計測する

2.2.1.2 バイナリコードを計測する

2.2.2 データの競合の検出実験を作成する

2.2.3 データの競合の検出実験を検証する

2.2.3.1 スレッドアナライザを使用したデータの競合実験の表示

2.2.3.2 er_print を使用したデータの競合実験の表示

2.3 実験結果について

2.3.1 prime_omp.c でのデータの競合

2.3.2 prime_pthr.c でのデータの競合

2.3.3 データの競合の呼び出しスタックトレース

2.4 データの競合の原因の診断

2.4.1 データの競合が誤検知であるかどうかをチェックする

2.4.2 データの競合が影響のないものであるかどうかを確認する

2.4.3 データの競合ではなくバグを修正する

2.4.3.1 prime_omp.c でのバグの修正

2.4.3.2 prime_pthr.c でのバグの修正

2.5 誤検知

2.5.1 ユーザー定義の同期

2.5.2 さまざまなスレッドでリサイクルされるメモリー

2.6 影響のないデータの競合

2.6.1 素数検索用のプログラム

2.6.2 配列値の型を検証するプログラム

2.6.3 二重検査されたロックを使用したプログラム

3.  デッドロックのチュートリアル

A.  スレッドアナライザで認識される API

B.  役に立つヒント

2.4 データの競合の原因の診断

この節では、データの競合の原因を診断する基本的な方法について説明します。

2.4.1 データの競合が誤検知であるかどうかをチェックする

誤検知のデータの競合は、スレッドアナライザで報告されますが、実際には起こっていないデータの競合です。スレッドアナライザは、報告する誤検知の数を減らそうと試みます。ただし、ツールが正確なジョブを行えずに、誤検知のデータの競合を報告する場合があります。

誤検知のデータの競合は本当のデータの競合ではなく、したがってプログラムの動作に影響しないので、このデータの競合は無視できます。

誤検知のデータの競合の例については、「2.5 誤検知」を参照してください。レポートから誤検知のデータの競合を削除する方法については、「A.1 スレッドアナライザユーザー API」を参照してください。

2.4.2 データの競合が影響のないものであるかどうかを確認する

影響のないデータの競合は、存在していてもプログラムの正確さには影響しない意図的なデータの競合です。

一部のマルチスレッドアプリケーションでは、データの競合を引き起こすコードを意図的に使用します。設計によってデータの競合が存在するので、修正は必要ありません。ただし、場合によっては、このようなコードを正しく実行させるには非常に慎重を要します。これらのデータの競合については注意深く調べてください。

影響のない競合については、「2.5 誤検知」を参照してください。

2.4.3 データの競合ではなくバグを修正する

スレッドアナライザは、プログラム内でデータの競合を見つけるときに役立ちますが、プログラム内のバグを自動的に見つけることも、見つかったデータの競合の修正方法を提示することもできません。データの競合は、バグによって生じることもあります。バグを見つけて修正することが重要です。単にデータの競合を取り除くだけでは正しいアプローチにはならず、以降のデバッグがさらに困難になる可能性があります。

2.4.3.1 prime_omp.c でのバグの修正

ここでは、prime_omp.c でのバグを修正する方法について説明します。完全なファイルのリストについては、「「2.1.2 prime_omp.c のソースコード」」を参照してください。

配列 primes[ ] の要素でのデータの競合を削除するために、行 50 および 51 を critical セクションに移します。

47      #pragma omp parallel for
48      for (i = 2; i < N; i++) {
49          if ( is_prime(i) ) {
                 #pragma omp critical

                 {
50                  primes[total] = i;
51                  total++;
                 }
52          }
53     }

また、次のように行 50 および 51 を 2 つの critical セクションに移すこともできますが、この変更ではプログラムを修正できません。

47      #pragma omp parallel for
48      for (i = 2; i < N; i++) {
49          if ( is_prime(i) ) {
                 #pragma omp critical
                 {
50                  primes[total] = i;
                 }
                 #pragma omp critical
                 {
51                  total++;
                 }
52          }
53     }

スレッドは、排他的ロックを使用して primes[ ] 配列へのアクセスを制御しているので、行 50 および 51 の critical セクションによってデータの競合が取り除かれます。ただし、プログラムはまだ正しくありません。2 つのスレッドは、同じ total 値を使用して primes[ ] の同じ要素を更新する可能性があり、primes[ ] の要素の中には、値がまったく割り当てられないものが生じる可能性があります。

行 23 での pflag[ ] からの読み取りと、行 26 での pflag[ ] への書き込みとの 2 番目のデータの競合は間違った結果を招かないので、実際には影響のない競合です。影響のないデータの競合の修正は必須ではありません。

2.4.3.2 prime_pthr.c でのバグの修正

ここでは、prime_pthr.c でのバグを修正する方法について説明します。完全なファイルのリストについては、「2.1.3 prime_pthr.c のソースコード」を参照してください。

単一の相互排他ロックを使用して、行 44 での prime[ ] のデータの競合と、行 45 での total のデータの競合を取り除きます。

行 60 での i への書き込みと、行 40 での (*arg という) 同じメモリー位置からの読み取りとのデータの競合と、行 27 での pflag[ ] のデータの競合は、別々のスレッドによる変数 i への共有アクセスに問題があることを明らかにします。prime_pthr.c の初期スレッドは、行 60 ~ 62 でループの子スレッドを作成し、関数 work() で動作するようにこれらをディスパッチします。ループ インデックス i は、アドレスで work() に渡されます。すべてのスレッドは i に対して同じメモリー位置にアクセスするので、各スレッドの i の値は一意のままではありませんが、初期スレッドがループ インデックスを増やすたびに変化します。別々のスレッドが同じ i の値を使用するので、データの競合が起こります。問題を修正する 1 つの方法は、i をアドレスではなく値で work() に渡すことです。

次に、修正されたバージョンの prime_pthr.c を示します。

  1  /*
  2   * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
  3   * @(#)prime_pthr_fixed.c 1.3 (Oracle) 10/03/26
  4   */
  5
  6  #include <stdio.h>
  7  #include <math.h>
  8  #include <pthread.h>
  9
 10  #define THREADS 4
 11  #define N 10000
 12
 13  int primes[N];
 14  int pflag[N];
 15  int total = 0;
 16  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 17
 18  int is_prime(int v)
 19  {
 20      int i;
 21      int bound = floor(sqrt(v)) + 1;
 22
 23      for (i = 2; i < bound; i++) {
 24          /* no need to check against known composites */
 25          if (!pflag[i])
 26              continue;
 27          if (v % i == 0) {
 28              pflag[v] = 0;
 29              return 0;
 30          }
 31      }
 32      return (v > 1);
 33  }
 34
 35  void *work(void *arg)
 36  {
 37      int start;
 38      int end;
 39      int i;
 40
 41      start = (N/THREADS) * ((int)arg) ;
 42      end = start + N/THREADS;
 43      for (i = start; i < end; i++) {
 44          if ( is_prime(i) ) {
 45              pthread_mutex_lock(&mutex);
 46              primes[total] = i;
 47              total++;
 48              pthread_mutex_unlock(&mutex);
 49          }
 50      }
 51      return NULL;
 52  }
 53
 54  int main(int argn, char **argv)
 55  {
 56      int i;
 57      pthread_t tids[THREADS-1];
 58
 59      for (i = 0; i < N; i++) {
 60          pflag[i] = 1;
 61      }
 62
 63      for (i = 0; i < THREADS-1; i++) {
 64          pthread_create(&tids[i], NULL, work, (void *)i);
 65      }
 66
 67      i = THREADS-1;
 68      work((void *)i);
 69
 70      for (i = 0; i < THREADS-1; i++) {
 71          pthread_join(tids[i], NULL);
 72      }
 73
 74      printf("Number of prime numbers between 2 and %d: %d\n",
 75             N, total);
 76
 77      return 0;
 78  }