Golang CPU affinity on Linux: sched_setaffinity, taskset, GOMAXPROCS

Golang CPU affinity on Linux: cpu masking with golang.org/x/sys/unix SchedSetaffinity, taskset from Go, runtime.LockOSThread for set thread affinity, and why gomaxprocs golang is not affinity.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Golang CPU affinity on Linux: sched_setaffinity, taskset, GOMAXPROCS

This article is about golang cpu affinity and how to set cpu affinity from Go on Linux: the kernel’s cpu masking for threads, and the difference from gomaxprocs golang settings. People also phrase this as set thread affinity; on Linux that is still an OS-thread property, so Go code typically combines runtime.LockOSThread with sched_setaffinity. Background on hardware threading is in CPU, cores, and threads explained; for thread counts per process, see check threads per process.

The snippets that call sched_setaffinity or taskset are marked non-Run: they need Linux, taskset on PATH for the shell example, and a module dependency on golang.org/x/sys/unix. They were validated in a small local go run on Linux, not in the browser Playground.


What CPU affinity does

The scheduler may run a runnable OS thread on any allowed logical CPU. CPU affinity restricts that thread to a subset by setting a mask of allowed CPUs. That is separate from how many goroutines your program creates or how busy it is.


Gomaxprocs golang is not affinity

runtime.GOMAXPROCS(n) caps how many OS threads run user Go code at once; it does not pin those threads to specific cores. For what GOMAXPROCS interacts with, read goroutine vs threads in Go.

go
package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Println("NumCPU:", runtime.NumCPU())
	fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
}
Output

Run prints the machine’s reported CPU count and the current GOMAXPROCS limit.


Use unix.CPUSet and unix.SchedGetaffinity / unix.SchedSetaffinity instead of hand-rolled syscall.RawSyscall with a uintptr mask, which uses the wrong size for the kernel cpu_set_t and is unsafe.

Save as main.go in a module, run go get golang.org/x/sys/unix, then go run . on Linux:

go
package main

import (
	"fmt"
	"runtime"

	"golang.org/x/sys/unix"
)

func main() {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cur unix.CPUSet
	if err := unix.SchedGetaffinity(0, &cur); err != nil {
		fmt.Println("SchedGetaffinity:", err)
		return
	}
	fmt.Println("CPUs in mask before:", cur.Count())

	var set unix.CPUSet
	set.Zero()
	set.Set(0)
	if err := unix.SchedSetaffinity(0, &set); err != nil {
		fmt.Println("SchedSetaffinity:", err)
		return
	}

	var after unix.CPUSet
	if err := unix.SchedGetaffinity(0, &after); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("pinned to CPU 0 only:", after.IsSet(0) && after.Count() == 1)
}

On a multi-CPU machine you normally see more than one CPU allowed before the call, then true after restricting to CPU 0 only. Adjust set.Set for the CPUs you want; multiple Set calls build a larger cpu mask.


Set cpu affinity with taskset from Go

The taskset utility changes affinity for a PID; wrapping it with os/exec avoids cgo while still expressing set cpu affinity in shell semantics.

go
package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	pid := fmt.Sprintf("%d", os.Getpid())
	before, err := exec.Command("taskset", "-p", pid).CombinedOutput()
	if err != nil {
		fmt.Println(err, string(before))
		return
	}
	fmt.Print(string(before))

	out, err := exec.Command("taskset", "-cp", "0", pid).CombinedOutput()
	if err != nil {
		fmt.Println(err, string(out))
		return
	}
	fmt.Print(string(out))

	after, err := exec.Command("taskset", "-p", pid).CombinedOutput()
	if err != nil {
		fmt.Println(err, string(after))
		return
	}
	fmt.Print(string(after))
}

-cp prints and sets the mask; the exact hex mask depends on the machine. This pattern is useful for ops scripts; prefer unix.SchedSetaffinity inside pure Go libraries when you can add golang.org/x/sys.


Pitfalls: permissions, containers, and performance

  • Capabilities and policy: changing another process or restrictive environments may need extra privileges; pinning the current thread as above is the common local test.
  • Kubernetes and cgroups: you may be required to set allowed CPUs via the orchestrator instead of from inside the pod.
  • Performance: forcing a busy CPU can hurt latency if that CPU is already hot; measure with tools such as perf or sar rather than pinning blindly.

Summary

Golang cpu affinity on Linux means applying a cpu mask to an OS thread with sched_setaffinity, most safely through golang.org/x/sys/unix and a real unix.CPUSet. Set thread affinity from Go usually adds runtime.LockOSThread so the mask applies to the right thread. gomaxprocs golang settings control parallel Go scheduling width, not which hardware threads run your process. taskset from os/exec remains a practical option for scripts and for how to set cpu affinity without writing syscalls yourself.


References


Frequently Asked Questions

1. Does GOMAXPROCS set CPU affinity?

No. GOMAXPROCS adjusts the runtime thread pool size for executing goroutines; it does not set the kernel CPU mask. Use SchedSetaffinity or taskset for affinity.

2. Why use runtime.LockOSThread before SchedSetaffinity?

Affinity is per OS thread. LockOSThread keeps the current goroutine on one thread so a following SchedSetaffinity call affects that thread instead of whichever thread the scheduler picked.

3. Why prefer golang.org/x/sys/unix over syscall.SchedSetaffinity?

The syscall package is frozen; x/sys/unix tracks kernel types such as CPUSet with the correct size for sched_getaffinity and sched_setaffinity, avoiding incorrect masks from guessing sizes with uintptr.

4. Can I always change affinity inside a container?

Orchestrators and cgroup cpusets may override or forbid changes. Prefer setting allowed CPUs in the platform configuration when possible.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …