Skip to content

runtime: standardize meaning of procyield argument across architectures #81304

Open

Description

@rhysh

The runtime's platform-independent code uses procyield when it needs to briefly delay a thread while another makes progress. It takes an argument, which it calls "cycles". It's called in lock2, casgstatus, suspendG, lfstack.pop, spanSet.pop, and internal/sync.Mutex.lockSlow.

The implementations in procyieldAsm result in very much architecture-dependent behavior:

  • On GOARCH=amd64 and GOARCH=386, the PAUSE instruction provides delay for this purpose and when called with an argument of N, procyieldAsm will loop to execute the instruction N times. The latency of the instruction varies between microarchitectures, and even between cores in heterogenous chips. The wall-clock duration of the delay varies with the processor's current operating frequency.
  • Prior to CL 654895 (part of Go 1.26), the GOARCH=arm64 implementation used the YIELD instruction, which has meaning for SMT processors but ends up as a NOP on processors that have only one thread per core.
  • There are also architecture-specific looping implementations on loong64 and ppc64.
  • On mips, mips64, riscv64, s390x, and wasm, procyieldAsm is a simple RET with no loop at all.

CL 654895 changed procyieldAsm on GOARCH=arm64 to treat the argument as the number of nanoseconds to wait before returning, consulting a fast clock while looping to run ISB SY. That shows a path to a platform-independent contract.

What we have today is that a procyield(30) call on amd64 can be ~2 µs (or longer, if the CPU frequency has scaled down), or ~30 ns on arm64, or faster still on riscv64 (where the argument is ignored). The pop methods have platform-specific behavior (to only call procyield at all on arm64) since the contract isn't stable elsewhere.

Since no other platforms have attempted this sort of stability (across architectures, or across µarches / hardware generations), and since nanoseconds is a fine choice anyway, I propose that we adopt it as the contract. This will require updating the arch-specific implementations to interpret the argument as "nanoseconds", and re-tuning the arch-independent callers to expect a pause of that magnitude.

CC @golang/runtime

Activity

  1. prattmic commented on Sep 4, 2026

    @prattmic
    Member

    cc @golang/runtime

  2. added
    NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.
    on Sep 4, 2026
  3. added this to the Backlog milestone on Sep 15, 2026
  4. cherrymui commented on Sep 15, 2026

    @cherrymui
    Member

    This will require updating the arch-specific implementations to interpret the argument as "nanoseconds", and re-tuning the arch-independent callers to expect a pause of that magnitude.

    Discussed during triage. This sounds reasonable. We don't need to be very precise, maybe within 2x is good enough. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions