Description
What version of Go are you using (go version
)?
go1.11 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/lni/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/lni/golang_ws"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build440058908=/tmp/go-build -gno-record-gcc-switches"
What did you do?
I need to call a function roughly every millisecond, there is no real time requirement, as long as it is called roughly every millisecond everything is fine. However, I realised that both time.Ticker and time.Sleep() are causing excessive CPU overhead, probably due to runtime scheduling.
The following Go program uses 20-25% %CPU as reported by top on Linux.
package main
import (
"time"
)
func main() {
ticker := time.NewTicker(time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
}
}
}
for loop with range is causing similar overhead
package main
import (
"time"
)
func main() {
ticker := time.NewTicker(time.Millisecond)
defer ticker.Stop()
for range ticker.C {
}
}
while the following program is showing 10-15% %CPU in top
package main
import (
"time"
)
func main() {
for {
time.Sleep(time.Millisecond)
}
}
To workaround the issue, I had to move the ticker/sleep part to C and let the C code to call the Go function that need to be invoked every millisecond. Such a cgo based ugly hack reduced %CPU in top to 2-3%. Please see the proof of concept code below.
ticker.h
#ifndef TEST_TICKER_H
#define TEST_TICKER_H
void cticker();
#endif // TEST_TICKER_H
ticker.c
#include <unistd.h>
#include "ticker.h"
#include "_cgo_export.h"
void cticker()
{
for(int i = 0; i < 30000; i++)
{
usleep(1000);
Gotask();
}
}
ticker.go
package main
/*
#include "ticker.h"
*/
import "C"
import (
"log"
)
var (
counter uint64 = 0
)
//export Gotask
func Gotask() {
counter++
}
func main() {
C.cticker()
log.Printf("Gotask called %d times", counter)
}
What did you expect to see?
Much lower CPU overhead when using time.Ticker or time.Sleep()
What did you see instead?
20-25% %CPU in top