本教程所依赖的两个程序都存在数据争用:
第一个程序查找质数。该程序是用 C 编写的,并使用 OpenMP 指令进行了并行化。该源文件称为 prime_omp.c。
第二个程序也查找质数,也是用 C 编写的。但它使用 POSIX 线程(而不是 OpenMP 指令)进行了并行化。该源文件称为 prime_pthr.c。
本教程中使用的源文件位于 /opt/solstudio12.2/prod/examples/tha(Oracle Solaris 系统)或 /opt/oracle/solstudio12.2/prod/examples/tha(Linux 或 OpenSolaris 系统)下。这些示例位于 prime_omp 和 prime_pthr 子目录中。每个示例目录都包含 Makefile 和 DEMO 说明文件,但是本教程并不遵循这些说明,也不使用 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 |
请注意每个程序的三次运行结果的不一致性。可能需要运行这些程序三次以上才能看到不一致的结果。
接下来将会校验代码并创建实验,以便可以找出发生数据争用的位置。