|
|
@@ -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
|
+}
|