From: Zecheng Li <zecheng@google.com>
To: Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>
Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>,
Steven Rostedt <rostedt@goodmis.org>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
Xu Liu <xliuprof@google.com>,
Blake Jones <blakejones@google.com>,
Namhyung Kim <namhyung@kernel.org>,
Josh Don <joshdon@google.com>,
Madadi Vineeth Reddy <vineethr@linux.ibm.com>,
linux-kernel@vger.kernel.org, Zecheng Li <zecheng@google.com>
Subject: [PATCH v3 0/3] sched/fair: Optimize cfs_rq and sched_entity allocation for better data locality
Date: Tue, 1 Jul 2025 21:02:26 +0000 [thread overview]
Message-ID: <20250701210230.2985885-1-zecheng@google.com> (raw)
Hi all,
This patch series improves CFS cache performance by allocating cfs_rq
and sched_entity together in the per-cpu allocator. It allows for
replacing the pointer arrays in task_group with a per-cpu offset.
v3:
- Rebased on top of 6.16-rc4.
- Minor wording and comment updates.
v2:
https://lore.kernel.org/lkml/20250609193834.2556866-1-zecheng@google.com/
- Allocate cfs_rq and sched_entity together for non-root task group
instead of embedding sched_entity into cfs_rq to avoid increasing the
size of struct rq based on the feedback from Peter Zijlstra.
v1:
https://lore.kernel.org/lkml/20250604195846.193159-1-zecheng@google.com/
Accessing cfs_rq and sched_entity instances incurs many cache misses.
This series of patches aims to reduce these cache misses. A struct
cfs_rq instance is per CPU and per task_group. Each task_group instance
(and the root runqueue) holds cfs_rq instances per CPU. Additionally,
there are corresponding struct sched_entity instances for each cfs_rq
instance (except the root). Currently, both cfs_rq and sched_entity
instances are allocated in NUMA-local memory using kzalloc_node, and
tg->cfs_rq and tg->se are arrays of pointers.
Original memory layout:
tg->cfs_rq = kcalloc(nr_cpu_ids, sizeof(cfs_rq), GFP_KERNEL);
tg->se = kcalloc(nr_cpu_ids, sizeof(se), GFP_KERNEL);
+----+ +-----------------+
| tg | ----> | cfs_rq pointers |
+----+ +-----------------+
| | |
v v v
cfs_rq cfs_rq cfs_rq
+----+ +--------------------+
| tg | ----> | sched_entity ptrs |
+----+ +--------------------+
| | |
v v v
se se se
Layout after Optimization:
+--------+ | CPU 0 | | CPU 1 | | CPU 2 |
| tg | | percpu | | percpu | | percpu |
| | ... ... ...
| percpu | -> | cfs_rq | | cfs_rq | | cfs_rq |
| offset | | se | | se | | se |
+--------+ +--------+ +--------+ +--------+
The optimization includes two parts:
1) Co-allocate cfs_rq and sched_entity for non-root task groups.
- This benefits loading the sched_entity for the parent runqueue.
Currently it incurs pointer chasing, i.e., cfs_rq->tg->se[cpu]. After
co-locating, the sched_entity fields can be loaded with simple offset
computations from cfs_rq.
2) Allocate the combined cfs_rq/se struct using percpu allocator.
- Accesses to cfs_rq instances in hot paths are mostly iterating through
multiple task_groups for the same CPU. Therefore, the new percpu
layout can reuse the base pointer, and they are more likely to reside
in the CPU cache than the per-task_group pointer arrays.
- This optimization also reduces the memory needed for the array of
pointers.
To measure the impact of the patch series, we construct a tree structure
hierarchy of cgroups, with “width” and “depth” parameters controlling
the number of children per node and the depth of the tree. Each leaf
cgroup runs a schbench workload and gets an 80% quota of the total CPU
quota divided by number of leaf cgroups (in other words, the target CPU
load is set to 80%) to exercise the throttling functions. Bandwidth
control period is set to 10ms. We run the benchmark on Intel and AMD
machines; each machine has hundreds of threads.
Tests were conducted on 6.15.
| Kernel LLC Misses | depth 3 width 10 | depth 5 width 4 |
+-------------------+---------------------+---------------------+
| AMD-orig | [2218.98, 2241.89]M | [2599.80, 2645.16]M |
| AMD-opt | [1957.62, 1981.55]M | [2380.47, 2431.86]M |
| Change | -11.69% | -8.248% |
| Intel-orig | [1580.53, 1604.90]M | [2125.37, 2208.68]M |
| Intel-opt | [1066.94, 1100.19]M | [1543.77, 1570.83]M |
| Change | -31.96% | -28.13% |
There's also a 25% improvement on kernel IPC on the AMD system. On
Intel, the improvement is 3% despite a greater LLC miss reduction.
Other workloads without CPU share limits, while also running in a cgroup
hierarchy with O(1000) instances, show no obvious regression:
sysbench, hackbench - lower is better; ebizzy - higher is better.
workload | base | opt | metric
----------+-----------------------+-----------------------+------------
sysbench | 63.55, [63.04, 64.05] | 64.36, [62.97, 65.75] | avg latency
hackbench | 36.95, [35.45, 38.45] | 37.12, [35.81, 38.44] | time
ebizzy | 610.7, [569.8, 651.6] | 613.5, [592.1, 635.0] | record/s
Zecheng Li (3):
sched/fair: Co-locate cfs_rq and sched_entity
sched/fair: Remove task_group->se pointer array
sched/fair: Allocate both cfs_rq and sched_entity with per-cpu
kernel/sched/core.c | 40 +++++++-------------
kernel/sched/debug.c | 2 +-
kernel/sched/fair.c | 87 ++++++++++++++++----------------------------
kernel/sched/sched.h | 48 ++++++++++++++++++++----
4 files changed, 87 insertions(+), 90 deletions(-)
base-commit: 66701750d5565c574af42bef0b789ce0203e3071
--
2.50.0
next reply other threads:[~2025-07-01 21:03 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-01 21:02 Zecheng Li [this message]
2025-07-01 21:02 ` [PATCH v3 1/3] sched/fair: Co-locate cfs_rq and sched_entity Zecheng Li
2025-07-01 21:02 ` [PATCH v3 2/3] sched/fair: Remove task_group->se pointer array Zecheng Li
2025-07-01 21:02 ` [PATCH v3 3/3] sched/fair: Allocate both cfs_rq and sched_entity with per-cpu Zecheng Li
2025-07-16 8:50 ` kernel test robot
2025-07-25 21:05 ` Zecheng Li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250701210230.2985885-1-zecheng@google.com \
--to=zecheng@google.com \
--cc=blakejones@google.com \
--cc=bsegall@google.com \
--cc=dietmar.eggemann@arm.com \
--cc=joshdon@google.com \
--cc=juri.lelli@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=vincent.guittot@linaro.org \
--cc=vineethr@linux.ibm.com \
--cc=vschneid@redhat.com \
--cc=xliuprof@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®