本教程所依赖的两个程序都存在数据争用:
第一个程序查找质数。该程序是用 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
请注意每个程序的三次运行结果的不一致性。可能需要运行这些程序三次以上才能看到不一致的结果。
接下来将会检测代码并创建实验,以便可以找出发生数据争用的位置。