我在Go调度程序中遇到了一些神秘的行为,我对正在发生的事情非常好奇。要点是runtime.Gosched()
在Linux中没有按预期工作,除非它之前是log.Printf()
调用,但它在OS X上的两种情况下都能正常工作。这是一个重现行为的最小设置:
主要goroutine睡眠1000个1ms的时间段,并且在每次睡眠之后通过信道将虚拟消息推送到另一个goroutine上。第二个goroutine会收听新消息,每次获取一个消息时,它会完成10ms的工作。因此,如果没有runtime.Gosched()
次调用,程序将需要10秒钟才能运行。
当我在第二个goroutine中添加定期runtime.Gosched()
个调用时,正如预期的那样,程序运行时在我的Mac上缩小到1秒。但是,当我尝试在Ubuntu上运行相同的程序时,它仍然需要10秒。我确保在两种情况下都设置runtime.GOMAXPROCS(1)
。
这里真的很奇怪:如果我只是在runtime.Gosched()
调用之前添加一个日志语句,那么突然程序也会在Ubuntu的预期的1秒内运行。
package main
import (
"time"
"log"
"runtime"
)
func doWork(c chan int) {
for {
<-c
// This outer loop will take ~10ms.
for j := 0; j < 100 ; j++ {
// The following block of CPU work takes ~100 microseconds
for i := 0; i < 300000; i++ {
_ = i * 17
}
// Somehow this print statement saves the day in Ubuntu
log.Printf("donkey")
runtime.Gosched()
}
}
}
func main() {
runtime.GOMAXPROCS(1)
c := make(chan int, 1000)
go doWork(c)
start := time.Now().UnixNano()
for i := 0; i < 1000; i++ {
time.Sleep(1 * time.Millisecond)
// Queue up 10ms of work in the other goroutine, which will backlog
// this goroutine without runtime.Gosched() calls.
c <- 0
}
// Whole program should take about 1 second to run if the Gosched() calls
// work, otherwise 10 seconds.
log.Printf("Finished in %f seconds.", float64(time.Now().UnixNano() - start) / 1e9)
}
其他细节:我正在运行go1.10 darwin / amd64,并使用。编译linux二进制文件
env GOOS=linux GOARCH=amd64 go build ...
我尝试了一些简单的变体:
所有这些比调用log.Printf()和Gosched()慢〜10倍。
任何见解都将不胜感激!这个例子当然是非常人为的,但在编写websocket广播服务器时出现了问题,导致性能显着下降。
编辑:我摆脱了我的例子中的多余部分,使事情变得更加透明。我发现如果没有print语句,runtime.Gosched()
调用仍在运行,只是它们似乎被延迟了5ms,在下面的例子中导致总运行时间几乎为5秒,该程序应该立即完成(并在我的Mac上,或在Ubuntu上使用print语句完成)。
package main
import (
"log"
"runtime"
"time"
)
func doWork() {
for {
// This print call makes the code run 20x faster
log.Printf("donkey")
// Without this line, the program never terminates (as expected). With this line
// and the print call above it, the program takes <300ms as expected, dominated by
// the sleep calls in the main goroutine. But without the print statement, it
// takes almost exactly 5 seconds.
runtime.Gosched()
}
}
func main() {
runtime.GOMAXPROCS(1)
go doWork()
start := time.Now().UnixNano()
for i := 0; i < 1000; i++ {
time.Sleep(10 * time.Microsecond)
runtime.Gosched()
}
log.Printf("Finished in %f seconds.", float64(time.Now().UnixNano() - start) / 1e9)
}
答案 0 :(得分:0)
当我在第二个goroutine中添加定期的runtime.Gosched()调用时, 正如预期的那样,程序运行时在我的Mac上缩小到1秒。 但是,当我尝试在Ubuntu上运行相同的程序时,它仍然需要 10秒。
在Ubuntu上,我无法重现您的问题,一秒钟,而不是十秒钟,
输出:
$ uname -srvm
Linux 4.13.0-37-generic #42-Ubuntu SMP Wed Mar 7 14:13:23 UTC 2018 x86_64
$ go version
go version devel +f1deee0e8c Mon Apr 2 20:18:14 2018 +0000 linux/amd64
$ go build rampatowl.go && time ./rampatowl
2018/04/02 16:52:04 Finished in 1.122870 seconds.
real 0m1.128s
user 0m1.116s
sys 0m0.012s
$
rampatowl.go
:
package main
import (
"log"
"runtime"
"time"
)
func doWork(c chan int) {
for {
<-c
// This outer loop will take ~10ms.
for j := 0; j < 100; j++ {
// The following block of CPU work takes ~100 microseconds
for i := 0; i < 300000; i++ {
_ = i * 17
}
// Somehow this print statement saves the day in Ubuntu
//log.Printf("donkey")
runtime.Gosched()
}
}
}
func main() {
runtime.GOMAXPROCS(1)
c := make(chan int, 1000)
go doWork(c)
start := time.Now().UnixNano()
for i := 0; i < 1000; i++ {
time.Sleep(1 * time.Millisecond)
// Queue up 10ms of work in the other goroutine, which will backlog
// this goroutine without runtime.Gosched() calls.
c <- 0
}
// Whole program should take about 1 second to run if the Gosched() calls
// work, otherwise 10 seconds.
log.Printf("Finished in %f seconds.", float64(time.Now().UnixNano()-start)/1e9)
}