-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathodd-even-par-static.cpp
More file actions
209 lines (173 loc) · 5.98 KB
/
odd-even-par-static.cpp
File metadata and controls
209 lines (173 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
* ---- odd-even-par-static.cpp
*
* Parallel version of the Odd-even Sort using pthread with static
* division of the work.
* Uses atomic variables and active barriers for thread synchronization.
* Takes 3 or 4 arguments:
* N : number of array elements
* niter : upper bound for the number of iterations (optional)
* seed : seed for the problem generation
* nw : number of workers
*
* Compile with
* g++ -g -O3 -std=c++17 -ftree-vectorize -pthread odd-even-par-static.cpp -o odd-even-par-static
*
* Compile with -DPRINT to display the vector after every iteration
* Compile with -DSTATS to print extended statistics (for each thread) at the end
*/
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>
#include <atomic>
#include <cassert>
#include <mutex>
#include <thread>
#include "business_logic.cpp"
#include "utils.cpp"
#include "ActiveBarrier.cpp"
#include "Timer.cpp"
using namespace std;
using namespace std::chrono;
int main(int argc, char const *argv[])
{
if(argc < 4) {
cout << "Usage: " << argv[0] << " N [niter] seed nw" << endl;
cout << " N : number of array elements" << endl
<< " niter : number of iterations (optional)" << endl
<< " seed : seed for the random number generator (-1 => reversed vector)" << endl
<< " nw : number of workers" << endl;
return -1;
}
// Command line arguments
int N = atoi(argv[1]);
int niter = (argc >= 5) ? atoi(argv[2]) : 0;
int seed = (argc >= 5) ? atoi(argv[3]) : atoi(argv[2]);
int nw = (argc >= 5) ? atoi(argv[4]) : atoi(argv[3]);
// Statistics
unsigned long iter = 0;
#if STATS
mutex print_m; // For mutual exclusive prints
#endif
// Vector to be sorted
vector<int> A(N);
if(seed == -1) fill_reversed(A);
else if(niter == 0) fill_random(A, seed);
else fill_for_fixed_iterations(A, seed, niter);
#if PRINT
cout << "INIT ";
print_vector(A);
#endif
auto start = high_resolution_clock::now();
// Variable for the stopping condition
atomic<int> swapped = 0;
// Variables and structures for synchronization
atomic<bool> terminate = false;
ActiveBarrier even_barrier(nw), odd_barrier(nw);
auto worker_fun = [&] (int t)
{
#if STATS
unsigned long even_time = 0, even_swaps = 0; // Statistics for even phase
unsigned long odd_time = 0, odd_swaps = 0; // Statistics for odd phase
unsigned long barrier1_t = 0, barrier2_t = 0, update_t = 0; // Overhead statistics
unsigned long temp;
#endif
int E = N/2; // Number of couples in the even phase
int start_e = (t == 0) ? 0 : 2*( t*(E/nw) + min(E%nw, t) );
int end_e = (t == nw-1) ? 2*E : start_e + 2*( (E/nw) + (t < E%nw) );
int O = (N-1)/2; // Number of couples in the odd phae
int start_o = 1 + ((t == 0) ? 0 : 2*( t*(O/nw) + min(O%nw, t) ));
int end_o = (t == nw-1) ? 1 + 2*O : start_o + 2*( (O/nw) + (t < O%nw) );
int swapped_prv, nswaps;
while(!terminate) {
// Even phase
#if STATS
{ Timer t_even(&temp);
#endif
nswaps = sort_couples(A, start_e, end_e);
swapped_prv = nswaps;
#if STATS
} even_time += temp;
even_swaps += nswaps;
#endif
// Barrier, wait for all the workers to reach it
#if STATS
{ Timer t_b1(&temp);
#endif
odd_barrier.wait_all();
#if STATS
} barrier1_t += temp;
#endif
// Odd phase
#if STATS
{ Timer t_odd(&temp);
#endif
nswaps = sort_couples(A, start_o, end_o);
swapped_prv |= nswaps;
#if STATS
} odd_time += temp;
odd_swaps += nswaps;
#endif
// Atomicly update the shared variable
#if STATS
{ Timer t_update(&temp);
#endif
swapped |= swapped_prv;
#if STATS
} update_t += temp;
#endif
// Barrier, wait for the master thread (main) to reset the barrier
#if STATS
{ Timer t_b2(&temp);
#endif
even_barrier.wait_reset();
#if STATS
} barrier2_t += temp;
#endif
} // End of loop
#if STATS
{
unique_lock<mutex> print_lock(print_m);
cout << "Worker " << t << ":" << endl
<< "\tAvg even phase " << ((float)even_time)/iter/1000 << " usecs"
<< " (" << even_swaps/iter << " swaps)" << endl
<< "\tAvg barrier 1 " << ((float)barrier1_t)/iter/1000 << " usecs" << endl
<< "\tAvg odd phase " << ((float)odd_time)/iter/1000 << " usecs"
<< " (" << odd_swaps/iter << " swaps)" << endl
<< "\tAvg update " << ((float)update_t)/iter/1000 << " usecs" << endl
<< "\tAvg barrier 2 " << ((float)barrier2_t)/iter/1000 << " usecs" << endl << endl;
}
#endif
return;
};
// Start the workers
vector<thread*> workers(nw);
for(int i=0; i<nw; i++)
workers[i] = new thread(worker_fun, i);
while(true) {
iter++;
// Wait for the end of the iteration
even_barrier.wait_all_nomod();
#if PRINT
cout << "ITER ";
print_vector(A);
#endif
// Check for termination
if(!swapped) break;
odd_barrier.reset();
swapped = 0;
even_barrier.reset();
}
terminate = true; // Send termination signal
even_barrier.reset();
for(int i=0; i<nw; i++)
workers[i]->join();
auto stop = high_resolution_clock::now();
auto total_time = duration_cast<microseconds>(stop - start).count();
cout << "Total time with " << nw << " workers: " << ((float)total_time)/1000.0 << " msecs" << endl;
cout << "Iterations: " << iter << " (" << ((float)total_time)/iter << " usecs per iteration)" << endl;
// Just to make sure it works for larger vectors
assert(is_sorted(A.begin(), A.end()));
return 0;
}