-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME
271 lines (230 loc) · 10.5 KB
/
README
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
yonilipman, danielle.kut
Yonatan Lipman (305629750), Danielle Kutner (204094460)
EX: 3
FILES:
README -- This file
Makefile -- makefile for the project, compiles the library and a demo
of its use called Search. creates tar and cleans the file
MapReduceFramework.cpp -- MapReduceFramework library implementation
Search.cpp -- Demo of the framework that searches for a substring in file names
in the given directories
REMARKS:
We designed the framework such that the main function runMapReduceFramework
runs each part of the process in different modules.
Specifically, the framework will create threads that handle the mapping and
reduction. each thread will handle a chunk of the given data at a time and
we make sure that there is a seperation between the shared data structures
using pthread's mutex feature.
In order to tell between the different threads and their data structures and
mutexes, we used thread_local, which allows to have a global int that is unique
to each thread.
When shuffle is signaled to start working, it iterates over all of the
mapping containers and works on them. mapping in chunks ensures that we dont
work too hard when shuffling in each iteration over the containers so each time
the containers are small when not empty. we ensure that we emptied all of the
containers by iterating one final time after the mapping process is finished.
We used deque and map as the containers of choice and List when we had to
comply to the given header files.
Notice that each time that the framework runs, it is initialized and terminated
properly such that it is possible to run it multiple times in the same process.
ANSWERS:
1) Design:
The framework will create a pipe for each thread created.
Mapping and Shuffling:
Each thread has a pipe that reads chunks of k1 and v1 from the main
data given to read. ExecMap will write the emitted k2 and v2 to fd's that
are unique to each thread. Shuffle will read the k2 and v2's from each
fd that map wrote to and will write to a single fd that will hold the
shuffled data. shuffle will choose the pipe to read from using select.
Reduce:
Each thread has a pipe that reads from the shuffled data fd the k2 and the
list of v2's. ExecReduce will write the emitted k3 and v3 to the main
threads mapped and reduced data fd.
The data that is read and written will be composed entirely from pointers,
such that it is easier to read and write known chunks of bytes and let the
user handle the dereferencing of the pointers.
Generally lists are good enough for the needed result.
ExecMap could use lists of k1*,v1* and lists of k2*,v2* for storing the
mapped pointers. Shuffle would have a list of (k2*,list(v2*)).
ExecReduce would get shuffle's list and have its own list of k3*,v3*
that would be returned to the user.
In order for the consumers to know that there is no more data,
we will make use of boolean variables.
Such that when reading data for mapping, we will keep reading until the
var will turn false. Shuffle will use the select function to choose which
fd to read from when the fd is ready. when ExecMap is done writing, it will
close the fd thus select could use the fd to read from it using pipe.
when 0 will be read then shuffle will know that it is done with the fd
in context.
ExecReduce will work in a similar fashion and such will the end of the
frameworks run.
2) We would use 7 threads for multiThreadLevel.
in our implementation shuffle thread does not count as one of the thread in
multiThreadLevel (creating multiThreadLevel amount of
execMap and execReduce).
and ignoring main thread since it will waits (for join) most time framework
runs, this way 8 threads can run on each core in parallel manner:
7 will execute map and reduce actions, and one will shuffle.
3)
a. Utilizing multi-cores:
a- Nira:
Nira's thread will run on one core at most and will not utilize
multi-core actions.
there is no concurrency.
b- Moti:
we cannot know for sure if Moti's program will support multi-threading.
it is able to utilize seperate cores but won't necessarily do so.
c- Danny:
Danny uses user-level threads so one thread will run at any given time.
Concurrency is not achieved.
d- Galit:
Galit has the best approach, each thread is actually a process, so the
OS will try and take advantage of multi-core abilities: running each
process on an available core.
b. Creating sophisticated scheduler ability:
a- Nira:
each time main thread will get run time from the kernel, the thread
will run.
b- Moti:
OS responsible of scheduling kernel level threads,
it can be affected by using mutexes, condition variables, barriers...
c- Danny:
user level threads scheduling isnt difficult and can be implemented
using scheduling
algorithm (such as Round Robin that we implemented in ex2).
d- Galit:
OS would be in charge of scheduling each process and circumventing it
would be very difficult.
c. Communication between threads and processes:
a- Nira:
thread can easily communicate with itself
b- Moti:
threads share the same memory pool of their process. We can easily
implement a communication between memory using locks and flags.
c- Danny:
we can access the same memory for each thread by locks and flags,
communication is fast.
d- Galit:
communication between processes will take a long time since multiple
processes communication is done by reading and writing into the
pipe) which is slower then shared memory
communication (Danny and Moti).
d. Ability to progress while a thread is blocked:
a- Nira:
Nira has only one thread so in case it will be blocked, the whole
process will be blocked.
b- Moti:
in case one thread is blocked, there will be other thread that c
ontinue with the map reduce work ad the process will continue.
c- Danny:
User-level threads doesn't cannot progress while a certain thread
is blocked.
OS takes all user level threads as one unit, so the process will be
blocked.
d- Galit:
blocking one thread will not block others. OS will manage each process
and runs them on different cores when possible. If a thread is blocked
then the OS will run another process in its place.
e. Overall speed:
a- Nira:
In case context switch is expensive, Nira's solution will be
better then Moti's and Galit's since a single thread is running-
no context switching!
in most cases framework contains many items so the major time would
be spent in the map and reduce stages where more threads/processes
would save time using concurrency
b- Moti:
Moti's solution will be the faster among othe solutions.
creation and communication time are faster using
threads over processes (threads communicate
through shared memory in contrast to I/O actions). synchronization
of our shared data forces us to use mutexes and flags which also take
time but we think in overall this will probably be the best way to
implement this task.
c- Danny:
Danny uses user level threads, not creating separate processes or
kernel level threads.
His solution may still be faster than Nira's depending on the cost
vs benefit of the context switches.
But may be slower than others
for the same reason Nira's will most likely be slower. And may
be faster than the other's due to the lower cost of creating
user-level threads and doing context switches.
d- Galit:
Galit solution will be fast since Galit since it takes advantage of
any possible multi-cores available on the machine the
program runs on.
it can be slower than multithreading due to the time it takes to
create processes and the time it takes us to switch between them.
4)
Kernel-level thread:
Global variables
Heap
User-level thread:
Global variables
Heap
Process:
None
5) Livelock vs Deadlock:
Although they are similar in a way, we will notice that when in deadlock,
the thread will get stuck waiting for a condition that wont be fulfilled.
Livelock on the other hand wont have the threads waiting but it wont make
any progress since the threads will always respond to each other creating
an endless chain of replies to one another.
Deadlock example:
----------------------------
Process 0:
flag[0] = true;
while (flag[1]); // does nothing until false
/* critical section and actions */
flag[0] = false;
Process 1:
flag[1] = true;
while (flag[0]); // does nothing until false
/* critical section and actions */
flag[1] = false;
// notice that if both flags are true before getting to the while command,
// they will never reach the critical section and stay in deadlock.
Livelock example:
----------------------------
Process 0:
flag[0] = true;
while (flag[1])
{
flag[0] = false;
// wait constant time
flag[0] = true;
}
/* critical section and actions */
flag[0] = false;
Process 1:
flag[1] = true;
while (flag[0])
{
flag[1] = false;
// wait constant time
flag[1] = true;
}
/* critical section and actions */
flag[1] = false;
If both flags are true, then p0 makes flag0 false and p1 makes flag1 false
and then p0 makes flag0 true and p1 makes flag1 true we will experience
livelock.
6) Process|Arrival Time| Running Time| Priority (higher number-higher priority)
P1 0 10 1
P2 1 1 3
P3 3 2 2
P4 7 12 3
P5 8 1 1
RR (quantum = 2):
-Turnaround time: (18+2+4+19+4)/5 = 9.4
-Average wait time: 8+1+2+7+3)/5 = 4.2
FCFS:
-Turnaround time: (10+10+10+18+18)/5 = 13.2
-Average wait time: (0+9+8+6+17)/5 = 8
SRTF:
-Turnaround time: (14+1+2+19+1)/5 = 7.4
-Average wait time: (4+0+0+7+0)/5 = 2.2
Priority Scheduling:
-Turnaround time : (25+1+2+12+18)/5 = 11.6
-Average wait time : (15+0+0+0+17)/5