Matt Coles před 8 roky
revize
b7a9a66aed
4 změnil soubory, kde provedl 497 přidání a 0 odebrání
  1. 1 0
      .gitignore
  2. 13 0
      README.md
  3. 296 0
      parallel.c
  4. 187 0
      single.c

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
1
+*.pdf

+ 13 - 0
README.md

@@ -0,0 +1,13 @@
1
+Instructions for how to compile and run the programs are included in the report
2
+but to save opening a pdf some simple instructions are reproduced here:
3
+
4
+cc parallel.c -o parallel -lrt -lpthread -std=gnu11
5
+cc single.c -o single -lrt -std=gnu11
6
+
7
+./parallel
8
+./single
9
+
10
+./parallel -h
11
+./single -h
12
+
13
+To print usage information.

+ 296 - 0
parallel.c

@@ -0,0 +1,296 @@
1
+#include <stdio.h>
2
+#include <stdbool.h>
3
+#include <stdlib.h>
4
+#include <time.h>
5
+#include <pthread.h>
6
+#include <math.h>
7
+#include <string.h>
8
+
9
+static pthread_barrier_t sync_barrier;
10
+bool solved;
11
+bool allow_print;
12
+
13
+// Struct containing all the useful information for a thread
14
+typedef struct thread_data {
15
+    unsigned long start_row;
16
+    unsigned long end_row;
17
+    double **array;
18
+    unsigned long dimension;
19
+    double precision;
20
+    bool finished;
21
+} THREAD_DATA_T;
22
+
23
+void printSquare(double **array, unsigned long dimension) {
24
+    unsigned long i, j;
25
+    for (i = 0; i < dimension; i++) {
26
+        for (j = 0; j < dimension; j++) printf("%f ", array[i][j]);
27
+        printf("\n");
28
+    }
29
+}
30
+
31
+long double time_seconds(struct timespec begin, struct timespec end) {
32
+    long long sec_diff = end.tv_sec - begin.tv_sec;
33
+    long long nsec_diff = end.tv_nsec - begin.tv_nsec;
34
+    long long as_ns = sec_diff * 1000000000L + nsec_diff;
35
+    return (long double) as_ns / 1000000000.0;
36
+}
37
+
38
+void solveArray(double **array, unsigned long dimension, double precision,
39
+                unsigned long start, unsigned long end) {
40
+    unsigned long i, j;
41
+    for (i = start; i < end; i++) {
42
+        for (j = 1; j < dimension - 1; j++) {
43
+            // no need to gain locks on any of the array elements as we are the
44
+            // only one writing to them
45
+            // and whether we get the old or new values of an element
46
+            // doesn't matter
47
+            double previous = array[i][j];
48
+            double avg = (array[i][j - 1] + array[i][j + 1] + array[i - 1][j] +
49
+                          array[i + 1][j]) / 4.0;
50
+            array[i][j] = avg;
51
+            // only do the actual check if we haven't already failed
52
+            if (solved == true && fabs(avg - previous) > precision) {
53
+                // dont bother getting a lock here because the only other value
54
+                // it will be set to
55
+                // is false and we don't care if it gets messed up
56
+                solved = false;
57
+            }
58
+        }
59
+    }
60
+}
61
+
62
+void *bootstrap_thread(void *arg) {
63
+    THREAD_DATA_T *td = (THREAD_DATA_T *) arg;
64
+    // Each thread will do a single iteration over the array before waiting for
65
+    // every thread to finish
66
+    // and then waiting for the main thread to check if things are solved
67
+    while (1) {
68
+        if (td->finished) {
69
+            // this is only here to stop static code analysis
70
+            // whining about infinite loops
71
+            return NULL;
72
+        }
73
+        solveArray(td->array, td->dimension, td->precision, td->start_row,
74
+                   td->end_row);
75
+        // first barrier waits for all threads to finish
76
+        pthread_barrier_wait(&sync_barrier);
77
+        // wait for main thread to check completion
78
+        pthread_barrier_wait(&sync_barrier);
79
+    }
80
+    return NULL;
81
+}
82
+
83
+double **constructBigArray(double top, double bottom, double left, double right,
84
+                           long unsigned int n) {
85
+    // here lies exciting memory allocation and array initialisation, boring..
86
+    long unsigned int i, j, k;
87
+    double **arr = (double **) malloc(n * sizeof(double *));
88
+    for (k = 0; k < n; k++) {
89
+        arr[k] = (double *) malloc(n * sizeof(double));
90
+    }
91
+    for (i = 0; i < n; i++) {
92
+        for (j = 0; j < n; j++) {
93
+            if (i == 0) {
94
+                if (j == 0 || j == n - 1) arr[i][j] = 0;
95
+                else arr[i][j] = top;
96
+            } else if (i == n - 1) {
97
+                if (j == 0 || j == n - 1) arr[i][j] = 0;
98
+                else arr[i][j] = bottom;
99
+            } else if (j == 0) {
100
+                arr[i][j] = left;
101
+            } else if (j == n - 1) {
102
+                arr[i][j] = right;
103
+            } else {
104
+                arr[i][j] = 0;
105
+            }
106
+        }
107
+    }
108
+    return arr;
109
+}
110
+
111
+// CHANGE ME to set thread count
112
+#define NUM_THREADS 10
113
+
114
+// struct to hold some argument data
115
+typedef struct arg_data {
116
+    double precision;
117
+    unsigned long dimension;
118
+    double top_start;
119
+    double bottom_start;
120
+    double left_start;
121
+    double right_start;
122
+} ARG_DATA_T;
123
+
124
+// helper function for reading in arguments
125
+int argsContain(const char *val, char **arr, int size) {
126
+    int i;
127
+    for (i = 0; i < size; i++) {
128
+        if (strcmp(arr[i], val) == 0)
129
+            return i;
130
+    }
131
+    return -1;
132
+}
133
+
134
+// take a look at argv and print out usage or take in some arguments
135
+ARG_DATA_T *parseArgs(int argc, char **argv) {
136
+    int tmp;
137
+    ARG_DATA_T *args = (ARG_DATA_T *) malloc(sizeof(ARG_DATA_T));
138
+    allow_print = true;
139
+    if ((tmp = argsContain("-p", argv, argc)) >= 0 ||
140
+        (tmp = argsContain("--precision", argv, argc)) >= 0) {
141
+        args->precision = strtod(argv[tmp + 1], NULL);
142
+    } else {
143
+        args->precision = 0.00001;
144
+    }
145
+    if ((tmp = argsContain("-d", argv, argc)) >= 0 ||
146
+        (tmp = argsContain("--dimension", argv, argc)) >= 0) {
147
+        args->dimension = (unsigned long) strtol(argv[tmp + 1], NULL, 10);
148
+    } else {
149
+        args->dimension = 10;
150
+    }
151
+    if ((tmp = argsContain("-t", argv, argc)) >= 0 ||
152
+        (tmp = argsContain("--top", argv, argc)) >= 0) {
153
+        args->top_start = strtod(argv[tmp + 1], NULL);
154
+    } else {
155
+        args->top_start = 1;
156
+    }
157
+    if ((tmp = argsContain("-l", argv, argc)) >= 0 ||
158
+        (tmp = argsContain("--left", argv, argc)) >= 0) {
159
+        args->left_start = strtod(argv[tmp + 1], NULL);
160
+    } else {
161
+        args->left_start = 2;
162
+    }
163
+    if ((tmp = argsContain("-r", argv, argc)) >= 0 ||
164
+        (tmp = argsContain("--right", argv, argc)) >= 0) {
165
+        args->right_start = strtod(argv[tmp + 1], NULL);
166
+    } else {
167
+        args->right_start = 4;
168
+    }
169
+    if ((tmp = argsContain("-b", argv, argc)) >= 0 ||
170
+        (tmp = argsContain("--bottom", argv, argc)) >= 0) {
171
+        args->bottom_start = strtod(argv[tmp + 1], NULL);
172
+    } else {
173
+        args->bottom_start = 3;
174
+    }
175
+    if ((tmp = argsContain("-q", argv, argc)) > 0 ||
176
+        (tmp = argsContain("--quiet", argv, argc)) >= 0) {
177
+      allow_print = false;
178
+    }
179
+    if (argsContain("-h", argv, argc) >= 0 ||
180
+        argsContain("--help", argv, argc) >= 0) {
181
+        printf("Usage: %s [-p N] [-d N] [-t N] [-l N] [-r N] [-b N]\n",
182
+               argv[0]);
183
+        printf("Constructs a 2D array of doubles, with values from arguments "
184
+                       "of size dimension^2 and performs relaxation method"
185
+                       " to given precision.\n");
186
+        printf("Options:\n");
187
+        printf("-p/--precision: A double value to specify precision. "
188
+                       "Default: 0.0001\n");
189
+        printf("-d/--dimension: A long value to specify width and height of "
190
+                       "grid. Default: 10\n");
191
+        printf("-t/--top: A double indicating the values along the top of the "
192
+                       "array. Default: 1.0\n");
193
+        printf("-l/--left: A double indicating the values along the left side"
194
+                       " of the array. Default: 2.0\n");
195
+        printf("-b/--bottom: A double indicating the values along the bottom "
196
+                       "of the array. Default: 3.0\n");
197
+        printf("-r/--right: A double indicating the values along the right "
198
+                       "side of the array. Default: 4.0\n");
199
+        printf("-q/--quiet: Disables all printing except the result.\n");
200
+        exit(0);
201
+    }
202
+    return args;
203
+}
204
+
205
+// this function is used to return the number of rows a given thread will work
206
+// on
207
+// for an amount of rows
208
+unsigned long
209
+spread(unsigned long group, unsigned long totalGroups, unsigned long n) {
210
+    if (totalGroups % n == 0) return totalGroups / n;
211
+    else {
212
+        unsigned long tmp = (totalGroups / n);
213
+        if (totalGroups % n >= group) return tmp + 1;
214
+        else return tmp;
215
+    }
216
+}
217
+
218
+// helper function as not everyone defines the MIN macro
219
+unsigned long mini(unsigned long a, unsigned long b) {
220
+    if (a < b) {
221
+        return a;
222
+    } else {
223
+        return b;
224
+    }
225
+}
226
+
227
+int main(int argc, char **argv) {
228
+    int rc;
229
+    pthread_t thread[NUM_THREADS];
230
+    THREAD_DATA_T *td[NUM_THREADS];
231
+    ARG_DATA_T *args;
232
+    struct timespec begin, end;
233
+
234
+    args = parseArgs(argc, argv);
235
+
236
+    // the barrier should be at least the array size, subtract 2, as we won't
237
+    // use all threads if there are not enough single rows to share out
238
+    pthread_barrier_init(&sync_barrier, NULL,
239
+                         (unsigned int) mini(args->dimension - 2, NUM_THREADS));
240
+    double **array = constructBigArray(args->top_start, args->bottom_start,
241
+                                       args->left_start, args->right_start,
242
+                                       args->dimension);
243
+
244
+    // use clock_gettime to measure performance as it has best resolution
245
+    clock_gettime(CLOCK_MONOTONIC, &begin);
246
+    td[0] = (THREAD_DATA_T *) malloc(sizeof(THREAD_DATA_T));
247
+    solved = true;
248
+
249
+    // create NUM_THREADS-1 pthreads, starting from 1 - because the main thread
250
+    // is one of our threads
251
+    unsigned long rowCounter = 1 + spread(1, args->dimension - 2, NUM_THREADS);
252
+    for (unsigned long i = 1; i < NUM_THREADS; i++) {
253
+        // divides up the array into rows, so that each thread is operating on
254
+        // chunks of contiguous memory
255
+        unsigned long start_row, end_row;
256
+        start_row = rowCounter;
257
+        rowCounter += spread(i + 1, args->dimension - 2, NUM_THREADS);
258
+        end_row = rowCounter;
259
+
260
+        td[i] = (THREAD_DATA_T *) malloc(sizeof(THREAD_DATA_T));
261
+        td[i]->start_row = start_row;
262
+        td[i]->end_row = end_row;
263
+        td[i]->array = array;
264
+        td[i]->dimension = args->dimension;
265
+        td[i]->precision = args->precision;
266
+
267
+        td[i]->finished = false;
268
+
269
+        if (end_row - start_row > 0) { // only create threads that will do work
270
+            if ((rc = pthread_create(&thread[i], NULL, bootstrap_thread,
271
+                                     (void *) td[i]))) {
272
+                printf("failed to create threads: %d\n", rc);
273
+                return 1;
274
+            }
275
+        }
276
+    }
277
+    while (1) {
278
+        //main thread should also be solving
279
+        solveArray(array, args->dimension, args->precision, 1,
280
+                   1 + spread(1, args->dimension - 2, NUM_THREADS));
281
+        pthread_barrier_wait(&sync_barrier);
282
+        if (solved) {
283
+            clock_gettime(CLOCK_MONOTONIC, &end);
284
+            if (allow_print) printf("took me %Lfs\n", time_seconds(begin, end));
285
+            for (int i = 1; i < NUM_THREADS; i++) {
286
+                td[i]->finished = true;
287
+            }
288
+            if (allow_print) printf("====== SOLVED =====\n");
289
+            printSquare(array, args->dimension);
290
+            return 0;
291
+        }
292
+        solved = true; // make sure to reset this
293
+        pthread_barrier_wait(&sync_barrier);
294
+    }
295
+
296
+}

+ 187 - 0
single.c

@@ -0,0 +1,187 @@
1
+#include <stdio.h>
2
+#include <stdbool.h>
3
+#include <stdlib.h>
4
+#include <math.h>
5
+#include <time.h>
6
+#include <string.h>
7
+#include <pthread.h>
8
+
9
+// this code is left uncommented as it is self explanatory or the same as the
10
+// parallel version
11
+bool allow_print;
12
+
13
+void printSquare(double **array, unsigned long dimension) {
14
+  unsigned long i, j;
15
+  for (i = 0; i < dimension; i++) {
16
+    for (j = 0; j < dimension; j++) printf("%f ", array[i][j]);
17
+    printf("\n");
18
+  }
19
+}
20
+
21
+long double time_seconds(struct timespec begin, struct timespec end) {
22
+  long long sec_diff = end.tv_sec - begin.tv_sec;
23
+  long long nsec_diff = end.tv_nsec - begin.tv_nsec;
24
+  long long as_ns = sec_diff * 1000000000L + nsec_diff;
25
+  return (long double) as_ns / 1000000000.0;
26
+}
27
+
28
+unsigned long solveArray(double **array, unsigned long dimension, 
29
+    double precision) {
30
+  unsigned long i, j, k;
31
+  bool solved;
32
+  struct timespec begin, end;
33
+  k = 0;
34
+
35
+  clock_gettime(CLOCK_MONOTONIC, &begin);
36
+  while (1) {
37
+    k++;
38
+    solved = true;
39
+    for (i = 1; i < dimension - 1; i++) {
40
+      for (j = 1; j < dimension - 1; j++) {
41
+        double previous = array[i][j];
42
+        double avg = (array[i][j - 1] + array[i][j + 1] + array[i - 1][j] + 
43
+            array[i + 1][j]) / 4.0;
44
+        array[i][j] = avg;
45
+        if (solved == true && fabs(avg - previous) > precision) {
46
+          solved = false;
47
+        }
48
+      }
49
+    }
50
+    if (solved == true) {
51
+      clock_gettime(CLOCK_MONOTONIC, &end);
52
+      if (allow_print) printf("time taken: %Lf\n", time_seconds(begin, end));
53
+      return k;
54
+    }
55
+  }
56
+}
57
+
58
+double **constructBigArray(double top, double bottom, double left, 
59
+    double right, long unsigned int n) {
60
+  long unsigned int i, j, k;
61
+  double **arr = (double **) malloc(n * sizeof(double *));
62
+  for (k = 0; k < n; k++) {
63
+    arr[k] = (double *) malloc(n * sizeof(double));
64
+  }
65
+  for (i = 0; i < n; i++) {
66
+    for (j = 0; j < n; j++) {
67
+      if (i == 0) {
68
+        if (j == 0 || j == n - 1) arr[i][j] = 0;
69
+        else arr[i][j] = top;
70
+      } else if (i == n - 1) {
71
+        if (j == 0 || j == n - 1) arr[i][j] = 0;
72
+        else arr[i][j] = bottom;
73
+      } else if (j == 0) {
74
+        arr[i][j] = left;
75
+      } else if (j == n - 1) {
76
+        arr[i][j] = right;
77
+      } else {
78
+        arr[i][j] = 0;
79
+      }
80
+    }
81
+  }
82
+  return arr;
83
+}
84
+
85
+typedef struct arg_data {
86
+  double precision;
87
+  unsigned long dimension;
88
+  double top_start;
89
+  double bottom_start;
90
+  double left_start;
91
+  double right_start;
92
+} ARG_DATA_T;
93
+
94
+int argsContain(const char *val, char **arr, int size) {
95
+  int i;
96
+  for (i = 0; i < size; i++) {
97
+    if (strcmp(arr[i], val) == 0)
98
+      return i;
99
+  }
100
+  return -1;
101
+}
102
+
103
+ARG_DATA_T *parseArgs(int argc, char **argv) {
104
+  int tmp;
105
+  ARG_DATA_T *args = (ARG_DATA_T *) malloc(sizeof(ARG_DATA_T));
106
+  allow_print = true;
107
+  if ((tmp = argsContain("-p", argv, argc)) >= 0 || 
108
+      (tmp = argsContain("--precision", argv, argc)) >= 0) {
109
+    args->precision = strtod(argv[tmp + 1], NULL);
110
+  } else {
111
+    args->precision = 0.00001;
112
+  }
113
+  if ((tmp = argsContain("-d", argv, argc)) >= 0 
114
+      || (tmp = argsContain("--dimension", argv, argc)) >= 0) {
115
+    args->dimension = (unsigned long) strtol(argv[tmp + 1], NULL, 10);
116
+  } else {
117
+    args->dimension = 10;
118
+  }
119
+  if ((tmp = argsContain("-t", argv, argc)) >= 0 
120
+      || (tmp = argsContain("--top", argv, argc)) >= 0) {
121
+    args->top_start = strtod(argv[tmp + 1], NULL);
122
+  } else {
123
+    args->top_start = 1;
124
+  }
125
+  if ((tmp = argsContain("-l", argv, argc)) >= 0 
126
+      || (tmp = argsContain("--left", argv, argc)) >= 0) {
127
+    args->left_start = strtod(argv[tmp + 1], NULL);
128
+  } else {
129
+    args->left_start = 2;
130
+  }
131
+  if ((tmp = argsContain("-r", argv, argc)) >= 0 
132
+      || (tmp = argsContain("--right", argv, argc)) >= 0) {
133
+    args->right_start = strtod(argv[tmp + 1], NULL);
134
+  } else {
135
+    args->right_start = 4;
136
+  }
137
+  if ((tmp = argsContain("-b", argv, argc)) >= 0 
138
+      || (tmp = argsContain("--bottom", argv, argc)) >= 0) {
139
+    args->bottom_start = strtod(argv[tmp + 1], NULL);
140
+  } else {
141
+    args->bottom_start = 3;
142
+  }
143
+  if ((tmp = argsContain("-q", argv, argc)) > 0 ||
144
+      (tmp = argsContain("--quiet", argv, argc)) >= 0) {
145
+    allow_print = false;
146
+  }
147
+  if (argsContain("-h", argv, argc) >= 0 
148
+      || argsContain("--help", argv, argc) >= 0) {
149
+    printf("Usage: %s [-p N] [-d N] [-t N] [-l N] [-r N] [-b N]\n",
150
+        argv[0]);
151
+    printf("Constructs a 2D array of doubles, with values from arguments "
152
+        "of size dimension^2 and performs relaxation method"
153
+        " to given precision.\n");
154
+    printf("Options:\n");
155
+    printf("-p/--precision: A double value to specify precision. "
156
+        "Default: 0.0001\n");
157
+    printf("-d/--dimension: A long value to specify width and height of "
158
+        "grid. Default: 10\n");
159
+    printf("-t/--top: A double indicating the values along the top of the "
160
+        "array. Default: 1.0\n");
161
+    printf("-l/--left: A double indicating the values along the left side"
162
+        " of the array. Default: 2.0\n");
163
+    printf("-b/--bottom: A double indicating the values along the bottom "
164
+        "of the array. Default: 3.0\n");
165
+    printf("-r/--right: A double indicating the values along the right "
166
+        "side of the array. Default: 4.0\n");
167
+    printf("-q/--quiet: Disables all printing except the result.\n");
168
+    exit(0);
169
+  }
170
+  return args;
171
+}
172
+
173
+
174
+int main(int argc, char **argv) {
175
+  unsigned long iter;
176
+  ARG_DATA_T *args;
177
+
178
+  args = parseArgs(argc, argv);
179
+
180
+  double **array = constructBigArray(args->top_start, args->bottom_start, 
181
+      args->left_start, args->right_start, args->dimension);
182
+
183
+  iter = solveArray(array, args->dimension, args->precision);
184
+  if (allow_print) printf("completed with iters: %lu\n", iter);
185
+  printSquare(array, args->dimension);
186
+
187
+}