Skip to content

Commit

Permalink
Update Thu Oct 28 10:58:03 PDT 2021
Browse files Browse the repository at this point in the history
  • Loading branch information
zedchance committed Oct 28, 2021
1 parent a7749c7 commit b03ff91
Show file tree
Hide file tree
Showing 927 changed files with 246,429 additions and 5,175 deletions.
97 changes: 97 additions & 0 deletions content/CS139/CS139-lecture-20211021.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
title: "CS139-lecture-20211021"
# date: 2021-10-21T13:19:01-07:00
draft: false
bookToc: true
tags: ["synchronization", "semaphores"]
---

## Process synchronization

![image_2021-10-21-13-34-33](/notes/image_2021-10-21-13-34-33.png)
![image_2021-10-21-13-34-40](/notes/image_2021-10-21-13-34-40.png)
![image_2021-10-21-13-34-45](/notes/image_2021-10-21-13-34-45.png)

### Race condition

![image_2021-10-21-13-34-50](/notes/image_2021-10-21-13-34-50.png)
![image_2021-10-21-13-35-04](/notes/image_2021-10-21-13-35-04.png)
![image_2021-10-21-13-35-09](/notes/image_2021-10-21-13-35-09.png)
![image_2021-10-21-13-36-09](/notes/image_2021-10-21-13-36-09.png)

### Critical selection and mutual exclusion

![image_2021-10-21-13-37-17](/notes/image_2021-10-21-13-37-17.png)

In the above code examples, the `counter++` and `counter--` are considered a critical section.

![image_2021-10-21-13-38-05](/notes/image_2021-10-21-13-38-05.png)
![image_2021-10-21-13-40-00](/notes/image_2021-10-21-13-40-00.png)
![image_2021-10-21-13-40-06](/notes/image_2021-10-21-13-40-06.png)
![image_2021-10-21-13-40-11](/notes/image_2021-10-21-13-40-11.png)
![image_2021-10-21-13-40-34](/notes/image_2021-10-21-13-40-34.png)
![image_2021-10-21-13-40-57](/notes/image_2021-10-21-13-40-57.png)
![image_2021-10-21-13-43-30](/notes/image_2021-10-21-13-43-30.png)

### How to implement mutual exclusion

![image_2021-10-21-13-43-40](/notes/image_2021-10-21-13-43-40.png)
![image_2021-10-21-13-43-45](/notes/image_2021-10-21-13-43-45.png)
![image_2021-10-21-13-44-09](/notes/image_2021-10-21-13-44-09.png)

{{< hint info >}}
Note: This solution isn't fully correct.
{{< /hint >}}

```
producer:
while (lock == 0)
lock = 1
put
lock = 0
consumer:
while (lock == 0)
lock = 1
fetch
lock = 0
```

The problem with this code is that if the OS does a context switch during a critical section, it can create an error.

![image_2021-10-21-13-52-42](/notes/image_2021-10-21-13-52-42.png)
![image_2021-10-21-13-55-12](/notes/image_2021-10-21-13-55-12.png)

One solution is to disable interrupts.

![image_2021-10-21-13-55-27](/notes/image_2021-10-21-13-55-27.png)
![image_2021-10-21-13-55-31](/notes/image_2021-10-21-13-55-31.png)

### Sync hardware, spin locks and compare-and-swap

![image_2021-10-21-13-59-36](/notes/image_2021-10-21-13-59-36.png)
![image_2021-10-21-14-01-51](/notes/image_2021-10-21-14-01-51.png)
![image_2021-10-21-14-09-56](/notes/image_2021-10-21-14-09-56.png)
![image_2021-10-21-14-18-04](/notes/image_2021-10-21-14-18-04.png)
![image_2021-10-21-14-19-45](/notes/image_2021-10-21-14-19-45.png)

### Mutex locks

Mutex locks allow the OS to provide a mutual exclusion as a service.

![image_2021-10-21-14-20-05](/notes/image_2021-10-21-14-20-05.png)
![image_2021-10-21-14-21-32](/notes/image_2021-10-21-14-21-32.png)
![image_2021-10-21-14-23-46](/notes/image_2021-10-21-14-23-46.png)

### Semaphores

![image_2021-10-21-14-23-56](/notes/image_2021-10-21-14-23-56.png)

A semaphore is simply a non-negative interger that has two valid operations.

![image_2021-10-21-14-26-59](/notes/image_2021-10-21-14-26-59.png)
![image_2021-10-21-14-30-05](/notes/image_2021-10-21-14-30-05.png)
![image_2021-10-21-14-32-26](/notes/image_2021-10-21-14-32-26.png)
![image_2021-10-21-14-41-34](/notes/image_2021-10-21-14-41-34.png)
![image_2021-10-21-14-44-10](/notes/image_2021-10-21-14-44-10.png)

128 changes: 128 additions & 0 deletions content/CS139/CS139-lecture-20211026.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: "CS139-lecture-20211026"
# date: 2021-10-26T13:15:48-07:00
draft: false
bookToc: true
tags: ["synchronization", "semaphores"]
---

## Synchronization cont.


![image_2021-10-26-13-49-37](/notes/image_2021-10-26-13-49-37.png)

Since these could output in any order, we can setup semaphores to ensure the run order.

![image_2021-10-26-13-49-46](/notes/image_2021-10-26-13-49-46.png)

### Bounded buffer problem

![image_2021-10-26-13-49-57](/notes/image_2021-10-26-13-49-57.png)

- `full` and `empty` are counting semaphores.
- `full` notifies consumers how many items are there
- `empty` notifies producers how many empty slots available
- `mutex` is a binary semaphore.

![image_2021-10-26-13-54-46](/notes/image_2021-10-26-13-54-46.png)

- `mutex` starts at 1, "unlocked", so the first process can have mutual exclusion
- `full` is set to 0, and `empty` is set to n, because all slots are available

![image_2021-10-26-13-57-35](/notes/image_2021-10-26-13-57-35.png)
![image_2021-10-26-13-58-44](/notes/image_2021-10-26-13-58-44.png)

- The first `wait(empty)` checks if there is an empty slot
- `wait(mutex)` checks if there is a process accessing the shared buffer
- `wait(full)` checks if there is any items to consume

Consider switching the order of the first to `wait` functions, to:

```
wait(mutex);
wait(empty);
```

This would have the consequences of being able to get mutual exclusion first, then all other processes would be prevented from running.
But then if the buffer is already full it won't ever get past the `wait(empty)`.
So, the order cannot be swapped.

Next, consider switching the order of the `signal` functions:

```
signal(full);
signal(mutex);
```

This is actually okay because the order of the unlock does not matter as much.
The other process is waiting for both locks to continue.

![image_2021-10-26-14-10-59](/notes/image_2021-10-26-14-10-59.png)
![image_2021-10-26-14-11-20](/notes/image_2021-10-26-14-11-20.png)
![image_2021-10-26-14-11-24](/notes/image_2021-10-26-14-11-24.png)

### The readers writers problem

![image_2021-10-26-14-11-38](/notes/image_2021-10-26-14-11-38.png)

This is a slight difference in the producer consumer problem, because readers actually don't change the shared buffer (just read it).
Also, any number of readers can access data simultaneously.

![image_2021-10-26-14-14-09](/notes/image_2021-10-26-14-14-09.png)

- multiple readers are reading database
- new readers may join
- new writer must waiting

![image_2021-10-26-14-16-37](/notes/image_2021-10-26-14-16-37.png)

- new readers must wait
- new writers must wait

![image_2021-10-26-14-18-09](/notes/image_2021-10-26-14-18-09.png)

- if a new writer arrives at {{<k>}} t = 1 {{</k>}}, it waits
- if a new reader arrives at {{<k>}} t=2 {{</k>}}, there are 2 variations:
- skip the line and join the other readers, prioritizing readers
- wait behind the writer, prioritizing writers

![image_2021-10-26-14-20-41](/notes/image_2021-10-26-14-20-41.png)
![image_2021-10-26-14-22-45](/notes/image_2021-10-26-14-22-45.png)

Why ensure mutual exclusion for updating `readcount`?
We need to make sure that the `readcount` variable is being updated atomically.

{{< hint info >}}
Note: The solution to the second readers writers problem (prioritizing writers) is more complex.
It requires five variables.
{{< /hint >}}

### The dining philosophers problem

![image_2021-10-26-14-26-50](/notes/image_2021-10-26-14-26-50.png)
![image_2021-10-26-14-27-15](/notes/image_2021-10-26-14-27-15.png)
![image_2021-10-26-14-28-23](/notes/image_2021-10-26-14-28-23.png)

- you can only use your adjacent chopsticks
- you can only pick up one chopstick each turn
- you can eat when you have both chopsticks
- the goal is to order the picking up and placing of chopsticks without deadlock or starvation

So if everyone starts by picking up the left chopstick, then the right chopstick, it will end up in deadlock.

We can treat each philosopher as a process, and the chopstick as a competing resource.

![image_2021-10-26-14-32-56](/notes/image_2021-10-26-14-32-56.png)

- `wait(fork[i])` waits for the left chopstick
- `wait(fork[i+1 % 5]);` waits for the right chopstick
- then they may eat
- then they put the chopsticks back down right then left using the `signal` functions
- however, this current state results in deadlock

{{< hint info >}}
Note: `fork` here is the chopstick, for brevity.
{{< /hint >}}

![image_2021-10-26-14-40-02](/notes/image_2021-10-26-14-40-02.png)

163 changes: 163 additions & 0 deletions content/CS140/CS140-lecture-20211014.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
title: "CS140-lecture-20211014"
# date: 2021-10-14T14:03:41-07:00
draft: false
bookToc: true
tags: ["recurrences", "quicksort"]
---

## Recurrences cont

### Recursion tree method

Another method to solve recurrences is to draw a recursion tree, where each node gets a cost.
The cost of each node is the additional work done on each recursive call (not recursive call itself).

![image_2021-10-14-14-05-32](/notes/image_2021-10-14-14-05-32.png)

The leaf nodes are the base cases.
The idea is to identify a pattern, and use a known series to evaluate that pattern.

![image_2021-10-14-14-13-14](/notes/image_2021-10-14-14-13-14.png)

Then, after identifying the sum of each level of the tree, you then sum all the level's themselves (except the base level, the leaves).
Notice that the height of this tree is {{<k>}} \lg n {{</k>}}.
We can sum all the levels up to the last one (notice that {{<k>}} k {{</k>}} goes to {{<k>}} \lg n {{</k>}}).
Then, add the cost of the last level (there are {{<k>}} 2^{\lg n} = n {{</k>}} leaf nodes).
So we can describe this summation like this:

{{<k display>}}
\begin{aligned}
\sum_{k=1}^{\lg n} \frac{n^2}{2^k} + n W(1) &= \frac{\frac{1}{2}^{(\lg n - 1) + 1} -1}{\frac{1}{2} - 1} n^2 + \Theta(n) \\
&= \left( 2 - \frac{2}{n} \right) n^2 + \Theta(n) \\
&= \Theta(n^2)
\end{aligned}
{{</k>}}

![image_2021-10-14-14-29-16](/notes/image_2021-10-14-14-29-16.png)

Its okay to also let {{<k>}} n \to \infty {{</k>}} and get an approximation:

![image_2021-10-14-14-32-56](/notes/image_2021-10-14-14-32-56.png)

However, don't make the approximation too loose.

![image_2021-10-14-14-38-56](/notes/image_2021-10-14-14-38-56.png)

{{<k display>}}
\begin{aligned}
\sum_{k=0}^{\log_4 n - 1} 3^k c \left( \frac{n}{4^k} \right)^2 + n^{\log_3 4} T(1) &= cn^2 \sum_{k=0}^{\log_4 n - 1} \left( \frac{3}{4^2} \right)^k + \Theta(n^{\log_3 4})
\end{aligned}
{{</k>}}

![image_2021-10-14-14-49-22](/notes/image_2021-10-14-14-49-22.png)
![image_2021-10-14-14-50-56](/notes/image_2021-10-14-14-50-56.png)
![image_2021-10-14-14-53-54](/notes/image_2021-10-14-14-53-54.png)
![image_2021-10-14-14-55-57](/notes/image_2021-10-14-14-55-57.png)
![image_2021-10-14-14-57-05](/notes/image_2021-10-14-14-57-05.png)

This gives an overall complexity of {{<k>}} \Theta(n^2) {{</k>}}.

Lets solve this same problem using the substitution method.

![image_2021-10-14-15-06-12](/notes/image_2021-10-14-15-06-12.png)

Once this is setup, we can solve for {{<k>}} d {{</k>}}.

![image_2021-10-14-15-08-20](/notes/image_2021-10-14-15-08-20.png)

Now, lets try an unbalanced tree:

![image_2021-10-14-15-16-04](/notes/image_2021-10-14-15-16-04.png)

When we have an unbalanced tree, we need to find the height of the root to leaf nodes that reach the base case the fastest, and the height of the root to leaf nodes that reach the base case the slowest.
When we start to sum each level, we can see each level costs {{<k>}} n {{</k>}}, until the tree becomes unbalanced.
At this point, the cost is {{<k>}} \leq n {{</k>}}.

<blockquote class="book-hint info">
Note: If we assume the cost at each level is in fact {{<k>}} n {{</k>}}, then we will find an upper bound for the
complexity.
If we assume that the tree ends at the shortest path from root to leaf, then we will get the lower bound.
</blockquote>

![image_2021-10-14-15-22-25](/notes/image_2021-10-14-15-22-25.png)

If we find the lower bound and upper bound, and they are the same, then we can say we found the exact bound.
So, the lower bound is

![image_2021-10-14-15-28-05](/notes/image_2021-10-14-15-28-05.png)
![image_2021-10-14-15-29-04](/notes/image_2021-10-14-15-29-04.png)

Now that we have found a lower bound, lets find the upper bound:

![image_2021-10-14-15-35-09](/notes/image_2021-10-14-15-35-09.png)

<blockquote class="book-hint info">
Note: In the term {{<k>}} n^{\log_\frac{3}{2} 2} {{</k>}}, since the base in the log is less than the number, it means
that the overall value is {{<k>}} > 1 {{</k>}}, so the polynomial term dominates the linearithmic term.
</blockquote>

![image_2021-10-14-15-37-11](/notes/image_2021-10-14-15-37-11.png)

Since our lower and upper bounds are not equivalent, we cannot say we have an exact bound (big theta).

We can get a better assumption but not overestimating as much.
If we assume that each level costs the same all the way down, then NOT include the leaf node costs,
This would give our upper bound as {{<k>}} O(n \lg n) {{</k>}}, which matches our lower bound.
That means we have an overall complexity of {{<k>}} \Theta(n \lg n) {{</k>}}.

![image_2021-10-14-15-44-48](/notes/image_2021-10-14-15-44-48.png)
![image_2021-10-14-15-45-12](/notes/image_2021-10-14-15-45-12.png)

### Quicksort recurrence

![image_2021-10-14-15-45-35](/notes/image_2021-10-14-15-45-35.png)
![image_2021-10-14-15-46-32](/notes/image_2021-10-14-15-46-32.png)
![image_2021-10-14-15-47-41](/notes/image_2021-10-14-15-47-41.png)
![image_2021-10-14-15-51-32](/notes/image_2021-10-14-15-51-32.png)
![image_2021-10-14-15-51-46](/notes/image_2021-10-14-15-51-46.png)
![image_2021-10-14-15-54-19](/notes/image_2021-10-14-15-54-19.png)

### Master's method

Masters method can be used when recurrences are in this exact form:

![image_2021-10-14-16-01-42](/notes/image_2021-10-14-16-01-42.png)

Generally, we'll have 3 cases:

![image_2021-10-14-16-03-53](/notes/image_2021-10-14-16-03-53.png)

Lets visualize the first case with a recursion tree:

![image_2021-10-14-16-11-50](/notes/image_2021-10-14-16-11-50.png)
![image_2021-10-14-16-14-58](/notes/image_2021-10-14-16-14-58.png)


### Mergesort recurrence using Master's method

So first we identify our variables {{<k>}} a,b,f(n) {{</k>}} and go thru the cases.

![image_2021-10-14-16-17-24](/notes/image_2021-10-14-16-17-24.png)

The first case is false because {{<k>}} \Theta(n) \not = O(n^{1-\epsilon}) {{</k>}}.
So we check the second case, which is true because {{<k>}} \Theta(n) = \Theta(n) {{</k>}}.

![image_2021-10-14-16-19-21](/notes/image_2021-10-14-16-19-21.png)

So the Master's method gives us a runtime complexity of {{<k>}} \Theta(n \lg n) {{</k>}}, which is the average case runtime of mergesort.

### Some more recurrence examples using Master's method

![image_2021-10-14-16-20-21](/notes/image_2021-10-14-16-20-21.png)
![image_2021-10-14-16-22-42](/notes/image_2021-10-14-16-22-42.png)

The third case works for this example, and you usually need to find a {{<k>}} \epsilon {{</k>}} value between 0 and 1.

![image_2021-10-14-16-24-15](/notes/image_2021-10-14-16-24-15.png)
![image_2021-10-14-16-26-00](/notes/image_2021-10-14-16-26-00.png)
![image_2021-10-14-16-30-38](/notes/image_2021-10-14-16-30-38.png)
![image_2021-10-14-16-33-41](/notes/image_2021-10-14-16-33-41.png)

In this example, all 3 cases fail, so another method of solving recurrences it needed besides Master's method.

0 comments on commit b03ff91

Please sign in to comment.