Practice exam 2¶
Question 1¶
part a)¶
Define the term "race condition"?
part b)¶
What is the main purpose of a sync.Mutex?
Question 2¶
part a)¶
What does it mean that Go channels are typed?
part b)¶
Consider:
ch := make(chan int)
What characterizes an unbuffered channel?
Question 3¶
part a)¶
Derive the fundamental limit of speedup according to Amdahl's law.
part b)¶
Explain the key idea behind Gustafson's law, and give the formula describing that law. (You don't need to derive the final formula of Gustafson's law.)
Question 4¶
Consider the following Go program that computes a convolution in 1 dimension:
package main
import (
"fmt"
)
func main() {
x := []float64{1, 2, 3, 4}
h := []float64{1, 1}
y := make([]float64, len(x)+len(h)-1)
for n := 0; n < len(y); n++ {
go func(n int) {
var sum float64
for k := 0; k < len(h); k++ {
if n-k >= 0 && n-k < len(x) {
sum += x[n-k] * h[k]
}
}
y[n] = sum
}(n)
}
fmt.Println(y)
}
part a)¶
What is the expected correct result of this computation for the given x and h?
part b)¶
Will this program reliably print the correct result? Briefly explain why or why not.
part c)¶
Identify one concurrency issue in this program and describe how to fix it (no need to write full code).
Hint: Which goroutine will run the
Printlnstatement?
Question 5¶
Recall the definition of discrete convolution:
where the subscript denotes the index of a vector, for example: \(h_k\) is the \(k\)-th element of the vector \(h\).
You are given:
- Signal
xof lengthN - Kernel
hof lengthM - Number of workers
k
part a)¶
Write a sequential Go function:
func Conv1D(x, h []float64) []float64
that computes the convolution using reflective padding: \(x_{n−k} = x_{\text{reflect}(n-k,N)}\) where the reflection function is
part b)¶
Now describe (in words or pseudocode) how to partition the work among k goroutines
so that each computes a subset of output indices.
part c)¶
Write a Go function:
func ParallelConv1D(x, h []float64, k int) []float64
that:
- Spawns
kgoroutines - Assigns each a contiguous chunk of output indices
- Uses a
sync.WaitGroup - Writes results into a shared preallocated slice
(You may omit minor syntax details.)
part d)¶
Briefly explain if this problem needs channels or mutexes. If it does not, explain why not.
Question 6¶
Consider the following Go code:
package main
import (
"sync"
)
func worker(x, h, y []float64, start, end int, wg *sync.WaitGroup) {
defer wg.Done()
for n := start; n < end; n++ {
sum := 0.0
for k := 0; k < len(h); k++ {
idx := n - k
if idx >= 0 && idx < len(x) {
sum += x[idx] * h[k]
}
}
y[n] = sum
}
}
func ParallelCompute(x, h []float64, k int) []float64 {
N := len(x)
M := len(h)
y := make([]float64, N+M-1)
var wg sync.WaitGroup
chunk := len(y) / k
for i := 0; i < k; i++ {
start := i * chunk
end := start + chunk
if i == k-1 {
end = len(y)
}
wg.Add(1)
go worker(x, h, y, start, end, &wg)
}
wg.Wait()
return y
}
part a)¶
What is the purpose of the worker function? Be specific about its inputs and outputs.
part b)¶
Explain how the output indices are divided among goroutines.
part c)¶
Why is it safe for multiple goroutines to write to the slice y without additional
synchronization?
part d)¶
Modify or describe how you would modify the code so that:
Instead of contiguous chunks, each goroutine processes interleaved indices (e.g.,
goroutine i processes i, i+k, i+2k, ...).
Provide pseudocode or a short code snippet.