Go: Unexpected Results from time.Sleep - performance

Running this code (as a built executable, not with the debugger):
package main
import (
"fmt"
"time"
)
func main() {
startTime := time.Now()
for i := 0; i < 1000; i++ {
time.Sleep(1 * time.Millisecond)
}
fmt.Printf("%d\n", time.Since(startTime).Milliseconds())
}
I get the output:
15467
This seems like a major overhead for calling the time.Sleep() function; it's essentially taking 15ms per loop iteration, even though it's only sleeping for 1ms in each loop. This suggests that there's a 14ms overhead for running an iteration of the loop and initiating a sleep.
If we adjust the sleep duration:
package main
import (
"fmt"
"time"
)
func main() {
startTime := time.Now()
for i := 0; i < 1000; i++ {
time.Sleep(10 * time.Millisecond)
}
fmt.Printf("%d\n", time.Since(startTime).Milliseconds())
}
I get the output:
15611
This is essentially the same duration, even though it should be sleeping for 10x as long. This kills the idea that there's a 14ms overhead for the loop iteration and initiating the sleep, because if that were the case, it would be (14+10)*1000 = 24000ms total, which it is not.
What am I missing? Why would this code take the same duration to execute, whether the sleep duration is 1ms or 10ms?
Note that I've tried running this in the Go playground but get different results; I think it handles sleeping differently. These results are consistent on my laptop, which is running an i7-10510.

It is probably related to the frequency of the system's timer. For example, on Windows the clock ticks every 15 milliseconds (source):
For example, for Windows running on an x86 processor, the default interval between system clock ticks is typically about 15 milliseconds, and the minimum interval between system clock ticks is about 1 millisecond. Thus, the expiration time of a default-resolution timer (which ExAllocateTimer creates if the EX_TIMER_HIGH_RESOLUTION flag is not set) can be controlled only to within about 15 milliseconds, but the expiration time of a high-resolution timer can be controlled to within a millisecond.
If you need a higher precision timer you probably need to find a way to use High-Resolution Timers.
More information can be found in the threads below:
https://github.com/golang/go/issues/44343
https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/
https://github.com/golang/go/issues/44343

Related

Data race in Go: Why does it happen below 10-11ms?

Here is the code I ran:
package main
import (
"fmt"
"time"
)
const delay = 9 * time.Millisecond
func main() {
n := 0
go func() {
time.Sleep(delay)
n++
}()
fmt.Println(n)
}
Here is the command I used:
go run -race data_race_demo.go
Here is the behavior I noticed:
With delay set to 9ms or lower, data race is always detected (program throws Found 1 data race(s))
With delay set to 12ms or higher, data race is never detected (program simply prints 0)
With delay set to 10 to 11ms, data race occurs intermittently (that is, sometimes prints 0 and sometimes throws Found 1 data race(s))
Why does this happen at around 10-11ms?
I'm using Go 1.16.3 on darwin/amd64, if that matters.
You have 2 goroutines: the main and the one you launch. They access the n variable (and one is a write) without synchronization: that's a data race.
Whether this race is detected depends on whether this racy access occurs. When the main() function ends, your app ends as well, it does not wait for other non-main goroutines to finish.
If you increase the sleep delay, main() will end sooner than the end of sleep and will not wait for the n++ racy write to occur, so nothing is detected. If sleep is short, shorter than the fmt.Prinln() execution time, the racy write occurs and is detected.
There's nothing special about the 10ms. That's just the approximated time it takes to execute fmt.Println() and terminate your app in your environment. If you do other "lengthy" task before the Println() statement, such as this:
for i := 0; i < 5_000_000_000; i++ {
}
fmt.Println(n)
The race will be detected even with 50ms sleep too (because that loop will take some time to execute, allowing the racy write to occur before n is read for the fmt.Println() call and the app terminated). (A simple time.Sleep() would also do, I just didn't want anyone to draw the wrong conclusion that they "interact" with each other somehow.)

What is the most time efficient way to guarantee at least one nanosecond has elapsed in Go? time.Sleep(time.Nanosecond) can take milliseconds

I have two function calls that I would like to separate by at least a nanosecond. But I want the delay to be as small as possible.
The code below shows an empty for loop is much more efficient at this than using time.Sleep(time.Nanosecond)
Is there an even more efficient way to guarantee at least one nanosecond has elapsed?
func TimeWaster () {
start := uint64(time.Now().UnixNano())
stop := uint64(time.Now().UnixNano())
fmt.Println(time.Duration(stop-start))//0s
//no nanoseconds pass
start = uint64(time.Now().UnixNano())
time.Sleep(time.Nanosecond)
stop = uint64(time.Now().UnixNano())
fmt.Println(time.Duration(stop-start))//6.9482ms
//much *much* more than one nanosecond passes
start = uint64(time.Now().UnixNano())
for uint64(time.Now().UnixNano()) == start {
//intentionally empty loop
}
stop = uint64(time.Now().UnixNano())
fmt.Println(time.Duration(stop-start))//59.3µs
//much quicker than time.Sleep(time.Nanosecond), but still much slower than 1 nanosecond
}
The package you're using strangely enforces uniqueness of values by time, so all you need to do is loop until the time package is no longer reporting the same value for the current nanosecond. This doesn't happen after 1 nanosecond, in fact the resolution of the UnixNano is about 100 nanoseconds on my machine and only updates about every 0.5 milliseconds.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(time.Now().UnixNano())
smallWait()
fmt.Println(time.Now().UnixNano())
}
func smallWait() {
for start := time.Now().UnixNano(); time.Now().UnixNano() == start; {}
}
The loop is pretty self-explanatory, just repeat until the UnixNano() is different

How to measure execution time of function in golang, excluding waiting time

I have a demand to measure execute time(cpu cost) of plugins in go, we can treat plugins as functions, there may be many goroutine running in the same time. More precisely, the execute time should exclude idle time(goroutine waiting time), only cpu acquire time(of current goroutine).
it's like:
go func(){
// this func is a plugin
** start to record cpu acquire time of current func/plugin/goroutine **
** run code **
** stop to record cpu acquire time of current func/plugin/goroutine **
log.Debugf("This function is buzy for %d millisecs.", cpuAcquireTime)
** report cpuAcquirTime to monitor **
}()
In my circunstance, it's hard to make unit test to measure function, the code is hard to decouple.
I search google and stackoverflow and find no clue, is there any solution to satisfy my demand, and does it take too much resource?
There is no built-in way in Go to measure CPU time, but you can do it in a platform-specific way.
For example, on POSIX systems (e.g. Linux) use clock_gettime with CLOCK_THREAD_CPUTIME_ID as the parameter.
Similarly you can use CLOCK_PROCESS_CPUTIME_ID to measure process CPU time and CLOCK_MONOTONIC for elapsed time.
Example:
package main
/*
#include <pthread.h>
#include <time.h>
#include <stdio.h>
static long long getThreadCpuTimeNs() {
struct timespec t;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t)) {
perror("clock_gettime");
return 0;
}
return t.tv_sec * 1000000000LL + t.tv_nsec;
}
*/
import "C"
import "fmt"
import "time"
func main() {
cputime1 := C.getThreadCpuTimeNs()
doWork()
cputime2 := C.getThreadCpuTimeNs()
fmt.Printf("CPU time = %d ns\n", (cputime2 - cputime1))
}
func doWork() {
x := 1
for i := 0; i < 100000000; i++ {
x *= 11111
}
time.Sleep(time.Second)
}
Output:
CPU time = 31250000 ns
Note the output is in nanoseconds. So here CPU time is 0.03 sec.
For people who stumble on this later like I did, you can actually use the built-in syscall.Getrusage instead of using Cgo. An example of this looks like
func GetCPU() int64 {
usage := new(syscall.Rusage)
syscall.Getrusage(syscall.RUSAGE_SELF, usage)
return usage.Utime.Nano() + usage.Stime.Nano()
}
where I have added up the Utime (user CPU time) and Stime (system CPU time) of the calling process (RUSAGE_SELF) after converting them both to nanoseconds. man 2 getrusage has a bit more information on this system call.
The documentation for syscall.Timeval suggests that Nano() returns the time in nanoseconds since the Unix epoch, but in my tests and looking at the implementation it appears actually to return just the CPU time in nanoseconds, not in nanoseconds since the Unix epoch.

Golang Timers with 0 length

I've written a code snipped that creates a timer with a 0 length time, and it does not immediately expire (which is what I expected). A very short sleep call does make it expire, but I'm confused as to why.
The reason I care is that the code using this idea has a snippet that returns 0 on a low probability error, with the idea that the timer should be set to immediately expire, and retry a function. I do not believe that the nanosecond sleep needed here will affect my implementation, but it bothers me.
Did I make a mistake, is this expected behaviour?
Thanks!
package main
import (
"fmt"
"time"
)
func main() {
testTimer := time.NewTimer(time.Duration(0) * time.Millisecond)
fmt.Println(Expired(testTimer))
time.Sleep(time.Nanosecond)
fmt.Println(Expired(testTimer))
}
func Expired(T *time.Timer) bool {
select {
case <-T.C:
return true
default:
return false
}
}
Playground link: https://play.golang.org/p/xLLHoR8aKq
Prints
false
true
time.NewTimer() does not guarantee maximum wait time. It only guarantees a minimum wait time. Quoting from its doc:
NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
So passing a zero duration to time.NewTimer(), it's not a surprise the returned time.Timer is not "expired" immediately.
The returned timer could be "expired" immediately if the implementation would check if the passed duration is zero, and would send a value on the timer's channel before returning it, but it does not. Instead it starts an internal timer normally as it does for any given duration, which will take care of sending a value on its channel, but only some time in the future.
Note that with multiple CPU cores and with runtime.GOMAXPROCS() being greater than 1 there is a slight chance that another goroutine (internal to the time package) sends a value on the timer's channel before NewTimer() returns, but this is a very small chance... Also since this is implementation detail, a future version might add this "optimization" to check for 0 passed duration, and act as you expected it, but as with all implementation details, don't count on it. Count on what's documented, and expect no more.
Go's timer functions guarantee to sleep at least the specified time. See the docs for Sleep and NewTimer respectively:
Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
(emphasis added)
In your situation, you should probably just not use a timer in the situation that you don't want to sleep at all.
This is due to the internal time it takes to set up the timer object. If you'll note in the playground link below the timer does expire at the proper time, but the internal go routine that sets it up and starts it takes longer than your Expire function does to check it.
When the Timer expires, the current time will be sent on C (the channel)
So you'll notice that after it expires, it still sends the original time, because it has expired even before the nanosecond Sleep finished.
https://play.golang.org/p/Ghwq9kJq3J
package main
import (
"fmt"
"time"
)
func main() {
testTimer := time.NewTimer(0 * time.Millisecond)
Expired(testTimer)
time.Sleep(time.Nanosecond)
Expired(testTimer)
n := time.Now()
fmt.Printf("after waiting: %d\n", n.UnixNano())
}
func Expired(T *time.Timer) bool {
select {
case t:= <-T.C:
fmt.Printf("expired %d\n", t.UnixNano())
return true
default:
n := time.Now()
fmt.Printf("not expired: %d\n", n.UnixNano())
return false
}
}

High resolution timers (millisecond precision) in Go on Windows

I'm trying to use Go's time.Timers to schedule tasks that need to be run in the right order with a precision in the order of half a millisecond. This works perfectly fine on OSX and on Linux, but fails every time on Windows.
The following code demonstrates the issue. It sets 5 timers, the first one to 1 ms, the second to 2 ms, ..., and the last one to 5 ms. Once a timer fires, its number is printed. On OSX and Linux, this obviously produced "12345" as output, but on Windows the numbers are more or less random (tested on Win 7 and Windows Server 2012).
package main
import (
"fmt"
"time"
)
func main() {
var timer1, timer2, timer3, timer4, timer5 *time.Timer
timer1 = time.NewTimer(1 * time.Millisecond)
timer2 = time.NewTimer(2 * time.Millisecond)
timer3 = time.NewTimer(3 * time.Millisecond)
timer4 = time.NewTimer(4 * time.Millisecond)
timer5 = time.NewTimer(5 * time.Millisecond)
// should print 12345
for {
select {
case <-timer1.C:
fmt.Print("1")
case <-timer2.C:
fmt.Print("2")
case <-timer3.C:
fmt.Print("3")
case <-timer4.C:
fmt.Print("4")
case <-timer5.C:
fmt.Print("5")
case <-time.After(200 * time.Millisecond):
return // exit the program
}
}
}
I think this behavior is due to the changes made in Go 1.6 (https://golang.org/doc/go1.6#runtime, 4th paragraph), where the Windows timer precision was reduced from 1 ms to 16 ms, although it should also have occurred with shorter intervals (of the order of 100 μs) before.
Is there any way to reset the global Windows timer precision back to 1 ms, or to access a high resolution timer that would make the example above work?
Since Go 1.7, timers now have a higher resolution and this problem should not occur.

Resources