1. Speedups

d)

For two processors, one with 1.5GGHz, 8 cores, other with 2.5GHz and 5 processors, figure out which is faster for .

Compute the speedup of each processor against 1 core, disregarding the clockspeed.


Now multiple the speedups with the clock frequency .

Da , ist option B besser.
Das funktioniert, da für beide gleicher Work ist, und durch multiplizieren mit Clock-Speed kann man sie dann vergleichen.

2. Pipelining

b)

Totale time = first cycle * (N - 1) * max(time_stage).

d)

Massive Rechenfehler lol
compute alle vergleiche independently, schau was optimal ist.

3. Task Graphs

b)

Wieviele Prozessoren sind nötig?

NICHT Zählen wieviel nodes per Layer und dann max nehmen!

  • man muss sich die einzelnen nodes pro layer anschauen und rechnen mit wievielen prozessoren alle schnell fertig sind

Layer 3 = 20, 30, 100, 60. Hier reichen 3, da 20 und 30 mit einem Prozessor fertig werden, bevor 100 fertig ist.

  • note, 20 + 30 + 60 > 100, also geht es nicht mit 2.

Dann ist die Execution time equal to critical Path. da wir mit mehr als 3 prozessoren keinen Speedup mehr kriegen

Deps

When drawing in the processing times, keep in mind the time at which node finishes is .

Check: wenn critical-path time < time mit split up noch nicht ideal split gefunden!

c)

Knoten mit 100 aufteilen 4 prozessoren mit critical path 300.
Knoten mit 80 autteilen 3 prozessoren mit critical path 300

solution mit 80 ist ideal.

4. Wait/notify

thread.interrupt() called den interrupt. Mit thread.join() kann man dann warten bis sie fertig sind.
Check das extends Thread gegeben ist.

ExamPile

public Exam pollExam() {
	synchronized (this) {
		while (exams.size() == 0) wait();
		// why no notifyAll?
		return exams.poll();
	}
}

Das notify ist hier nicht sinnvoll, da es ja keinen anderen wartenden thread interessiert, wenn ein exam wieder da ist

  • wir entfernen es ja hier direkt wieder ergo nur wir brauchen es sehen

Note: in addExam() ist wohl ein notify wir warten ja hier genau auf dieses Notify

Fork/Join Framework

Im Fork/Join Framework wird

  • RecursiveTask<Double> verwendet um einen Wert zurückzugeben.
  • RecursiveAction verwendet, wenn in-place alles geschieht

6. Locks

a)

Fair: A fair lock is acquired in the order it is requested.
“fair lock is acquired in the order the threads enter the doorway”

Deadlock-Free: A lock is deadlock free if at least one waiting thread acquires it eventually system wide progress.

Starvation-Free: A lock is starvation free if a thread that requested the lock will eventually acquire it.

Lock-free: infinitely often some method call completes, i.e. the system as a whole makes progress even if individual threads starve.

Wait-free: Every method call by every thread completes in a finite number of its own steps, regardless of speed or failure of other threads.

b)

Starvation-free + deadlock-free fair?

No, the filter-lock (or Dekker) for example is not fair.
“In a fair lock, threads cannot overtake each other. In a starvation free lock if threads A, B, C request the lock in that order, it is fine to give the lock to A, C, B (in that order). But this is not fair.”

c)

fair starvation + deadlock-free?

False: simple lock that never returns. It’s fair, but it’s also not starvation free.

7. Linearizability

  • Jede mit synchronized annotierte Methode hat einen Linearisierungspunkt. TRUE

    • ja weil, mutex auf dem Monitor des Objekts. Totally ordered and never overlap.
      • jeder beliebige Punkt in der Methode ist linearisierungspunkt
    • false, if we pass in non-synchronized objects into the method.
    • wait() in der Methode
  • Sequentiell konsistentes Speichermodell schreibt vor, das alle Aktionen eines Threads in PO sichtbar werden TRUE, def. SC

8. Barriers and Semaphores

a)

public synchronized void acquire() throws InterruptedException {
    while (counter <= 0) this.wait();
    counter--;
}
public synchronized void release() {
    counter++;
    this.notify();
}

Note notifyAll vs. notify here

  • both would work, but since only one thread can reacquire the lock only wake up one sleeping waiting on counter

b)

Learn that by heart!

9. MPI

b)

Eliminiert message-passing (MPI) alle möglichkeiten für deadlocks?

Nein sie müssen immer noch einen weg haben zu synchronizen, um zu funktionieren (barriers, etc…). Wir können immer noch auf messages waiting mit send, recv

Note: ssend, not send (for synchronous send).

10. Mixer

b)

Explain ABA, using an example.

“Some atomic operations, such as CAS are used to compare a value of a register to an old value, assuming if they are equal that the state of a datastructure did not change in between. This assumption can be wrong, i.e., if we use a node pool and delete and later reuse a node.”

c)

Two techniques to prevent ABA.


Usually the ABA problem applies to pointers. We use some bits of the address to use as a counter, incrementing it every time we change the pointer.
This counter might overflow, and thus it makes the ABA problem only less likely.

Another technique is to have a seperate datastructure that contains all elements which have been changed.

or use DCAS, LL/SC, Hazard Pointers.

d)

TATAS Lock.

TATAS lock uses non-atomic (but still volatile! otherwise no guarantee we ever get updated value) read first, which avoids the more expensive atomic-reads while the lock is still taken.

void lock(AtomicBoolean flag) {
	while (flag.getAndSet(true)) {
		while (flag.get()) {} // <- non atomic, but volatile read
	}
}
 
void unlock(AtomicBoolean flag) {
	flag.getAndSet(false);
}

volatile read compiles to an ordinary mov no fence, no lock prefix.
Hardware already guarantees Load-load and load-store ordering, the only thing the volatile does is constrain the JIT forbids hoisting the load out of the loop:

while (true)  { while (flag) {} }
// can be JITted to
if (flag) { while (true) {} } // hoisting

This read is cheaper than atomic RMW.

TATAS does not prevent “release storm” when false is written, all treads try to write (exponential backoff is needed).