From: Shakeel Butt <shakeel.butt@linux.dev>
To: Joanne Koong <joannelkoong@gmail.com>
Cc: akpm@linux-foundation.org, yosry@kernel.org, hannes@cmpxchg.org,
mhocko@kernel.org, roman.gushchin@linux.dev,
muchun.song@linux.dev, david@kernel.org, ljs@kernel.org,
liam@infradead.org, vbabka@kernel.org, rppt@kernel.org,
surenb@google.com, kasong@tencent.com, qi.zheng@linux.dev,
baohua@kernel.org, baolin.wang@linux.alibaba.com,
axelrasmussen@google.com, yuanchu@google.com,
weixugc@google.com, baoquan.he@linux.dev,
cgroups@vger.kernel.org, linux-mm@kvack.org
Subject: Re: [PATCH v2] mm: move LRU size stat updates out of lru_lock in folio batches
Date: Thu, 29 Oct 2026 17:19:15 -0700 [thread overview]
Message-ID: <auPhT70GU-Zqe9Qy@linux.dev> (raw)
In-Reply-To: <20261029204157.117879-1-joannelkoong@gmail.com>
On Thu, Oct 29, 2026 at 01:41:57PM -0700, Joanne Koong wrote:
> folio_batch_move_lru() and folios_put_refs() take the lruvec's lru_lock
> once per batch, but they update the node and memcg lruvec stats for
> every folio in the batch while holding the lock. Only the LRU lists and
> the memcg lru_zone_size need lru_lock. Updating the stats while holding
> the lru_lock is unnecessary, and they make up a large part of the
> critical section, as seen in [1] where a change that added just a few
> instructions to the memcg stat update path cost about 2% throughput
> because the LRU stat updates run under lru_lock, and the overhead went
> away once they were moved outside of the lock.
>
> With many threads faulting and freeing memory in a single memcg, about
> half of all cycles are spent waiting for lru_lock, and the hold time of
> the lru_lock directly limits throughput. A perf profile with 80 threads
> faulting in and freeing shmem in a single memcg shows 52.8% of all
> cycles spent spinning on lru_lock, split evenly between
> folio_batch_move_lru() and folios_put_refs(). About 25% of the time
> spent holding lru_lock in those two paths goes to the LRU stat updates,
> mostly cache misses loading pn->memcg, pn->lruvec_stats_percpu and the
> per-cpu counters. The stat updates are only 0.7% of all cycles, but they
> lengthen the critical section that the other threads are waiting on.
>
> Accumulate the stat changes per LRU list while holding the lock instead,
> and only apply them after dropping the lock (while still inside
> the RCU read section that keeps the lruvec's memcg alive). This moves
> the stat updates out of the lru_lock critical section and turns the
> per-folio updates into at most one update per LRU list per batch.
This updating of stats after dropping the lru lock does introduce one behavior
change i.e. race between reparenting on offlining and stats update. I think it
is safe and correct though. Better to document it.
>
> The batch is attached to the lruvec while lru_lock is held so that the
> LRU add/del helpers don't need to pass it down. To keep struct lruvec
> from growing, cost_lock is moved into the hole after lru_lock, which
> leaves room for the batch pointer in the same cacheline as lru_lock.
>
> The stats can now lag behind the LRU lists by one batch, which is well
> within the per-cpu batching they already have.
>
> On a 2-socket Skylake (80 CPUs, 2 NUMA nodes), with threads pinned to
> CPUs repeatedly faulting in and freeing memory in a single memcg,
> average pages per second over 7 freshly created memcgs per run, 2 boots
> each:
>
> before after
> shmem, 20 threads 4.74M 5.47M (+15%)
> shmem, 80 threads 7.94M 10.33M (+30%)
> anon, 20 threads 3.04M 3.35M (+10%)
> anon, 80 threads 3.23M 3.41M (+5%)
>
> Link: https://lore.kernel.org/linux-fsdevel/CAJnrk1ZezmmzrYdf8KP6re_a76izMS=qGPeC8ZZXiteHyZZC-Q@mail.gmail.com/ [1]
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
prev parent reply other threads:[~2026-10-30 0:19 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 20:41 [PATCH v2] mm: move LRU size stat updates out of lru_lock in folio batches Joanne Koong
2026-10-30 0:19 ` Shakeel Butt [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=auPhT70GU-Zqe9Qy@linux.dev \
--to=shakeel.butt@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=axelrasmussen@google.com \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=baoquan.he@linux.dev \
--cc=cgroups@vger.kernel.org \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=joannelkoong@gmail.com \
--cc=kasong@tencent.com \
--cc=liam@infradead.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=qi.zheng@linux.dev \
--cc=roman.gushchin@linux.dev \
--cc=rppt@kernel.org \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=weixugc@google.com \
--cc=yosry@kernel.org \
--cc=yuanchu@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.