Every time I started to learn a new programming language that supports multi-threading, the “hello world” program to me is vector adding. Vector adding is very simple in logic, and involves almost every fundamental operations that a parallel program would use, such as creating threads, waiting for threads finished, and setting mutex, etc. So, implementing a vector add program in go is a good stepping stone for me to learn how to use goroutine to achieve concurrency.
Before started I did not expect achieving concurrency is that easy in golang. Only one line is needed to create a single thread, compared with how it goes in C++/Python – adding long and clumsy clauses to create threadpool and create threads, that is so easy. Thus I did not pay much effort and get the vector adding program as following.
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
)
func vec_add(a, b, c []int) {
// assert(len(a) == len(b))
if len(a) != len(b) {
panic("[!] length of two array are not the same\n")
}
/* thread function of vector add */
routine_add := func(start, end int, wg *sync.WaitGroup) {
for i := start; i < end; i++ {
c[i] = a[i] + b[i]
}
wg.Done()
return
}
fmt.Println("[+] using", runtime.NumCPU(), "cpu cores")
routine_bound := len(a) / runtime.NumCPU()
routine_remain := len(a) % runtime.NumCPU()
var wg sync.WaitGroup
/* spawn goroutines to work */
for i := 0; i < runtime.NumCPU(); i++ {
var bound int
if i == runtime.NumCPU()-1 {
bound = routine_remain + routine_bound
} else {
bound = routine_bound
}
wg.Add(1)
start := routine_bound * i
end := routine_bound*i + bound
go routine_add(start, end, &wg)
}
/* wait until all goroutine finished */
wg.Wait()
fmt.Println("[+] vec_add finished")
}
func main() {
array_size := 25
/* randomly assign values to elements in vector a, b*/
a := rand.Perm(array_size)
b := rand.Perm(array_size)
for i, _ := range a {
a[i]++
b[i]++
}
/* allocate a slice to store the result */
c := make([]int, array_size)
/* call kernel function*/
vec_add(a, b, c)
/* print processed results */
print_array := func(id string, arr []int) {
fmt.Print("[-] Array ", id)
for i,_:=range arr {
fmt.Print(" ", arr[i])
}
fmt.Println()
}
print_array("a", a)
print_array("b", b)
print_array("c", c)
}
Output:
$ go build vec_add.go -o vec_add
$ vec_add
[+] using 8 cpu cores
[+] vec_add finished
[-] Array a 22 5 3 14 11 1 20 12 8 6 24 19 10 15 7 9 2 21 18 4 17 23 25 16 13
[-] Array b 11 6 4 25 1 14 15 24 3 12 18 10 9 20 2 21 22 5 16 19 13 17 7 23 8
[-] Array c 33 11 7 39 12 15 35 36 11 18 42 29 19 35 9 30 24 26 34 23 30 40 32 39 21