Should you set GOMAXPROCS higher than the number of CPUs for I/O-bound workloads?
Usually no. This question often catches people who are used to Java or C thread pools. In Go, a goroutine blocked on network I/O does not hold a thread or a P. It parks on the netpoller, and the P runs other goroutines. A goroutine blocked in a syscall gives its P away, which is called hand-off. So I/O-bound Go programs already use the CPUs fully with GOMAXPROCS = cores.
A higher value brings more contexts to switch between, more idle Ps spinning, more GC worker goroutines, and in containers more CPU throttling. A lower value can help in some cases:
- A side-car process that should not use the whole node.
- A latency-sensitive service that shares cores with other processes.
- Reducing CFS throttling in a container, although this is automatic since Go 1.25.
If you need to limit concurrency for a specific resource, such as database connections or calls to a downstream service, use a semaphore or a worker pool. Do not change GOMAXPROCS for that.
What the interviewer wants to hear: GOMAXPROCS limits parallel execution of Go code. It is not the limit on concurrency.
More on Goroutines & the Scheduler
- Q154How does a goroutine's stack grow? What changed from segmented to contiguous stacks?
- Q155What is GOMAXPROCS, what is its default, and what changed in Go 1.25 for containers?
- Q157What happens to the scheduler when a goroutine makes a blocking syscall?
- Q158What is the netpoller and how does it make network I/O look blocking yet scale?
- Q159What is sysmon and what does it do?
- Q160What does runtime.Gosched do and when (if ever) should you use it?