本教程所依赖的两个程序都存在数据争用:
第一个程序查找质数。该程序是用 C 编写的,并使用 OpenMP 指令进行了并行化。该源文件称为 prime_omp.c。
第二个程序也查找质数,并且也是用 C 编写的。但是,它使用 POSIX 线程(而不是 OpenMP 指令)进行并行化。该源文件称为 prime_pthr.c。
您可以从 Oracle Developer Studio 开发者门户的下载区域中下载本教程所用的源文件。
在下载并解压缩样例文件之后,您可以在 OracleDeveloperStudio12.5-Samples/ThreadAnalyzer 目录中找到样例。这些样例位于 prime_omp 和 prime_pthr 子目录中。每个样例目录都包含 Makefile 和 DEMO 说明文件,但是本教程并不遵循这些说明,也不使用 Makefile。本教程将逐步指导您执行命令。
为跟随本教程学习,您可以将 prime_omp.c 和 prime_pthr.c 文件从样例目录复制到其他目录,也可以创建自己的文件并从下面列出的代码内容中复制代码。
本节列出了 prime_omp.c 的源代码,如下所示:
1 /*
2 * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All Rights Reserved.
3 */
4
5 #include <stdio.h>
6 #include <math.h>
7 #include <omp.h>
8
9 #define THREADS 4
10 #define N 10000
11
12 int primes[N];
13 int pflag[N];
14
15 int is_prime(int v)
16 {
17 int i;
18 int bound = floor(sqrt(v)) + 1;
19
20 for (i = 2; i < bound; i++) {
21 /* no need to check against known composites */
22 if (!pflag[i])
23 continue;
24 if (v % i == 0) {
25 pflag[v] = 0;
26 return 0;
27 }
28 }
29 return (v > 1);
30 }
31
32 int main(int argn, char **argv)
33 {
34 int i;
35 int total = 0;
36
37 #ifdef _OPENMP
38 omp_set_dynamic(0);
39 omp_set_num_threads(THREADS);
40 #endif
41
42 for (i = 0; i < N; i++) {
43 pflag[i] = 1;
44 }
45
46 #pragma omp parallel for
47 for (i = 2; i < N; i++) {
48 if ( is_prime(i) ) {
49 primes[total] = i;
50 total++;
51 }
52 }
53
54 printf("Number of prime numbers between 2 and %d: %d\n",
55 N, total);
56
57 return 0;
58 }
本节列出了 prime_pthr.c 的源代码,如下所示:
1 /*
2 * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All Rights Reserved.
3 */
4
5 #include <stdio.h>
6 #include <math.h>
7 #include <pthread.h>
8
9 #define THREADS 4
10 #define N 10000
11
12 int primes[N];
13 int pflag[N];
14 int total = 0;
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 void * work(void *arg)
34 {
35 int start;
36 int end;
37 int i;
38
39 start = (N/THREADS) * (*(int *)arg);
40 end = start + N/THREADS;
41 for (i = start; i < end; i++) {
42 if ( is_prime(i) ) {
43 primes[total] = i;
44 total++;
45 }
46 }
47 return NULL;
48 }
49
50 int main(int argn, char **argv)
51 {
52 int i;
53 pthread_t tids[THREADS-1];
54
55 for (i = 0; i < N; i++) {
56 pflag[i] = 1;
57 }
58
59 for (i = 0; i < THREADS-1; i++) {
60 pthread_create(&tids[i], NULL, work, (void *)&i);
61 }
62
63 i = THREADS-1;
64 work((void *)&i);
65
66 for (i = 0; i < THREADS-1; i++) {
67 pthread_join(tids[i], NULL);
68 }
69
70 printf("Number of prime numbers between 2 and %d: %d\n",
71 N, total);
72
73 return 0;
74 }
当代码包含争用情况时,内存访问的顺序是不确定的,因此每次运行的计算结果会不同。prime_omp 和 prime_pthr 程序中的正确答案为 1229。
通过编译并运行示例可以看出,由于代码中存在数据争用,执行 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: 1180
在下面的示例中,在提示符处键入命令以编译并运行 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
请注意每个程序的三次运行结果的不一致性。可能需要运行这些程序三次以上才能看到不一致的结果。
接下来将会检测代码并创建实验,以便可以找出发生数据争用的位置。