package main
import "fmt"
import "runtime"
import "time"
type callRet struct {
ret int
}
type callIn struct {
ret_chan chan *callRet
arg1 int
}
func caller(call_in_c chan *callIn, arg1 int) int {
ret_c := make(chan *callRet, 1)
ci := callIn{ret_c, arg1}
t1 := time.Now().UnixNano()
call_in_c <- &ci
ret := <-ret_c
t2 := time.Now().UnixNano()
_, _ = t1, t2
//fmt.Println(t2, t1, float64(t2-t1)/1e6)
return ret.ret
}
func call_srv(call_in_c chan *callIn) {
//runtime.LockOSThread()
ct := 0
for {
select {
case in := <-call_in_c:
ret_c := in.ret_chan
ret := callRet{3 + in.arg1}
ret_c <- &ret
ct++
if ct%1000 == 0 {
fmt.Println(ct)
}
//default:
// time.Sleep(1 * time.Millisecond)
}
}
//runtime.UnlockOSThread()
}
func init() {
//runtime.LockOSThread()
}
func main() {
p := fmt.Println
runtime.GOMAXPROCS(5)
call_in_c := make(chan *callIn, 2000)
fp := func(call_in_c chan *callIn) {
ct := 0
for ; ct < 2000000; ct = ct + 1 {
caller(call_in_c, 1)
time.Sleep(100 * time.Nanosecond)
}
p("done:)")
}
go fp(call_in_c)
go fp(call_in_c)
go func() {
for {
fmt.Println("heartbeat...")
time.Sleep(10 * 1000 * time.Microsecond)
}
}()
go func() {
for {
}
}()
call_srv(call_in_c)
return
}
(gdb) info goroutines
1 waiting runtime.gopark
2 waiting runtime.gopark
3 waiting runtime.gopark
4 waiting runtime.gopark
5 waiting runtime.gopark
* 6 running runtime.systemstack_switch
7 waiting runtime.gopark
* 8 running main.main.func3
17 runnable runtime.exitsyscall
33 waiting runtime.gopark
18 waiting runtime.gopark
49 waiting runtime.gopark
50 waiting runtime.gopark
51 waiting runtime.gopark
(gdb) goroutine 6 bt
#0 runtime.systemstack_switch () at /usr/local/go/src/runtime/asm_amd64.s:281
#1 0x0000000000415088 in runtime.gcStart (mode=0, forceTrigger=false)
at /usr/local/go/src/runtime/mgc.go:1010
#2 0x000000000040d841 in runtime.mallocgc (size=96, typ=0x4a7fa0, needzero=true,
~r3=0x507998 <runtime.timers+24>) at /usr/local/go/src/runtime/malloc.go:785
#3 0x000000000040df68 in runtime.newobject (typ=0x4a7fa0, ~r1=0x0)
at /usr/local/go/src/runtime/malloc.go:820
#4 0x00000000004035d2 in runtime.makechan (t=0x492960, size=1, ~r2=0xc4200016c0)
at /usr/local/go/src/runtime/chan.go:92
#5 0x00000000004840e4 in main.caller (call_in_c=0xc420056060, arg1=1, ~r2=0)
at /root/go/src/chan/x.go:18
#6 0x00000000004846ac in main.main.func1 (call_in_c=0xc420056060) at /root/go/src/chan/x.go:62
#7 0x000000000044da21 in runtime.goexit () at /usr/local/go/src/runtime/asm_amd64.s:2197
#8 0x000000c420056060 in ?? ()
#9 0x0000000000000000 in ?? ()
go func() {
for {
}
}()
go func() {
for {
}
}()
go func() {ct := 0t1 := time.Now().UnixNano()for {ct++if ct%600000000 == 0 {t2 := time.Now().UnixNano()fmt.Println("hooray", t2, t1, float64(t2-t1)/1e6)t1 = t2}}}()
go func() {ct := 0t1 := time.Now().UnixNano()for {ct++if ct%100000 == 0 {runtime.Gosched()}if ct%600000000 == 0 {t2 := time.Now().UnixNano()fmt.Println("hooray", t2, t1, float64(t2-t1)/1e6)t1 = t2}}}()
Yes, this is expected. Don't write infinite loops like that.
This is working as intended.
time.Sleep is defined as it will fire _after_ the duration passed to the function, so
technically this is not a bug.
Replace :
go func() {
for {
}
}()
With:
go func() {
ct := 0
t1 := time.Now().UnixNano()
for {
ct++
if ct%600000000 == 0 {
t2 := time.Now().UnixNano()
fmt.Println("hooray", t2, t1, float64(t2-t1)/1e6)
t1 = t2
}
}
}()
Thus the program does not stuck anymore -- But whole process
is thrashing and churning! (With a time delay nearly 100-200ms
based on value '600000000' and the speed of your computer)
-j
This piece of code prohibits cooperative scheduling in 599 999 999 out of
600 000 000 times. It's a nice example how to not code in Go.
Particularly, delays should be implemented using the functions in the time
package, not by burning CPU cycles at the expense of other useful work
that could have been done instead.
current golang's scheduler is not
good at cpu intensive applications
package mainimport "fmt"import "runtime"
//import "math/big"import "time"/*#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/time.h>#include <time.h>int deadloop() {long long ct=0;struct timespec start, stop;clock_gettime(CLOCK_MONOTONIC_RAW, &start);while (1) {ct++;if (ct%800000000==0){clock_gettime(CLOCK_MONOTONIC_RAW, &stop);printf("cgo-> ct:%ld | c loop delay:%lu ms\n",ct,((stop.tv_sec - start.tv_sec) * 1000000 + (stop.tv_nsec - start.tv_nsec) / 1000)/1000);clock_gettime(CLOCK_MONOTONIC_RAW, &start);}}return rand();}*/import "C"
type callRet struct {ret int}type callIn struct {ret_chan chan *callRetarg1 int}
var delta_t_ms float64
func caller(call_in_c chan *callIn, arg1 int) int {ret_c := make(chan *callRet, 1)ci := callIn{ret_c, arg1}t1 := time.Now().UnixNano()call_in_c <- &ciret := <-ret_ct2 := time.Now().UnixNano()_, _ = t1, t2
if float64(t2-t1)/1e6 > delta_t_ms {delta_t_ms = float64(t2-t1) / 1e6
}//fmt.Println(t2, t1, float64(t2-t1)/1e6)return ret.ret}func call_srv(call_in_c chan *callIn) {//runtime.LockOSThread()ct := 0for {select {case in := <-call_in_c:ret_c := in.ret_chanret := callRet{3 + in.arg1}ret_c <- &retct++
if ct%10000 == 0 {fmt.Println("channel_op_ct:", ct, " max_channel_op_delay:", delta_t_ms, "ms")
}//default:// time.Sleep(1 * time.Millisecond)}}//runtime.UnlockOSThread()}func init() {//runtime.LockOSThread()}func main() {p := fmt.Println
runtime.GOMAXPROCS(2)
call_in_c := make(chan *callIn, 2000)fp := func(call_in_c chan *callIn) {ct := 0
for ; ct < 10*60000; ct = ct + 1 {caller(call_in_c, 1)time.Sleep(time.Microsecond / 1000)
}p("done:)")}go fp(call_in_c)
//go fp(call_in_c)go func() {for {fmt.Println("heartbeat... ", "max_channel_op_delay:", delta_t_ms, "ms")time.Sleep(1000 * 1000 * time.Microsecond)}}()godeadloop := func() {
ct := 0t1 := time.Now().UnixNano()for {ct++
if ct%360000000 == 0 {
t2 := time.Now().UnixNano()fmt.Println("hooray", t2, t1, float64(t2-t1)/1e6)t1 = t2}
if ct%360000000 == 0 {runtime.Gosched()}}}_ = godeadloop_ = C.deadloopgo C.deadloop()//go godeadloop() // <<---------call_srv(call_in_c)return}
CPU intensive code in cgo:
heartbeat... max_channel_op_delay: 0.81509 ms
channel_op_ct: 590000 max_channel_op_delay: 0.81509 ms
channel_op_ct: 600000 max_channel_op_delay: 0.81509 ms
done:)
cgo-> ct:3200000000 | c loop delay:1039 ms
heartbeat... max_channel_op_delay: 0.81509 ms
CPU intensive code in pure go code:
hooray 1498049095239314392 1498049094319306332 920.00806 ms (go dead loop)
channel_op_ct: 600000 max_channel_op_delay: 970.921145 ms
done:)
heartbeat... max_channel_op_delay: 970.921145 ms
hooray 1498049096158299639 1498049095239314392 918.985247
Particularly, delays should be implemented using the functions in the timepackage, not by burning CPU cycles at the expense of other useful workthat could have been done instead.Sure this kinda of dead loop is just an simulation about some really CPU intensive jobs.And I think for those kind of job, the delay about range [20 ms- inf ms) is quite ordinary.Thus I think that kind of writing is acceptable :D
It seems really pointless if you've got lots of work to do to have to sit in a delay.
Would not a call to runtime.Gosched() be more efficient. That way you accept that you yeald to other procs, but still allow yourself to be rescheduled immediately if there is nothing else in the queue?
Hi, I found when running this code below (play):
$ time go run na.go
heartbeat...
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
11000
heartbeat...
12000
13000
heartbeat...
14000
15000
16000
17000
18000
19000
heartbeat...
^Csignal: interrupt
real 0m0.779s
user 0m0.720s
sys 0m0.152s
> just add a function call occasionally depending on your required latency bound
Thanks Jesper for your all detailed explanation. I quiteagree with your point of view and conclusion.One thing left here should to be discussed is about thego scheduler's unwanted behaviour.Unwanted behaviour of the scheduler:After go run the codes at the topmost of this thread, we got:$ time go run na.go
heartbeat...
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
11000
heartbeat...
12000
13000
heartbeat...
14000
15000
16000
17000
18000
19000
heartbeat...
^Csignal: interrupt
real 0m0.779s
user 0m0.720s
sys 0m0.152s(The process stuck here forever and no new outputs anymore)As the runtime.GOMAXPROCS is 5 now, why can't the scheduler justlet the other several goroutines which is already pending all switchto the 4 left idly goroutine 'slots' and run on instead of suspend forever?I think there is probably some design flaw about it.