Memory usage in Go application with pprof - go

Is it possible to see live memory usage in Go program? What I mean by live? Let's say I have an application which allocates slice of 100'000 ints memory allocation should be different in main and different in heavyUsage function I there way to see this with pprof or even better is there to see memory usage in some dynamically resized slice when we have no idea how big it will be?
package main
import (
"fmt"
"math/rand"
"net/http"
_ "net/http/pprof"
"time"
)
var heapVariable = make([]int, 10000, 10000)
func heavyUsage() {
time.Sleep(10 * time.Second)
fmt.Println("allocating significant amount of memory")
s := make([]int64, 100000, 100000)
time.Sleep(10 * time.Second)
min, max := 1000, 100000
s[rand.Intn(max-min)+min] = 42
fmt.Println("Done")
}
func main() {
go func() {
_ = http.ListenAndServe("0.0.0.0:8080", nil)
}()
heavyUsage()
}

Related

concurrency in go -Taking same time with different no of CPU

I am running a go concurrent program with the below two case and observed that It is taking same time irrespective of no of CPU it using while execution.
Case1: When cpuUsed = 1
program took 3m20.973185s.
when I am increasing the no of CPU used.
Case2: when cpuUsed = 8
program took 3m20.9330516s.
Please find the below Go code for more details.
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
"time"
)
var waitG sync.WaitGroup
var cpuUsed = 1
var maxRandomNums = 1000
func init() {
maxCPU := runtime.NumCPU() //It'll give us the max CPU :)
cpuUsed = 8 //getting same time taken for 1 and 8
runtime.GOMAXPROCS(cpuUsed)
fmt.Printf("Number of CPUs (Total=%d - Used=%d) \n", maxCPU, cpuUsed)
}
func main() {
start := time.Now()
ids := []string{"rotine1", "routine2", "routine3", "routine4"}
waitG.Add(4)
for i := range ids {
go numbers(ids[i])
}
waitG.Wait()
elapsed := time.Since(start)
fmt.Printf("\nprogram took %s. \n", elapsed)
}
func numbers(id string) {
rand.Seed(time.Now().UnixNano())
for i := 1; i <= maxRandomNums; i++ {
time.Sleep(200 * time.Millisecond)
fmt.Printf("%s-%d ", id, rand.Intn(20)+20)
}
waitG.Done()
}
you will find out:
total time (3 min 20s) = 200s = sleep(200ms) * loops(1000)
Let's simplify your code and focus on CPU usage:
Remove the Sleep, which does not use the CPU at all
fmt.Println as a stdio, does not use the CPU
Random number did nothing but introduce uncertainty into the program, remove it
The only code that takes CPU in the goroutine is the "rand.Intn(20)+20", making it a constant addition
Increase the "maxRandomNums"
then your code will be like this, run it again
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
var waitG sync.WaitGroup
var cpuUsed = 1
var maxRandomNums = 1000000000
func init() {
maxCPU := runtime.NumCPU() //It'll give us the max CPU :)
cpuUsed = 8 //getting same time taken for 1 and 8
runtime.GOMAXPROCS(cpuUsed)
fmt.Printf("Number of CPUs (Total=%d - Used=%d) \n", maxCPU, cpuUsed)
}
func main() {
start := time.Now()
ids := []string{"rotine1", "routine2", "routine3", "routine4"}
waitG.Add(4)
for i := range ids {
go numbers(ids[i])
}
waitG.Wait()
elapsed := time.Since(start)
fmt.Printf("\nprogram took %s. \n", elapsed)
}
func numbers(id string) {
// rand.Seed(time.Now().UnixNano())
for i := 1; i <= maxRandomNums; i++ {
// time.Sleep(200 * time.Millisecond)
// fmt.Printf("%s-%d ", id, rand.Intn(20)+20)
_ = i + 20
}
waitG.Done()
}

Unable to use goroutines concurrently to find max until context is cancelled

I have successfully made a synchronous solution without goroutines to findMax of compute calls.
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
func findMax(ctx context.Context, concurrency int) uint64 {
var (
max uint64 = 0
num uint64 = 0
)
for i := 0; i < concurrency; i++ {
num = compute()
if num > max {
max = num
}
}
return max
}
func compute() uint64 {
// NOTE: This is a MOCK implementation of the blocking operation.
time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
return rand.Uint64()
}
func main() {
maxDuration := 2 * time.Second
concurrency := 10
ctx, cancel := context.WithTimeout(context.Background(), maxDuration)
defer cancel()
max := findMax(ctx, concurrency)
fmt.Println(max)
}
https://play.golang.org/p/lYXRNTDtNCI
When I attempt to use goroutines to use findMax to repeatedly call compute function using as many goroutines until context ctx is canceled by the caller main function. I am getting 0 every time and not the expected max of the grouting compute function calls. I have tried different ways to do it and get deadlock most of the time.
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
func findMax(ctx context.Context, concurrency int) uint64 {
var (
max uint64 = 0
num uint64 = 0
)
for i := 0; i < concurrency; i++ {
select {
case <- ctx.Done():
return max
default:
go func() {
num = compute()
if num > max {
max = num
}
}()
}
}
return max
}
func compute() uint64 {
// NOTE: This is a MOCK implementation of the blocking operation.
time.Sleep(time.Duration(rand.Int63n(100)) * time.Millisecond)
return rand.Uint64()
}
func main() {
maxDuration := 2 * time.Second
concurrency := 10
ctx, cancel := context.WithTimeout(context.Background(), maxDuration)
defer cancel()
max := findMax(ctx, concurrency)
fmt.Println(max)
}
https://play.golang.org/p/3fFFq2xlXAE
Your program has multiple problems:
You are spawning multiple goroutines that are operating on shared variables i.e., max and num leading to data race as they are not protected (eg. by Mutex).
Here num is modified by every worker goroutine but it should have been local to the worker otherwise the computed data could be lost (eg. one worker goroutine computed a result and stored it in num, but right after that a second worker computes and replaces the value of num).
num = compute // Should be "num := compute"
You are not waiting for every goroutine to finish it's computation and it may result in incorrect results as every workers computation wasn't taken into account even though context wasn't cancelled. Use sync.WaitGroup or channels to fix this.
Here's a sample program that addresses most of the issues in your code:
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
type result struct {
sync.RWMutex
max uint64
}
func findMax(ctx context.Context, workers int) uint64 {
var (
res = result{}
wg = sync.WaitGroup{}
)
for i := 0; i < workers; i++ {
select {
case <-ctx.Done():
// RLock to read res.max
res.RLock()
ret := res.max
res.RUnlock()
return ret
default:
wg.Add(1)
go func() {
defer wg.Done()
num := compute()
// Lock so that read from res.max and write
// to res.max is safe. Else, data race could
// occur.
res.Lock()
if num > res.max {
res.max = num
}
res.Unlock()
}()
}
}
// Wait for all the goroutine to finish work i.e., all
// workers are done computing and updating the max.
wg.Wait()
return res.max
}
func compute() uint64 {
rnd := rand.Int63n(100)
time.Sleep(time.Duration(rnd) * time.Millisecond)
return rand.Uint64()
}
func main() {
maxDuration := 2 * time.Second
concurrency := 10
ctx, cancel := context.WithTimeout(context.Background(), maxDuration)
defer cancel()
fmt.Println(findMax(ctx, concurrency))
}
As #Brits pointed out in the comments that when context is cancelled make sure that you stop those worker goroutines to stop processing (if possible) because it is not needed anymore.

Sleep by fraction of integer

I am trying to write a program that pauses for random intervals of time that are decimal numbers.
Here is the program that is not working:
package main
import (
"fmt"
"math/rand"
"time"
)
var test int
func main() {
intervalGenerate()
}
func intervalGenerate() {
var randint float64
rand.Seed(time.Now().UnixNano())
randInterval := randFloats(3, 7, 1)
randint = randInterval[0]
duration := time.Duration(randint) * time.Second
fmt.Println("Sleeping for", duration)
time.Sleep(duration)
fmt.Println("Resuming")
}
func randFloats(min, max float64, n int) []float64 {
res := make([]float64, n)
for i := range res {
res[i] = min + rand.Float64()*(max-min)
}
return res
}
Currently a random number is generated between 3 and 7 (including decimals) but Sleep rounds to the nearest round number.
From what I understand the reason this is failing is because Sleep takes Duration, which is an Int64:
func Sleep(d Duration)
Is there a way to sleep a program for fractions of a second?
Go Playground:
https://play.golang.org/p/z-dnDBnUfxr
Use time.Millisecond, time.Microsecond, or time.Nanosecond depending on the level of granularity you need.
// sleep for 2.5 seconds
milliseconds := 2500
time.Sleep(time.Duration(milliseconds) * time.Millisecond)

Access with mutex has more speed on more cpu

I tried some synchronization techniques to share state between goroutines and find out that incorrect variant (without sync) works slowly than same program with mutex.
Given the code:
package main
import (
"sync"
"time"
"fmt"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
hash := make(map[string]string)
hash["test"] = "string"
num := 40000000
wg.Add(num)
start := time.Now()
for i := 0; i < num; i++ {
go func() {
mu.Lock()
_, _ = hash["test"]
mu.Unlock()
wg.Done()
}()
}
wg.Wait()
fmt.Println(time.Since(start))
}
Perform on my laptop with 8 HT cores for 9-10 seconds.
But if just remove sync it works for 11-12 seconds:
package main
import (
"sync"
"time"
"fmt"
)
func main() {
var wg sync.WaitGroup
hash := make(map[string]string)
hash["test"] = "string"
num := 40000000
wg.Add(num)
start := time.Now()
for i := 0; i < num; i++ {
go func() {
_, _ = hash["test"]
wg.Done()
}()
}
wg.Wait()
fmt.Println(time.Since(start))
}
A synced version is faster and unitizes CPU much higher. Question is why?
My thought is about how goroutines scheduled and overhead for context switching because of the more GOMAXPROCS the greater the gap between these two versions. But I can't explain the real reason for that that happens under the hood of the scheduler.

convert byte[] to string in golang strange occupy heap

I found strange occupy heap when convert byte[] to string with below code
package main
import (
"bytes"
"fmt"
"net/http"
_ "net/http/pprof"
"strings"
"time"
)
var (
c = make(chan int, 500000)
)
func main() {
go func() {
http.ListenAndServe(":8080", nil)
}()
f := func(ss []string) {
fmt.Println(ss)
time.Sleep(time.Millisecond)
<-c
}
for {
c <- 1
bs := bytes.NewBufferString("A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z").Bytes()
fmt.Println(bs) // will raise memory leak after marked as comment???
s := string(bs)
ss := strings.Split(s, ",")
go f(ss)
}
}
without fmt.Println(bs) will gradually exhausting memory.
with fmt.Println(bs) work fine. i can't understand what happened? i'm worked with version go1.9.2 darwin/amd64
No, there is no memory leak:
You are using 500000 concurrent goroutines, you just need to limit (reduce) the number of concurrent goroutines, e.g.:
c := make(chan int, runtime.NumCPU())
Try this ( and see the end of this edit):
package main
import (
"bytes"
"fmt"
"runtime"
"strings"
"time"
)
func main() {
c := make(chan int, runtime.NumCPU())
for {
c <- 1
bs := bytes.NewBufferString("A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z").Bytes()
s := string(bs)
ss := strings.Split(s, ",")
go func(ss []string) {
fmt.Println(ss)
time.Sleep(time.Millisecond)
<-c
}(ss)
}
}
Your Code:
package main
import (
"bytes"
"fmt"
"net/http"
_ "net/http/pprof"
"strings"
"time"
)
var (
c = make(chan int, 500000)
)
func main() {
go func() {
http.ListenAndServe(":8080", nil)
}()
f := func(ss []string) {
fmt.Println(ss)
time.Sleep(time.Millisecond)
<-c
}
for {
c <- 1
bs := bytes.NewBufferString("A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z").Bytes()
// fmt.Println(bs) // will raise memory leak after marked as comment???
s := string(bs)
ss := strings.Split(s, ",")
go f(ss)
}
}
It reaches steady state after a while and even reduced Memory usage:
// Mem CPU time:
// 5,464,208K 0:1:20
// 5,468,208K 0:2:20
// 5,469,608K 0:3:20
// 5,469,844K 0:4:20
// 5,469,844K 0:5:20
// 5,469,848K 0:6:20
// 5,469,848K 0:7:20 fixed
// 5,469,848K 0:8:20 fixed
// 5,469,616K 0:9:20 reduced

Resources