This tutorial relies on two programs, both of which contain data races:
The first program finds prime numbers. It is written with C and is parallelized with OpenMP directives. The source file is called prime_omp.c.
The second program also finds prime numbers and is also written with C. However, it is parallelized with POSIX threads instead of OpenMP directives. The source file is called prime_pthr.c.
You can download the source files used in this tutorial from the Download area (http://www.oracle.com/technetwork/server-storage/developerstudio/downloads/index.html) of the Oracle Developer Studio developer portal.
After you download and unpack the sample files, you can find the samples in the OracleDeveloperStudio12.5-Samples/ThreadAnalyzer directory. The samples are located in the prime_omp and prime_pthr subdirectories. Each sample directory includes a Makefile and a DEMO file of instructions, but this tutorial does not follow those instructions or use the Makefile. Instead, you are instructed to execute commands individually.
To follow the tutorial, you can copy the prime_omp.c and prime_pthr.c files from the samples directories to a different directory, or you can create your own files and copy the code from the following code listings.
This section shows the source code for prime_omp.c as follows:
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 }
This section shows source code for prime_pthr.c as follows:
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 }
When there is a race condition in the code, the order of memory accesses is non-deterministic so the computation gives different results from run to run. The correct answer in the prime_omp and prime_pthr programs is 1229.
You can compile and run the examples so you can see that the execution of prime_omp or prime_pthr produces incorrect and inconsistent results because of the data races in the code.
In the following example, type the commands at the prompt to compile and run the prime_omp program:
% 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
In the following example, type the commands at the prompt to compile and run the prime_pthr program:
% 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
Notice the inconsistency of the results of the three runs of each program. You might need to run the programs more than three times to see inconsistent results.
Next, you instrument the code and create experiments so you can find out where the data races are occurring.