time.Ticker的性能

huangapple go评论68阅读模式
英文:

Performance of time.Ticker

问题

无法找出在下面的for循环中我们花费了超过十微秒的地方,以至于我们错过了大量的时钟周期。

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	RunTicker(time.Millisecond, 10*time.Second) // 场景1
	RunTicker(10*time.Microsecond, 10*time.Second) // 场景2
}

func RunTicker(tickerInterval, tickerDuration time.Duration) {
	var counter int

	ctx, can := context.WithTimeout(context.Background(), tickerDuration)
	defer can()

	ticker := time.NewTicker(tickerInterval)

exitfor:
	for {
		select {
		case <-ticker.C:
			counter++
		case <-ctx.Done():
			ticker.Stop()
			break exitfor
		}
	}

	fmt.Printf("Tick interval %v and running for %v. Expected counter: %d but got %d\n", tickerInterval, tickerDuration, tickerDuration/tickerInterval, counter)
}

输出:

Tick interval 1ms and running for 10s. Expected counter: 10000 but got 9965
Tick interval 10μs and running for 10s. Expected counter: 1000000 but got 976590
英文:

Not able to find out where in the below for loop we are spending more than ten microseconds so that we are missing vast number of ticks?

package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	RunTicker(time.Millisecond, 10 * time.Second) // Scenario 1
	RunTicker(10 * time.Microsecond, 10 * time.Second) // Scenario 2
}

func RunTicker(tickerInterval, tickerDuration time.Duration) {
	var counter int

	ctx, can := context.WithTimeout(context.Background(), tickerDuration)
	defer can()

	ticker := time.NewTicker(tickerInterval)

exitfor:
	for {
		select {
		case &lt;-ticker.C:
			counter++
		case &lt;- ctx.Done():
			ticker.Stop()
			break exitfor
		}
	}

	fmt.Printf(&quot;Tick interval %v and running for %v.Expected counter: %d but got %d\n&quot;, tickerInterval, tickerDuration, tickerDuration/tickerInterval, counter)
}

Output:

Tick interval 1ms and running for 10s.Expected counter: 10000 but got 9965
Tick interval 10&#181;s and running for 10s.Expected counter: 1000000 but got 976590

答案1

得分: 8

一般来说,当一个API表示一个事件将花费X秒时,它实际上保证经过的时间将至少为X秒,而不会更少。特别是对于小的时间增量,这是一个重要的区别。

此外,考虑一下来自NewTicker文档的这一点:

每个tick的时间间隔由duration参数指定。ticker将调整时间间隔或丢弃ticks以弥补接收器的慢速。

在考虑到这两点的情况下,你真正拥有的唯一保证是实际的ticks数量将小于或等于你计算的预期数量,而不会超过。换句话说,在理想情况下,实际的ticks数等于预期的ticks数,而其他情况都会少于预期。

在这种小的时间增量(~<1ms)中,除了“用户代码”之外,可能还会有其他超过tick时间的事件,包括:

  • Goroutine调度逻辑(goroutine的休眠和恢复,线程切换)。
  • 垃圾回收(即使在循环期间没有产生垃圾,垃圾回收可能仍然“活动”,并且偶尔会检查垃圾)

这些其他因素可能同时发生,从而更有可能跳过或延迟一个tick。

可以将其类比为你有一个装满水的桶,你需要将其倒入另一个桶,然后是另一个桶,再然后是另一个桶,以此类推,直到1000个桶。你唯一能做的就是丢失水,一旦洒出去就无法再获得。在这种情况下,你永远不会期望将100%的水保留到最后。

这与你提到的情况类似,因为错误只会朝一个方向发展。延迟只能至少为指定的时间,而ticks只能丢失(不会获得额外的)。无论何时发生这些事件中的任何一个,就像丢失了一滴水一样。

英文:

In general, when an API says that an event will take X seconds, it's actually guaranteeing that the elapsed time will be at least X seconds, and not less. Especially for small time increments, this is an important distinction.

Also, consider this point from the NewTicker documentation:

> The period of the ticks is specified by the duration argument. The ticker will adjust the time interval or drop ticks to make up for slow receivers.

With these two points in mind, the only guarantee you really have is that the number of real ticks will be <= to the expected number you calculate, and no more. In other words, actual ticks == expected ticks only in the ideal case, and every other case will be less than that.

In these kinds of small time increments (~< 1ms), there could be other events besides "user code" that exceed the tick time including:

  • Goroutine scheduling logic (sleeping and resuming goroutines, thread switching).
  • Garbage collection (even if there's no garbage made during the loop, the GC is probably still "alive" and will occasionally check for garbage)

These other factors can coincide, making it even more likely that a tick will be skipped or delayed.

Think of it like you have a bucket full of water, and you need to pour it into another bucket, and then another bucket, and another one and so on for 1000s of buckets. The only thing you can do is lose water, and you can't gain any more once it's spilled. With this scenario, you would never expect to retain 100% of the water to the very end.

This is similar to the case you mention because the errors only go in one direction. Delays can only be at least the specified time, and ticks can only be lost (extras are never gained). Any time any of these events happen, it's like a drop of water is lost.

huangapple
  • 本文由 发表于 2021年7月2日 14:10:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/68220364.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定