Understanding Linux CPU Scheduling

Unlock the hidden speed of your Linux system! In this post, we dive deep into CPU scheduler tuning with sysctl, explaining key parameters like sched_min_granularity_ns and sched_autogroup_enabled to help you achieve ultra-low latency and maximize performance for gaming and benchmarks.

Understanding Linux CPU Scheduling

Intro

Before we dive into each parameter, a quick reminder:
The Linux scheduler’s job is to share CPU time among all running tasks. It tries to balance responsiveness (how quickly apps react) and throughput (how much total work gets done).

When tuning these parameters, we’re effectively telling the kernel how aggressive or relaxed we want it to be about switching tasks and handling CPU load distribution.

For gaming, real-time workloads, or benchmarks, we often prioritize responsiveness and latency over raw fairness — meaning faster reaction times at the cost of slightly more CPU context-switching overhead.

1. kernel.sched_min_granularity_ns = 1000000

Default: 1000000 (1 millisecond)
Meaning: Minimum time slice (in nanoseconds) a task runs before the scheduler considers switching to another one.

  • Think of it as the minimum attention span the CPU gives to a task.
  • Lower values → the CPU switches between tasks more frequently → better responsiveness, but higher context-switching overhead.
  • Higher values → tasks run longer before being interrupted → better throughput, but slower reaction to input or real-time tasks.

Tuning tip:
Keep it low (like 1000000 or 750000) for gaming or low-latency performance.
Use higher (e.g. 2000000) for compute-heavy, background workloads.

2. kernel.sched_wakeup_granularity_ns = 1500000

Default: 1500000 (1.5 milliseconds)
Meaning: Controls how easily a waking task (like an app waiting for input or a network packet) can preempt a currently running one.

  • When a sleeping process wakes up, it “asks” to run.
  • If it’s given too much priority, it can constantly interrupt other processes, leading to thrashing.
  • A smaller value means tasks can preempt sooner — improving input latency (good for gaming, real-time), but may hurt overall throughput.

Tuning tip:
Lowering this to around 1000000 improves latency; keep it balanced with sched_min_granularity_ns.

3. kernel.sched_child_runs_first = 0

Default: 0 (off)
Meaning: Controls whether a new child process (created via fork()) should immediately run before the parent continues.

  • When 1, child processes run right away after being created.
  • When 0, the parent keeps running and the child waits for its turn.
  • Keeping it at 0 avoids sudden task switches and unexpected performance spikes, especially in fast-spawning workloads.

Tuning tip:
Keep this at 0 for predictable scheduling.
Enable only if your workload heavily relies on short-lived child processes.

4. kernel.sched_migration_cost = 500000

Default: 500000 (0.5 ms)
Meaning: Defines how “expensive” it is for the scheduler to move a task between CPU cores.

  • Higher value → the kernel avoids moving processes around, which improves cache locality (data stays in CPU caches longer).
  • Lower value → the kernel more aggressively balances load between CPUs, which may improve utilization but reduce cache efficiency.

Tuning tip:
Keep it around 500000 or even slightly higher (1000000) for games or latency-sensitive workloads to reduce CPU core migration jitter.
Reduce it if you want the system to distribute CPU load more evenly for multi-threaded, heavy parallel workloads.

5. kernel.sched_tunable_scaling = 0

Default: 1 or 2 (depends on kernel)
Meaning: Controls automatic scaling of scheduling parameters (like the two above) based on the number of CPUs.

When set to:

  • 0: Static — the same values apply across all CPUs.
  • 1: Logarithmic scaling — parameters scale logarithmically with CPU count.
  • 2: Linear scaling — parameters scale linearly with CPU count.

Tuning tip:
Set it to 0 when you’re manually tuning scheduler parameters and want consistent performance across all cores, regardless of how many CPUs the system has.

6. kernel.sched_autogroup_enabled = 0

Default: 1 (enabled)
Meaning: Controls “autogrouping” — the kernel’s feature that groups tasks by session (for example, all your desktop apps run in one autogroup).

Autogrouping improves fairness between users or desktop sessions, but can harm real-time or gaming performance because it limits per-task prioritization.

Tuning tip:
Set to 0 to disable autogrouping — this gives your active process (like a game or benchmark) more direct access to CPU time without the kernel trying to “balance fairness.”
This is one of the biggest boosts for low-latency gaming or benchmark accuracy.

Here’s a set of values that push Linux to behave more like a “real-time” or “gaming mode” system:

# /etc/sysctl.d/99-performance.conf
kernel.sched_min_granularity_ns = 1000000
kernel.sched_wakeup_granularity_ns = 1000000
kernel.sched_child_runs_first = 0
kernel.sched_migration_cost = 1000000
kernel.sched_tunable_scaling = 0
kernel.sched_autogroup_enabled = 0

Apply with:

sudo sysctl -p /etc/sysctl.d/99-performance.conf

Summary

Parameter Purpose Recommended for Performance
sched_min_granularity_ns Minimum task run time 1000000
sched_wakeup_granularity_ns Wakeup latency 1000000
sched_child_runs_first Control fork behavior 0
sched_migration_cost CPU migration delay 1000000
sched_tunable_scaling Disable scaling 0
sched_autogroup_enabled Disable autogroup 0

Read next

How to check health of NVMe SSD on ubuntu

A practical, step-by-step guide to checking NVMe SSD health on Ubuntu using nvme-cli, smartctl, and GNOME Disks. Learn how to read SMART data, spot early warning signs, run self-tests, update firmware, and keep your data safe.