Proper way to optimize multi-threading with WaitGroups and goroutines? - multithreading

I am trying to make my application run as fast as possible. I purchased a semi-powerful container off of Google Cloud and I am just itching to see how many iterations per second I can get out of this program. However, I am new to Go and so far my implementation is showing to be very messy and not working well.
The way I have it set up now, it will start out at a high rate (around 11,000 iterations per second) but then quickly dwindle down to 2,000. My goal is for a far bigger number than even 11,000. Also, the infofunc(i) function can't seem to keep up with fast speeds and using a goroutine for that function causes overlap of the printing to the console. Also, it will on occasion reuse the WaitGroup before the Wait has returned.
I don't like to be the person to ask to be spoon-fed code, but I am at a loss as to how to implement this. There seems to be so many different methods when it comes to parallelism, multithreading, etc. and it is confusing to me.
import (
"fmt"
"math/big"
"os"
"os/exec"
"sync"
"time"
)
var found = 0
var pages_queried = 0
var start_time = time.Now()
var bignum = new(big.Int)
var foundAddresses = 0
var wg sync.WaitGroup
var set = make(map[string]bool)
var addresses = []string{"6ab42gyr", "lo08n4g6"}
func main() {
bignum.SetString("1000000000000000000000000000", 10)
pick := os.Args[1]
kpp := 128
switch pick {
case "btc":
i := new(big.Int)
i, ok := i.SetString(os.Args[2], 10)
if ok {
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
cmd.Run()
for i.Cmp(bignum) < 0 {
wg.Add(1)
go func(i *big.Int) {
defer wg.Done()
go printKeys(i.String(), kpp)
i.Add(i, big.NewInt(1))
pages_queried += 1
infofunc(i)
}(i)
wg.Wait()
}
}
}
}
func infofunc(i *big.Int) {
elapsed := time.Now().Sub(start_time)
duration, _ := time.ParseDuration(elapsed.String())
duration2 := int(duration.Seconds())
if duration2 != 0 {
fmt.Printf("\033[5;0H")
fmt.Printf("Started at %s. Found: %d. Elapsed: %s. Queried: %d pages. Current page: %s. Rate: %d/s", start_time.String(), found, elapsed.String(), pages_queried, i.String(), (pages_queried / duration2))
}
}
func printKeys(pageNumber string, keysPerPage int) {
keys := generateKeys(pageNumber, keysPerPage)
length := len(keys)
var addressesLen = len(addresses)
for i := 0; i < length; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for ii := 0; ii < addressesLen; ii++ {
wg.Add(1)
go func(i int, ii int, keys []key) {
defer wg.Done()
for _, v := range addresses {
if set[keys[i].compressed] || set[keys[i].uncompressed] {
fmt.Print("Found an address: " + v + "!\n")
fmt.Printf("%v", keys[i])
fmt.Print("\n")
foundAddresses += 1
found += 1
}
}
}(i, ii, keys)
}
}(i)
foundAddresses = 0
}
}

I would not use a global sync.WaitGroup, it is hard to understand what is happening. Instead, just define it wherever you need.
You are calling wg.Wait() inside the loop block. That is basically blocking the loop every iteration waiting for goroutine to complete. What you really want is to spawn all the goroutines and only then wait for their completition.
if ok {
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
cmd.Run()
var wg sync.WaitGroup //I am about to spawn goroutines, I need to wait for them
for i.Cmp(bignum) < 0 {
wg.Add(1)
go func(i *big.Int) {
defer wg.Done()
go printKeys(i.String(), kpp)
i.Add(i, big.NewInt(1))
pages_queried += 1
infofunc(i)
}(i)
}
wg.Wait() //Now that all goroutines are working, let's wait
}
You cannot avoid the print overlap when you have multiple goroutines. If that's a problem you might think of using the Go's log stdlib, which will add timestamps for you. Then, you should be able to sort them in chronological order.
Anyway, split the code in more goroutines does not ensure a speed up. If the problem you are trying to solve is intrinsically sequential, then more goroutines will just add more contention and pressure on Go scheduler, leading to the opposite result. More details here. Thus, a goroutine for infofunc will not help. But it can be improved by using a logger library instead of plain fmt package.
func infofunc(i *big.Int) {
duration := time.Since(start_time).Seconds()
if duration != 0 {
log.Printf("\033[5;0H")
log.Printf("Started at %s. Found: %d. Elapsed: %s. Queried: %d pages. Current page: %s. Rate: %d/s", start_time.String(), found, elapsed.String(), pages_queried, i.String(), (pages_queried / duration2))
}
}
For printKeys, I would not create so many goroutines, they are not going to help if work they need to perform is CPU bound, which seems to be the case here.
func printKeys(pageNumber string, keysPerPage int) {
keys := generateKeys(pageNumber, keysPerPage)
length := len(keys)
var addressesLen = len(addresses)
var wg sync.WaitGroup //Local WaitGroup
for i := 0; i < length; i++ {
wg.Add(1)
go func(i int) { //This goroutine could be removed, in my opinion.
defer wg.Done()
for ii := 0; ii < addressesLen; ii++ {
for _, v := range addresses {
if set[keys[i].compressed] || set[keys[i].uncompressed] {
log.Printf("Found an address: %v\n", v)
log.Printf("%v", keys[i])
log.Printf("\n")
foundAddresses += 1
found += 1
}
}
}
}(i)
foundAddresses = 0
}
wg.Wait()
}
I would suggest to write a benchmark on these functions and then enable tracing. In this way you should get an idea where your code is spending most of the time.

Related

Variable of goroutine does not have time to sign up

This piece of code gets an array with strings and should mix it and make a response (both multithreaded).
The problem is that the text of the answer is randomly cutted. This is probably due to the fact that the variable "dump" does not have time to sign up completely. If it is wrapped in mutexes, the text is returned full, but threads are blocked and the program is executed for a long time. Please help!
const url = "https://yandex.ru/referats/write/?t=astronomy+mathematics"
const parseThreadsNum = 10
const generateThreadsNum = 1000
func startServer(wg *sync.WaitGroup) {
fmt.Println("Server started")
ch := make(chan []byte, generateThreadsNum)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
wg.Add(generateThreadsNum)
rawText := multiparseText() // text parsing
prettyText := prettifyText(rawText) // truncates commas, dots etc. Return []string
for i := 0; i < generateThreadsNum; i++ {
go func() {
dump, _ := json.Marshal(shuffle(&prettyText)) //shuffle mixes array randomly
ch <- dump
}()
go func() {
w.Write(<-ch)
defer wg.Done()
}()
}
fmt.Println("Text generated")
wg.Wait()
})
log.Fatal(http.ListenAndServe(":8081", nil))
}
func main() {
var wg sync.WaitGroup
runtime.GOMAXPROCS(runtime.NumCPU())
startServer(&wg)
}

Golang: Why are goroutines not running in parallel?

I have the following example below where two goroutine should be running in parallel. But if you check the output, the second goroutine only runs after the first goroutine completes. So, it's sequential.
Adding 2 processors: runtime.GOMAXPROCS(2) also didn't help. I'm running on a Mac pro with 8 cores and it is definitely not a hardware issue.
So my question - Is Golang really parallel and how to make example below run in
parallel?
Output:
Thread 1
Thread 1
…………....
Thread 1
Thread 1
Thread 2
Thread 2
…………....
Thread 2
Thread 2
Go code:
package main
import (
"runtime"
"time"
)
func main() {
runtime.GOMAXPROCS(2)
go func() {
for i := 0; i < 100; i++ {
println("Thread 1")
//time.Sleep(time.Millisecond * 10)
}
}()
go func() {
for i := 0; i < 100; i++ {
println("Thread 2")
//time.Sleep(time.Millisecond * 10)
}
}()
time.Sleep(time.Second)
}
In order to understand your program is running parallel or concurrently using goroutine is print the different value in sequence.
From this article : Concurrency, Goroutines and GOMAXPROCS .
Your code can't express enough to represent a parallel call. Please see the code below.
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(2)
var wg sync.WaitGroup
wg.Add(2)
fmt.Println("Starting Go Routines")
go func() {
defer wg.Done()
//time.Sleep(1 * time.Microsecond)
for char := 'a'; char < 'a'+26; char++ {
fmt.Printf("%c ", char)
}
}()
go func() {
defer wg.Done()
for number := 1; number < 27; number++ {
fmt.Printf("%d ", number)
}
}()
fmt.Println("Waiting To Finish")
wg.Wait()
fmt.Println("\nTerminating Program")
}
As you can see from the above code print the different value sequence using for loop.
The output would be different if you comment the runtime.GOMAXPROCS(2) and uncomment the line on time.Sleep(1 * time.Microsecond).

goroutine or multithreading is not working in golang

I was trying to implement multithreading in golang. I am able to implement go routines but it is not working as expected. below is the sample program which i have prepared,
func test(s string, fo *os.File) {
var s1 [105]int
count :=0
for x :=1000; x<1101;x++ {
s1[count] = x;
count++
}
//fmt.Println(s1[0])
for i := range s1 {
runtime.Gosched()
sd := s + strconv.Itoa(i)
var fileMutex sync.Mutex
fileMutex.Lock()
fmt.Fprintf(fo,sd)
defer fileMutex.Unlock()
}
}
func main() {
fo,err :=os.Create("D:/Output.txt")
if err != nil {
panic(err)
}
for i := 0; i < 4; i++ {
go test("bye",fo)
}
}
OUTPUT - good0bye0bye0bye0bye0good1bye1bye1bye1bye1good2bye2bye2bye2bye2.... etc.
the above program will create a file and write "Hello" and "bye" in the file.
My problem is i am trying to create 5 thread and wanted to process different values values with different thread. if you will see the above example it is printing "bye" 4 times.
i wanted output like below using 5 thread,
good0bye0good1bye1good2bye2....etc....
any idea how can i achieve this?
First, you need to block in your main function until all other goroutines return. The mutexes in your program aren't blocking anything, and since they're re-initialized in each loop, they don't even block within their own goroutine. You can't defer an unlock if you're not returning from the function, you need to explicitly unlock in each iteration of the loop. You aren't using any of the values in your array (though you should use a slice instead), so we can drop that entirely. You also don't need runtime.GoSched in a well-behaved program, and it does nothing here.
An equivalent program that will run to completion would look like:
var wg sync.WaitGroup
var fileMutex sync.Mutex
func test(s string, fo *os.File) {
defer wg.Done()
for i := 0; i < 105; i++ {
fileMutex.Lock()
fmt.Fprintf(fo, "%s%d", s, i)
fileMutex.Unlock()
}
}
func main() {
fo, err := os.Create("D:/output.txt")
if err != nil {
log.Fatal(err)
}
for i := 0; i < 4; i++ {
wg.Add(1)
go test("bye", fo)
}
wg.Wait()
}
Finally though, there's no reason to try and write serial values to a single file from multiple goroutines, and it's less efficient to do so. If you want the values ordered over the entire file, you will need to use a single goroutine anyway.

GoLang program executing on single thread even with GOMAXPROCS(4)

In the following GoLang program, I am trying to implement the stable marriage problem for N men and N women, using 2*N goroutines (1 for each man and woman).
The program closely follows the program definition as each goroutine (read "each man") sends a message via channel to the desired woman goroutine who in turn rejects/accepts his proposal. I expected the program to easily be scheduled on multiple threads on setting runtime.GOMAXPROCS(4) however it still runs in (almost) the exact same time (and running linux command time still shows CPU usage at 100% instead of expected 400%)
package main
import (
"fmt"
"runtime"
"time"
)
const N = 500
type human struct {
pref [N]int
phone chan int
cur_eng int
cur_num int
id int
}
var men = [N]human{}
var women = [N]human{}
func man(id int) {
guy := &men[id]
for {
runtime.Gosched()
for j := guy.cur_num + 1; j < N; j++ {
guy.cur_num = j
girl := &women[guy.pref[guy.cur_num]]
girl.phone <- guy.id
msg := <-guy.phone
if msg == 1 {
guy.cur_eng = guy.pref[guy.cur_num]
break
}
}
select {
case <-guy.phone:
guy.cur_eng = -1
}
}
}
func woman(id int, termi chan bool) {
girl := &women[id]
for {
runtime.Gosched()
select {
case msg := <-girl.phone:
if msg >= 0 {
if girl.cur_eng == -1 {
men[msg].phone <- 1
girl.cur_eng = msg
termi <- true
} else if girl.pref[girl.cur_eng] < girl.pref[msg] {
men[msg].phone <- 0
} else {
men[msg].phone <- 1
men[girl.cur_eng].phone <- -10
girl.cur_eng = msg
}
}
}
}
}
func main() {
runtime.GOMAXPROCS(8)
for i := 0; i < N; i++ {
men[i] = human{pref: [N]int{}, phone: make(chan int), cur_eng: -1, cur_num: -1, id: i}
for j := 0; j < N; j++ {
fmt.Scanf("%d\n", &(men[i].pref[j]))
}
}
for i := 0; i < N; i++ {
women[i] = human{pref: [N]int{}, phone: make(chan int), cur_eng: -1, cur_num: -1, id: i}
for j := 0; j < N; j++ {
t := 0
fmt.Scanf("%d\n", &t)
women[i].pref[t] = j
}
}
termi := make(chan bool)
for i := 0; i < N; i++ {
go man(i)
go woman(i, termi)
}
for i := 0; i < N; i++ {
<-termi
}
time.Sleep(100 * time.Millisecond)
for i := 0; i < N; i++ {
fmt.Printf("%d %d\n", i, men[i].cur_eng)
}
}
EDIT: The serial implementation of the program I made is here. The time comparison shows both running in almost identical time (1.27s for serial, 1.30s for above one).
Also, the algorithm followed for the parallel implementation was made according to this as well as I could understand (I used goroutines as I didn't know how to use MPI).
Please feel free to suggest an alternative implementation (parallel) if possible.
The program above needs the following as input file : https://drive.google.com/file/d/0B6jsnt965ZwrWlV1OE9LLVA1LUk/view?usp=sharing
I think the input file you provided would take that much time to be read by the program(via scanf per line).

Why concurrent code takes more time to execute

I have a function named linearize...I'm trying to speed up its execution but surprised to find that it had become slower. Have I missed something or have messed up with fundamentals..
As per my understanding things should improve..
Thanks,
package main
import (
"fmt"
"math"
"sync"
"time"
)
var rgb []float64
func linearizeWithWg(v float64, idx int, wg *sync.WaitGroup) {
defer wg.Done()
if v <= 0.04045 {
rgb[idx] = v / 12.92
} else {
rgb[idx] = math.Pow((v+0.055)/1.055, 2.4)
}
}
func linearizeWithGoR(v float64) float64 {
res := make(chan float64)
go func(input float64) {
if input <= 0.04045 {
res <- input / 12.92
} else {
res <- math.Pow((input+0.055)/1.055, 2.4)
}
}(v)
return <-res
}
func linearizeNomal(v float64) float64 {
if v <= 0.04045 {
return v / 12.92
}
return math.Pow((v+0.055)/1.055, 2.4)
}
func main() {
start := time.Now()
const C = 1.0 / 255.0
//Normal Execution
for i := 0; i < 100000; i++ {
linearizeNomal(float64(i) * C * 0.5)
linearizeNomal(float64(i) * C * 1.5)
linearizeNomal(float64(i) * C * 4.5)
}
elaspsed := time.Since(start)
fmt.Println(elaspsed)
//With GoRoutines.. Slow
start = time.Now()
for i := 0; i < 100000; i++ {
linearizeWithGoR(float64(i) * C * 0.5)
linearizeWithGoR(float64(i) * C * 1.5)
linearizeWithGoR(float64(i) * C * 2.5)
}
elaspsed = time.Since(start)
fmt.Println(elaspsed)
//With WaitGroup. Slow
for i := 0; i < 100000; i++ {
rgb = make([]float64, 3)
var wg sync.WaitGroup
wg.Add(3)
linearizeWithWg(float64(i)*C*0.5, 0, &wg)
linearizeWithWg(float64(i)*C*1.5, 1, &wg)
linearizeWithWg(float64(i)*C*4.5, 2, &wg)
wg.Wait()
}
elaspsed = time.Since(start)
fmt.Println(elaspsed)
}
The overhead of all the concurrency related functions(channel creation, channel sending, goroutine creation) is way bigger than the two instructions that you execute in each of your goroutine.
In addition, your goroutine version is basically serial because you spawn a goroutine and immediately wait for the result of its channel. The waitgroup version is similar.
Try again with a small number of goroutines each executing a chunk of your loop. #Joker_vD also has a good point to make sure that GOMAXPROCS is bigger than one.
Your problem is that you don't actually do anything concurrently.
in the workgroup example, you need to call go linearizeWithWg(...)
In the goroutine example, you start a goroutine, but then wait for it to end in the function. To run it concurrently, you would need a buffered response channel, and having another goroutine getting the responses

Resources