Skip to content

proposal: runtime/metrics: provide histogram of goroutines' on-CPU time #63341

Description

@rhysh

The /sched/latencies:seconds metric is a sampled view into how long goroutines need to wait after becoming runnable before they receive on-CPU time. I propose adding another metric, tentatively named /sched/running:seconds, showing how long goroutines spend in the running state before either cooperatively yielding or being successfully preempted. It would be sampled at the same rate (an undocumented 1 in 8) as the "latencies" metric.

On the short end: Scheduling a goroutine onto a thread consumes resources, and applications that do very little of their own work each time aren't able to amortize that overhead very well. Putting that information in the same context as the metric for scheduling latency will help paint a more complete picture of the application's use of the scheduler (though of course not as complete as the runtime/trace package provides). It will allow easier comparison against general recommendations for sizing the units of work in concurrent programs (such as "split tasks into about 100µs") in a way that's accessible both at runtime and to test code.

On the long end: Goroutines that stay in the running state for more than 2 scheduler quanta (20ms), when async preemption is enabled, indicate that part of the app (or its use of the runtime) operates with preemption disabled for too long. This can make it easier to notice and track down problems like #31222 where a long non-preemptible unit of work ends up delaying the entire app by delaying one of the GC's stop-the-worlds. (The /gc/pauses:seconds metric shows the non-preemptible regions, but only when they coincide with a GC phase transition. On top of that, maybe we should have a finer-grained measure of how long preemption remains disabled; 20ms is a very long time already.) It can also indicate when the Go scheduler assigned a goroutine to a software thread, but the OS didn't schedule that software thread onto a hardware thread.

Throughout: Being able to calculate a continuously-updating (sampled) sum of on-CPU time allows comparisons against other perspectives such as RUSAGE_SELF or CLOCK_PROCESS_CPUTIME_ID. This lets the application notice that it's exceeding the OS-provided resources.

Existing:

/sched/latencies:seconds
	Distribution of the time goroutines have spent in the scheduler
	in a runnable state before actually running. Bucket counts
	increase monotonically.

Proposed addition:

/sched/running:seconds
	Distribution of the time goroutines have spent continuously in
	the running state before yielding. Bucket counts increase
	monotonically.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Proposalcompiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    Status
    No status
    Status
    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions