From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: linux-kernel@vger.kernel.org,
"Paul E. McKenney" <paulmck@kernel.org>,
Steven Rostedt <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
Dennis Zhou <dennis@kernel.org>, Tejun Heo <tj@kernel.org>,
Christoph Lameter <cl@gentwo.org>,
Martin Liu <liumartin@google.com>,
David Rientjes <rientjes@google.com>,
christian.koenig@amd.com, Shakeel Butt <shakeel.butt@linux.dev>,
SeongJae Park <sj@kernel.org>, Michal Hocko <mhocko@suse.com>,
Johannes Weiner <hannes@cmpxchg.org>,
Sweet Tea Dorminy <sweettea-kernel@dorminy.me>,
Lorenzo Stoakes <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Vlastimil Babka <vbabka@kernel.org>,
Christian Brauner <brauner@kernel.org>,
Wei Yang <richard.weiyang@gmail.com>,
Miaohe Lin <linmiaohe@huawei.com>,
Al Viro <viro@zeniv.linux.org.uk>, Yu Zhao <yuzhao@google.com>,
Roman Gushchin <roman.gushchin@linux.dev>,
Mateusz Guzik <mjguzik@gmail.com>,
Matthew Wilcox <willy@infradead.org>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
Aboorva Devarajan <aboorvad@linux.ibm.com>,
David Carlier <devnexen@gmail.com>,
Andrew Morton <akpm@linux-foundation.org>,
linux-mm@kvack.org, Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
linux-doc@vger.kernel.org, Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
Kees Cook <kees@kernel.org>,
linux-trace-kernel@vger.kernel.org, Jan Kara <jack@suse.cz>,
linux-fsdevel@vger.kernel.org, Oleg Nesterov <oleg@redhat.com>
Subject: Re: [RFC PATCH v24 00/10] Hierarchical Percpu Counters for RSS
Date: Mon, 9 Nov 2026 21:35:26 +0100 [thread overview]
Message-ID: <88a96126-11be-41cc-ab0d-372d32189e79@kernel.org> (raw)
In-Reply-To: <20261105203639.1459524-1-mathieu.desnoyers@efficios.com>
On 11/5/26 21:36, Mathieu Desnoyers wrote:
> Hi David,
Hi!
>
> Here is the hierarchical per-cpu counters series for the RSS counters,
> based on v7.4-rc1. It is an RFC because of the questions at the end,
> and because of the significant changes since v22 [1], notably the
> per-cpu counter size (long -> byte), the configuration heuristics, and
> the lazy activation introduced by patches 6 and 7. v24 fixes the bugs
> found by the Sashiko review of v23 [2], including a NULL pointer
> dereference in patches 3 to 5 on sparc64.
I think we discussed other possible applications (IIRC Michal expressed concern
if a single user is sufficient motivation for these additional LOC). Did
anything result from that discussion that is relevant to v24?
>
> The problem
> -----------
>
> Since commit f1a7941243c1 ("mm: convert mm's rss stats into
> percpu_counter"), the RSS counters are struct percpu_counter with a
> batch size of max(32, 2 * nr_cpus): their approximation error grows with
> O(nr_cpus^2), up to 1.1GB per counter at 384 logical CPUs.
Okay, so that's a per-MM problem, not a per-system problem. On a large system
with many CPUs I'd have said "likel we don't care" :)
> /proc/PID/status, /proc/PID/statm and the OOM killer moved to precise
> sums (commits 82241a83cd15 and 5898aa8f9a0b), but the other readers
> still use the approximation: the RSS high watermark (VmHWM, the
> ru_maxrss reported as "Maximum resident set size" by /usr/bin/time -v,
> taskstats), /proc/PID/stat (ps, top, monitoring agents), the taskstats
> RSS-time integral (coremem, sampled at each tick), NUMA balancing and
> the mark_victim tracepoint. On the test machine below, the
> /proc/PID/stat RSS of a test process was off by 17MB while
> single-threaded, and by up to 134MB with 64 threads.
What's the resulting impact of that (temporary) imprecision?
>
> Precise sums cannot fix the high watermark or the RSS-time integral:
> update_hiwater_rss() samples the RSS on every unmap path, and the
As a side-note, I absolutely hate update_hiwater_rss() on the unmap path.
> integral at each tick, while a precise sum reads every possible CPU,
> about 4us per counter at 384 logical CPUs. A smaller struct
> percpu_counter batch takes the counter spinlock on every carry: at batch
> 32, an update of the counters of a shared mm costs 4.4 to 69us with one
> thread per CPU, compared to 26ns to 1.2us with this series.
>
> The series
> ----------
>
> Patches 1 to 5 replace struct percpu_counter for the RSS counters with
> hierarchical per-cpu counters: per-cpu bytes carry into a tree of atomic
> counters shaped at boot from nr_cpu_ids, with a fixed batch of 32, an
> error bound in O(n*log(n)) and no global lock.
How does this interact with CPU hotplug?
> Each level carries only
> once its residual reaches the batch in either direction, so updates
> going back and forth around a boundary do not climb the tree. The
> comparison functions have no user in this series; a follow-up will use
> them for a faster OOM killer task selection.
>
> Patches 6 and 7 keep the RSS counters of single-threaded processes exact
> and without per-cpu memory: until a second thread shares the mm, the
> task running on the mm adds to the bias of each counter without atomic
> instructions, and other tasks add to its atomic bias, both read in O(1).
> Creating the second thread allocates the per-cpu counters and tree
> items, and activates the tree. struct percpu_counter cannot do this as
> is: its count is an s64 under a raw spinlock, which the remote updates
> (reclaim, khugepaged, remote GUP) would then take.
What was the particular reason to special-case single-threaded apps? Do we
expect them in practice to really be that RSS heavy+dynamic such that it would
matter?
>
> Patches 8 to 10 are the tools to tune the tree shape on other hardware:
> the percpu_counter_tree.arity= and rss_stat_batch= boot parameters, and
> a KUnit benchmark. The default shapes were measured on one machine,
> with up to 384 logical CPUs, so results from other hardware are welcome.
Hm, so that means one might have to fine tune these toggles to get the best
performance? That's a bit unfortunate.
>
> Results
> -------
>
> 2-socket AMD EPYC 9654, 192 cores, 384 logical CPUs with SMT (the CPU
> counts in this letter are logical CPUs, as in nr_cpu_ids), tree 8,8,8,
> batch 32, whole series; details in patches 3 and 7. The bare-metal
> results were measured on v7.3: the rebase onto v7.4-rc1 had no conflict,
> and the hpcc update costs of the series as sent, measured in a pinned
> 384-vCPU guest, match the bare-metal ones within 2%.
>
> Accuracy per counter: bound, and largest |approximate - precise| seen
> by get_mm_counter() while a process hops across all CPUs doing
> mmap/munmap of 1 to 16 pages (384-vCPU KVM guest pinned to the host):
>
> hpcc upstream
> bound 158.5MB 1.1GB
> measured, single-threaded 0 (exact) 841MB
> measured, with a second thread 96MB 845MB
>
> Update cost, ns per update per thread, one thread per CPU, bare metal
> (mm: the 4 counters of one mm; private: one counter per thread; batch
> 32: local change of the benchmark, bound 46.5MB):
>
> upstream upstream
> hpcc batch 768 batch 32
> mm, +1 26 288 4370
> mm, random +-32 135 385 69171
> mm, random +-512 1174 20229 69186
> private, random +-32 31 20 -
>
> /proc/PID/statm read with 1000 to 16000 processes, bare metal:
> 5.3-6.5us vs 16.5-19.0us for single-threaded processes, on par for
> multi-threaded ones (14.4-16.0us vs 14.5-17.1us, guest). fork + exit
> of a single-threaded child: 116us vs 139us. will-it-scale
> page_fault1, 2 and 3: no regression (-0.6% to +5.1%, one boot each,
> the upstream one on the previous day).
>
> Memory per mm, counting the slab objects of mm_struct and of the tree
> items, and the per-cpu memory of the RSS counters, in bytes (guest,
> v7.4-rc1):
>
> single-threaded threaded upstream
> 16 CPUs 1820 1948 2076
> 384 CPUs 1928 8145 8072
> 1024 CPUs 2185 17204 18569
>
> The tree items are allocated with the per-cpu counters when the second
> thread is created, and the mm_struct slab object keeps its upstream
> size.
>
> Behavior changes
> ----------------
>
> - The allocation of the per-cpu counters and tree items moves from
> fork() to the second thread (clone() with CLONE_VM, without
> CLONE_VFORK), which can then fail with -ENOMEM.
> - mm_struct gains rss_owner, the task running on the mm, compared with
> current and never dereferenced, and rss_stat_mutex, which serializes
> the activations of the tree, as mm_cid.mutex serializes the mode
> changes of mm_cid at fork and exit.
> - The precise sums (/proc/PID/status, /proc/PID/statm, the OOM killer)
> take no lock: under concurrent updates of a multi-threaded process,
> they can miss a carry on its way up the tree, where upstream counts
> each concurrent update either whole or not at all. They are exact for
> a process at rest.
> - The rss_stat tracepoint reports the approximation (patch 5): exact for
> single-threaded processes, within the bound otherwise.
> - Like struct percpu_counter, the tree is sized at boot and not
> reshaped on hotplug; its shape and bound depend on nr_cpu_ids, while
> the default upstream batch follows the online CPUs.
>
> Caveats
> -------
>
> - From 33 to 39 CPUs, up to 1.37 times less accurate than upstream.
> - The tree is shaped for the number of CPUs rounded up to a power of 2:
> just above a power of 2, the RSS counters of a threaded process, with
> rss_owner and the mutex, use up to 1.50 times the memory of upstream
> (129 CPUs, 1.28 times at 65 and 1.38 times at 257), compared to at
> most 0.90 times at a power of 2, and 1.02 times at 384 logical CPUs.
> - Private counters with random +-32 updates: 31ns vs 20ns, the price of
> a 7 times tighter bound.
> - A multi-threaded process which ran on few CPUs can hold more error
> than upstream: 2047 pages per CPU path vs 767 at 384 logical CPUs. A
> follow-up could index the counters by concurrency ID (mm_cid) instead
> of CPU, so the accuracy bound and the precise sums would scale with
> the concurrency the process uses.
> - Before activation, concurrent remote updates of a single-threaded
> process contend on one atomic per counter.
> - The second thread allocates per-cpu memory, which the per-cpu
> allocator serializes, as it does upstream at every fork: with 384
> threads at once, init + destroy of 4 counters takes 874us per
> operation (1004us upstream, 6.0us alone). Tree item slab objects
> owning their per-cpu counters, with slab destructors, could avoid it.
> - The accuracy was measured in a guest, with a debug patch and a test
> program which are not part of the series. The bound is analytic and
> tested in both directions.
[...]
>
> Questions
> ---------
>
> 1. Is mm->rss_owner acceptable, or would you prefer current->mm == mm
> (without PF_KTHREAD) and an "activated" flag?
I'm afraid I'm the wrong person to ask that question. I'd have to look into the
details. If it's just a simple member, we likely don't care about the addition
memory footprint (if that's your main concern).
> 2. Is moving the -ENOMEM from fork() to the second thread acceptable?
I'd assume that's not a problem.
> 3. Would you rather take the single-threaded mode first, on its own?
Again, the question is how much we care in practice, I guess.
> 4. Should the rss_stat tracepoint report the approximation, stay
> precise, or report both?
No strong optinion on that. Do we know who uses these and for which purpose?
> 5. Should the tuning tools be merged, built only with the benchmark
> config, or dropped later? Who could run them on non-AMD, 4-socket,
> arm64 or POWER machines?
Is there some way to have it be an auto-tuning instead of requiring this tooling?
> 6. Is tools/testing/hpcc the right home for the topology test?
Good question, I'd assume so.
--
Cheers,
David
prev parent reply other threads:[~2026-11-09 20:35 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 20:36 Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 01/10] lib: introduce hierarchical per-cpu counters Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 02/10] lib: test " Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 03/10] mm: use hierarchical per-cpu counters for the RSS counters Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 04/10] tools/testing/hpcc: add userspace test of hierarchical per-CPU counters Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 05/10] mm: report the approximate RSS in the rss_stat tracepoint Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 06/10] lib: percpu_counter_tree: add lazy tree activation Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 07/10] mm: keep the RSS counters of single-threaded processes exact Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 08/10] lib: percpu_counter_tree: add the arity boot parameter Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 09/10] mm: add the rss_stat_batch " Mathieu Desnoyers
2026-11-05 20:36 ` [RFC PATCH v24 10/10] lib: add a benchmark of hierarchical per-cpu counters Mathieu Desnoyers
2026-11-09 20:35 ` David Hildenbrand (Arm) [this message]
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=88a96126-11be-41cc-ab0d-372d32189e79@kernel.org \
--to=david@kernel.org \
--cc=aboorvad@linux.ibm.com \
--cc=akpm@linux-foundation.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=brauner@kernel.org \
--cc=bsegall@google.com \
--cc=christian.koenig@amd.com \
--cc=cl@gentwo.org \
--cc=corbet@lwn.net \
--cc=dennis@kernel.org \
--cc=devnexen@gmail.com \
--cc=dietmar.eggemann@arm.com \
--cc=hannes@cmpxchg.org \
--cc=jack@suse.cz \
--cc=juri.lelli@redhat.com \
--cc=kees@kernel.org \
--cc=kprateek.nayak@amd.com \
--cc=liam@infradead.org \
--cc=linmiaohe@huawei.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=liumartin@google.com \
--cc=ljs@kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mgorman@suse.de \
--cc=mhiramat@kernel.org \
--cc=mhocko@suse.com \
--cc=mingo@redhat.com \
--cc=mjguzik@gmail.com \
--cc=oleg@redhat.com \
--cc=paulmck@kernel.org \
--cc=peterz@infradead.org \
--cc=rdunlap@infradead.org \
--cc=richard.weiyang@gmail.com \
--cc=rientjes@google.com \
--cc=roman.gushchin@linux.dev \
--cc=rostedt@goodmis.org \
--cc=rppt@kernel.org \
--cc=shakeel.butt@linux.dev \
--cc=sj@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=surenb@google.com \
--cc=sweettea-kernel@dorminy.me \
--cc=tj@kernel.org \
--cc=vbabka@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=viro@zeniv.linux.org.uk \
--cc=vschneid@redhat.com \
--cc=willy@infradead.org \
--cc=yuzhao@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®