2.2.3.1 スレッドアナライザを使用したデータの競合実験の表示
2.2.3.2 er_print を使用したデータの競合実験の表示
2.4.1 データの競合が誤検知であるかどうかをチェックする
このチュートリアルでは、データの競合を含んだ 2 つのプログラムを使用します。
最初のプログラムは素数を見つけます。このプログラムは C 言語で作成され、OpenMP 指令で並列化されています。ソースファイルは prime_omp.c と呼ばれます。
2 番目のプログラムも素数を見つけ、C 言語で作成されています。ただし、OpenMP 指令ではなく POSIX スレッドで並列化されています。ソースファイルは prime_pthr.c と呼ばれます。
このチュートリアルで使用されるソースファイルは、Oracle Solaris システムでは /opt/solstudio12.2/prod/examples/tha に、Linux または OpenSolaris システムでは /opt/oracle/solstudio12.2/prod/examples/tha にあります。例は、 prime_omp および prime_pthr サブディレクトリにあります。例を含んだ各ディレクトリには、手順に関する DEMO ファイルと Makefile ファイルが 1 つずつありますが、このチュートリアルではその手順を無視し、Makefile も使用しません。代わりに、コマンドを個別に実行していきます。
このチュートリアルに沿って学習するには、例を含んだディレクトリから prime_omp.c と prime_pthr.c ファイルを、別のディレクトリにコピーするか、自分でファイルを作成し、次のコードリストからコードをコピーしてください。
prime_omp.c のソースコードは次に示すとおりです。
1 /*
2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
3 * @(#)prime_omp.c 1.3 (Oracle) 10/03/26
4 */
5
6 #include <stdio.h>
7 #include <math.h>
8 #include <omp.h>
9
10 #define THREADS 4
11 #define N 10000
12
13 int primes[N];
14 int pflag[N];
15
16 int is_prime(int v)
17 {
18 int i;
19 int bound = floor(sqrt(v)) + 1;
20
21 for (i = 2; i < bound; i++) {
22 /* no need to check against known composites */
23 if (!pflag[i])
24 continue;
25 if (v % i == 0) {
26 pflag[v] = 0;
27 return 0;
28 }
29 }
30 return (v > 1);
31 }
32
33 int main(int argn, char **argv)
34 {
35 int i;
36 int total = 0;
37
38 #ifdef _OPENMP
39 omp_set_dynamic(0);
40 omp_set_num_threads(THREADS);
41 #endif
42
43 for (i = 0; i < N; i++) {
44 pflag[i] = 1;
45 }
46
47 #pragma omp parallel for
48 for (i = 2; i < N; i++) {
49 if ( is_prime(i) ) {
50 primes[total] = i;
51 total++;
52 }
53 }
54
55 printf("Number of prime numbers between 2 and %d: %d\n",
56 N, total);
57
58 return 0;
59 }
prime_pthr.c のソースコードは次に示すとおりです。
1 /*
2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
3 * @(#)prime_pthr.c 1.4 (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
17 int is_prime(int v)
18 {
19 int i;
20 int bound = floor(sqrt(v)) + 1;
21
22 for (i = 2; i < bound; i++) {
23 /* no need to check against known composites */
24 if (!pflag[i])
25 continue;
26 if (v % i == 0) {
27 pflag[v] = 0;
28 return 0;
29 }
30 }
31 return (v > 1);
32 }
33
34 void *work(void *arg)
35 {
36 int start;
37 int end;
38 int i;
39
40 start = (N/THREADS) * (*(int *)arg);
41 end = start + N/THREADS;
42 for (i = start; i < end; i++) {
43 if ( is_prime(i) ) {
44 primes[total] = i;
45 total++;
46 }
47 }
48 return NULL;
49 }
50
51 int main(int argn, char **argv)
52 {
53 int i;
54 pthread_t tids[THREADS-1];
55
56 for (i = 0; i < N; i++) {
57 pflag[i] = 1;
58 }
59
60 for (i = 0; i < THREADS-1; i++) {
61 pthread_create(&tids[i], NULL, work, (void *)&i);
62 }
63
64 i = THREADS-1;
65 work((void *)&i);
66
67 for (i = 0; i < THREADS-1; i++) {
68 pthread_join(tids[i], NULL);
69 }
70
71 printf("Number of prime numbers between 2 and %d: %d\n",
72 N, total);
73
74 return 0;
75 }
コードに競合状態が含まれ、実行するごとに別々の計算結果が得られる場合、メモリーアクセスの順序は決まっていません。
例をコンパイルして実行できるので、prime_omp または prime_pthr を実行するごとに、コード内のデータの競合によって誤ったまたは矛盾した結果が生じることがわかります。
次の例で、太字のコマンドを入力して、prime_omp プログラムをコンパイルし実行します。
% cc -xopenmp=noopt -o prime_omp prime_omp.c -lm % % ./prime_omp Number of prime numbers between 2 and 10000: 1229 % ./prime_omp Number of prime numbers between 2 and 10000: 1228 % ./prime_omp Number of prime numbers between 2 and 10000: 1229
次の例で、太字のコマンドを入力して、prime_pthr プログラムをコンパイルし実行します。
% cc -mt -o prime_pthr prime_pthr.c -lm % % ./prime_pthr Number of prime numbers between 2 and 10000: 1140 % ./prime_pthr Number of prime numbers between 2 and 10000: 1122 % ./prime_pthr Number of prime numbers between 2 and 10000: 1141
各プログラムを 3 回実行した結果が矛盾していることに注意してください。矛盾した結果が表示されるまで、4 回以上プログラムを実行する必要がある場合もあります。
次に、データの競合が生じている位置を特定できるように、コードを計測し、実験を作成します。