Skip to contentSkip to conversation

mm: PCP high auto-tuning

RFC In review Huang Ying 3 participants 22 messages Jul 10, 2023 – Jul 24, 2023

Subsystems (4)
lore
Share & tools
lore

Huang Ying

author
#0 lore raw

cover mm: PCP high auto-tuning

The page allocation performance requirements of different workloads are usually different. So, we need to tune the PCP (Per-CPU Pageset) high on each CPU automatically to optimize the page allocation performance.

In this patchset, a PCP high auto-tuning mechanism is implemented. The description of the algorithm and the test results can be found in the patch description of [2/2].

Best Regards,
Huang, Ying

author
#1 lore raw

RFC 1/2 mm: add framework for PCP high auto-tuning

The page allocation performance requirements of different workloads are usually different. So, we often need to tune PCP (per-CPU pageset) high to optimize the workload page allocation performance. Now, we have a system wide sysctl knob (percpu_pagelist_high_fraction) to tune PCP high by hand. But, it's hard to find out the best value by hand. And one global configuration may not work best for the different workloads that run on the same system. One solution to these issues is to tune PCP high of each CPU automatically.

This patch adds the framework for PCP high auto-tuning. With it, pcp->high will be changed automatically by tuning algorithm at runtime. Its default value (pcp->high_def) is the original PCP high value calculated based on low watermark pages or percpu_pagelist_high_fraction sysctl knob. To avoid putting too many pages in PCP, the original limit of percpu_pagelist_high_fraction sysctl knob, MIN_PERCPU_PAGELIST_HIGH_FRACTION, is used to calculate the max PCP high value (pcp->high_max).

This patch only adds the framework, so pcp->high will be set to pcp->high_def always. We will add actual auto-tuning algorithm in the next patch in the series.

Cc Andrew Morton <[email protected]>
Cc Mel Gorman <[email protected]>
Cc Vlastimil Babka <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Johannes Weiner <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Pavel Tatashin <[email protected]>
Cc Matthew Wilcox <[email protected]>
---
 include/linux/mmzone.h |  5 ++-
 mm/page_alloc.c        | 79 +++++++++++++++++++++++++++---------------
 2 files changed, 55 insertions(+), 29 deletions(-)
include/linux/mmzone.h +4 -1
index a4889c9d4055..7e2c1864a9ea 100644
@@ -663,6 +663,8 @@ struct per_cpu_pages {
 	spinlock_t lock;	/* Protects lists field */
 	int count;		/* number of pages in the list */
 	int high;		/* high watermark, emptying needed */
+	int high_def;		/* default high watermark */
+	int high_max;		/* max high watermark */
 	int batch;		/* chunk size for buddy add/remove */
 	short free_factor;	/* batch scaling factor during free */
 #ifdef CONFIG_NUMA
@@ -820,7 +822,8 @@ struct zone {
 	 * the high and batch values are copied to individual pagesets for
 	 * faster access
 	 */
-	int pageset_high;
+	int pageset_high_def;
+	int pageset_high_max;
 	int pageset_batch;
 
 #ifndef CONFIG_SPARSEMEM
mm/page_alloc.c +51 -28

188 lines not on this page · raw message

Signature · 1 line
2.39.2
reviewer
#2 lore raw

Re: [RFC 1/2] mm: add framework for PCP high auto-tuning

Quoted 9 lines · Huang Ying

The page allocation performance requirements of different workloads are usually different. So, we often need to tune PCP (per-CPU pageset) high to optimize the workload page allocation performance. Now, we have a system wide sysctl knob (percpu_pagelist_high_fraction) to tune PCP high by hand. But, it's hard to find out the best value by hand. And one global configuration may not work best for the different workloads that run on the same system. One solution to these issues is to tune PCP high of each CPU automatically.

Quoted 8 lines

This patch adds the framework for PCP high auto-tuning. With it, pcp->high will be changed automatically by tuning algorithm at runtime. Its default value (pcp->high_def) is the original PCP high value calculated based on low watermark pages or percpu_pagelist_high_fraction sysctl knob. To avoid putting too many pages in PCP, the original limit of percpu_pagelist_high_fraction sysctl knob, MIN_PERCPU_PAGELIST_HIGH_FRACTION, is used to calculate the max PCP high value (pcp->high_max).

It would have been very helpful to describe the basic entry points to the auto-tuning. AFAICS the central place of the tuning is tune_pcp_high which is called from the freeing path. Why? Is this really a good place considering this is a hot path? What about the allocation path? Isn't that a good spot to watch for the allocation demand?

Also this framework seems to be enabled by default. Is this really desirable? What about workloads tuning the pcp batch size manually? Shouldn't they override any auto-tuning?

Signature · 2 lines
Michal Hocko
SUSE Labs
#3 lore raw

Re: [RFC 1/2] mm: add framework for PCP high auto-tuning

Quoted 19 lines · Michal Hocko
Quoted 9 lines · Huang Ying

The page allocation performance requirements of different workloads are usually different. So, we often need to tune PCP (per-CPU pageset) high to optimize the workload page allocation performance. Now, we have a system wide sysctl knob (percpu_pagelist_high_fraction) to tune PCP high by hand. But, it's hard to find out the best value by hand. And one global configuration may not work best for the different workloads that run on the same system. One solution to these issues is to tune PCP high of each CPU automatically.

Quoted 8 lines

This patch adds the framework for PCP high auto-tuning. With it, pcp->high will be changed automatically by tuning algorithm at runtime. Its default value (pcp->high_def) is the original PCP high value calculated based on low watermark pages or percpu_pagelist_high_fraction sysctl knob. To avoid putting too many pages in PCP, the original limit of percpu_pagelist_high_fraction sysctl knob, MIN_PERCPU_PAGELIST_HIGH_FRACTION, is used to calculate the max PCP high value (pcp->high_max).

Quoted 5 lines

It would have been very helpful to describe the basic entry points to the auto-tuning. AFAICS the central place of the tuning is tune_pcp_high which is called from the freeing path. Why? Is this really a good place considering this is a hot path? What about the allocation path? Isn't that a good spot to watch for the allocation demand?

Yes. The main entry point to the auto-tuning is tune_pcp_high(). Which is called from the freeing path because pcp->high is only used by page freeing. It's possible to call it in allocation path instead. The drawback is that the pcp->high may be updated a little later in some situations. For example, if there are many page freeing but no page allocation for quite long time. But I don't think this is a serious problem.

Quoted 3 lines

Also this framework seems to be enabled by default. Is this really desirable? What about workloads tuning the pcp batch size manually? Shouldn't they override any auto-tuning?

In the current implementation, the pcp->high will be tuned between original pcp high (default or tuned manually) and the max pcp high (via MIN_PERCPU_PAGELIST_HIGH_FRACTION). So the high value tuned manually is respected at some degree.

So you think that it's better to disable auto-tuning if PCP high is tuned manually?

Best Regards,
Huang, Ying

reviewer
#4 lore raw

Re: [RFC 1/2] mm: add framework for PCP high auto-tuning

Quoted 27 lines · Ying
Quoted 19 lines · Michal Hocko
Quoted 9 lines · Huang Ying

The page allocation performance requirements of different workloads are usually different. So, we often need to tune PCP (per-CPU pageset) high to optimize the workload page allocation performance. Now, we have a system wide sysctl knob (percpu_pagelist_high_fraction) to tune PCP high by hand. But, it's hard to find out the best value by hand. And one global configuration may not work best for the different workloads that run on the same system. One solution to these issues is to tune PCP high of each CPU automatically.

Quoted 8 lines

This patch adds the framework for PCP high auto-tuning. With it, pcp->high will be changed automatically by tuning algorithm at runtime. Its default value (pcp->high_def) is the original PCP high value calculated based on low watermark pages or percpu_pagelist_high_fraction sysctl knob. To avoid putting too many pages in PCP, the original limit of percpu_pagelist_high_fraction sysctl knob, MIN_PERCPU_PAGELIST_HIGH_FRACTION, is used to calculate the max PCP high value (pcp->high_max).

Quoted 5 lines

It would have been very helpful to describe the basic entry points to the auto-tuning. AFAICS the central place of the tuning is tune_pcp_high which is called from the freeing path. Why? Is this really a good place considering this is a hot path? What about the allocation path? Isn't that a good spot to watch for the allocation demand?

Quoted 7 lines

Yes. The main entry point to the auto-tuning is tune_pcp_high(). Which is called from the freeing path because pcp->high is only used by page freeing. It's possible to call it in allocation path instead. The drawback is that the pcp->high may be updated a little later in some situations. For example, if there are many page freeing but no page allocation for quite long time. But I don't think this is a serious problem.

I consider it a serious flaw in the framework as it cannot cope with the transition of the allocation pattern (e.g. increasing the allocation pressure).

Quoted 11 lines
Quoted 3 lines

Also this framework seems to be enabled by default. Is this really desirable? What about workloads tuning the pcp batch size manually? Shouldn't they override any auto-tuning?

In the current implementation, the pcp->high will be tuned between original pcp high (default or tuned manually) and the max pcp high (via MIN_PERCPU_PAGELIST_HIGH_FRACTION). So the high value tuned manually is respected at some degree.

So you think that it's better to disable auto-tuning if PCP high is tuned manually?

Yes, I think this is a much safer option. For two reasons 1) it is less surprising to setups which know what they are doing by configuring the batching and 2) the auto-tuning needs a way to get disabled in case there are pathological patterns in behavior.

Signature · 2 lines
Michal Hocko
SUSE Labs
#5 lore raw

Re: [RFC 1/2] mm: add framework for PCP high auto-tuning

Quoted 36 lines · Michal Hocko
Quoted 27 lines · Ying
Quoted 19 lines · Michal Hocko
Quoted 9 lines · Huang Ying

The page allocation performance requirements of different workloads are usually different. So, we often need to tune PCP (per-CPU pageset) high to optimize the workload page allocation performance. Now, we have a system wide sysctl knob (percpu_pagelist_high_fraction) to tune PCP high by hand. But, it's hard to find out the best value by hand. And one global configuration may not work best for the different workloads that run on the same system. One solution to these issues is to tune PCP high of each CPU automatically.

Quoted 8 lines

This patch adds the framework for PCP high auto-tuning. With it, pcp->high will be changed automatically by tuning algorithm at runtime. Its default value (pcp->high_def) is the original PCP high value calculated based on low watermark pages or percpu_pagelist_high_fraction sysctl knob. To avoid putting too many pages in PCP, the original limit of percpu_pagelist_high_fraction sysctl knob, MIN_PERCPU_PAGELIST_HIGH_FRACTION, is used to calculate the max PCP high value (pcp->high_max).

Quoted 5 lines

It would have been very helpful to describe the basic entry points to the auto-tuning. AFAICS the central place of the tuning is tune_pcp_high which is called from the freeing path. Why? Is this really a good place considering this is a hot path? What about the allocation path? Isn't that a good spot to watch for the allocation demand?

Quoted 7 lines

Yes. The main entry point to the auto-tuning is tune_pcp_high(). Which is called from the freeing path because pcp->high is only used by page freeing. It's possible to call it in allocation path instead. The drawback is that the pcp->high may be updated a little later in some situations. For example, if there are many page freeing but no page allocation for quite long time. But I don't think this is a serious problem.

Quoted 3 lines

I consider it a serious flaw in the framework as it cannot cope with the transition of the allocation pattern (e.g. increasing the allocation pressure).

Sorry, my previous words are misleading. What I really wanted to say is that the problem may be just theoretical. Anyway, I will try to avoid this problem in the future version.

Quoted 9 lines
Quoted 9 lines
Quoted 3 lines

Also this framework seems to be enabled by default. Is this really desirable? What about workloads tuning the pcp batch size manually? Shouldn't they override any auto-tuning?

In the current implementation, the pcp->high will be tuned between original pcp high (default or tuned manually) and the max pcp high (via MIN_PERCPU_PAGELIST_HIGH_FRACTION). So the high value tuned manually is respected at some degree.

Quoted 7 lines
Quoted 2 lines

So you think that it's better to disable auto-tuning if PCP high is tuned manually?

Yes, I think this is a much safer option. For two reasons 1) it is less surprising to setups which know what they are doing by configuring the batching and 2) the auto-tuning needs a way to get disabled in case there are pathological patterns in behavior.

OK.

Best Regards,
Huang, Ying

author
#6 lore raw

RFC 2/2 mm: alloc/free depth based PCP high auto-tuning

In reply to Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

To detect the repetitive allocation and freeing pattern, the alloc/free depth is calculated for each tuning period (1 second) on each CPU. To calculate the alloc/free depth, we track the alloc count. Which increases for page allocation from PCP and decreases for page freeing to PCP. The alloc depth is the maximum alloc count difference between the later large value and former small value. While, the free depth is the maximum alloc count difference between the former large value and the later small value.

Then, the average alloc/free depth in multiple tuning periods is calculated, with the old alloc/free depth decay in the average gradually.

Finally, the PCP high is set to be the smaller value of average alloc depth and average free depth, after clamped between the default and the max PCP high. In this way, pure allocation or freeing will not enlarge the PCP high because PCP doesn't help.

We have tested the algorithm with several workloads on Intel's 2-socket server machines.

Will-it-scale/page_fault1
=========================

On one socket of the system with 56 cores, 56 workload processes are run to stress the kernel memory allocator. Each workload process is put in a different memcg to eliminate the LRU lock contention.

                                       base     optimized
                                       ----     -----​----

Thoughput (GB/s) 34.3 75.0
native_queued_spin_lock_slowpath% 60.9 0.2

This is a simple workload that each process allocates 128MB pages then frees them repetitively. So, it's quite easy to detect its allocation and freeing pattern and adjust PCP high accordingly. The optimized kernel almost eliminates the lock contention cycles% (from 60.9% to 0.2%). And its benchmark score increases 118.7%.

Kbuild
======

"make -j 224" is used to build the kernel in parallel on the 2-socket server system with 224 logical CPUs.

                                       base     optimized
                                       ----     -----​----
Build time (s)                       162.67        162.67​
native_queued_spin_lock_slowpath%     17.00         12.28​
rmqueue%                              11.53          8.33​
Free_unref_page_list%                  3.85          0.54​
folio_lruvec_lock_irqsave%             1.21          1.96​

The optimized kernel reduces cycles of the page allocation/freeing functions from ~15.38% to ~8.87% via enlarging the PCP high when necessary. The system overhead lock contention cycles% decreases too. But the benchmark score hasn't visible change. There should be other bottlenecks.

We also captured /proc/meminfo during test. After a short while (about 10s) from the beginning of the test, the Memused (MemTotal - MemFree) of the optimized kernel is higher than that of the base kernel for the increased PCP high. But in the second half of the test, the Memused of the optimized kernel decreases to the same level of that of the base kernel. That is, PCP high is decreased effectively for the decreased page allocation requirements.

Netperf/SCTP_STREAM_MANY

On another 2-socket server with 128 logical CPUs. 64 processes of netperf are run with the netserver runs on the same machine (that is, loopback network is used).

                                      base      optimized
                                      ----      -----​----
Throughput (MB/s)​	              7136           8489       +19.0%
vmstat.cpu.id%​		                73.05          63.73     -9.3
vmstat.procs.r​		                34.1           45.6     +33.5%

meminfo.Memused​ 5479861 8492875 +55.0%
perf-stat.ps.cpu-cycles​ 1.04e+11 1.38e+11 +32.3%
perf-stat.ps.instructions​ 0.96e+11 1.14e+11 +17.8%
perf-profile.free_unref_page% 2.46 1.65 -0.8
latency.99%.__alloc_pages​ 4.28 2.21 -48.4%
latency.99%.__free_unref_page 4.11 0.87 -78.8%​

From the test results, the throughput of benchmark increases 19.0%. That comes from the increased CPU cycles and instructions per second (perf-stat.ps.cpu-cycles and perf-stat.ps.instructions), that is, reduced CPU idle. And, perf-profile shows that page allocator cycles% isn't high. So, the reduced CPU idle may comes from the page allocation/freeing latency reduction, which influence the network behavior.

Cc Andrew Morton <[email protected]>
Cc Mel Gorman <[email protected]>
Cc Vlastimil Babka <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Johannes Weiner <[email protected]>
Cc Dave Hansen <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Pavel Tatashin <[email protected]>
Cc Matthew Wilcox <[email protected]>
---
 include/linux/mmzone.h |  8 +++++++
 mm/page_alloc.c        | 50 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 56 insertions(+), 2 deletions(-)
include/linux/mmzone.h +8 -0
index 7e2c1864a9ea..cd9b497cd596 100644
@@ -670,6 +670,14 @@ struct per_cpu_pages {
 #ifdef CONFIG_NUMA
 	short expire;		/* When 0, remote pagesets are drained */
 #endif
+	int alloc_count;	/* alloc/free count from tune period start */
+	int alloc_high;		/* max alloc count from tune period start */
+	int alloc_low;		/* min alloc count from tune period start */
+	int alloc_depth;	/* alloc depth from tune period start */
+	int free_depth;		/* free depth from tune period start */
+	int avg_alloc_depth;	/* average alloc depth */
+	int avg_free_depth;	/* average free depth */
+	unsigned long tune_start; /* tune period start timestamp */
 
 	/* Lists of pages, one per migrate type stored on the pcp-lists */
 	struct list_head lists[NR_PCP_LISTS];
mm/page_alloc.c +48 -2

70 lines not on this page · raw message

Signature · 1 line
2.39.2
reviewer
#7 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

Quoted 13 lines

To detect the repetitive allocation and freeing pattern, the alloc/free depth is calculated for each tuning period (1 second) on each CPU. To calculate the alloc/free depth, we track the alloc count. Which increases for page allocation from PCP and decreases for page freeing to PCP. The alloc depth is the maximum alloc count difference between the later large value and former small value. While, the free depth is the maximum alloc count difference between the former large value and the later small value.

Then, the average alloc/free depth in multiple tuning periods is calculated, with the old alloc/free depth decay in the average gradually.

Quoted 7 lines

Finally, the PCP high is set to be the smaller value of average alloc depth and average free depth, after clamped between the default and the max PCP high. In this way, pure allocation or freeing will not enlarge the PCP high because PCP doesn't help.

We have tested the algorithm with several workloads on Intel's 2-socket server machines.

How does this scheme deal with memory pressure?

Signature · 2 lines
Michal Hocko
SUSE Labs
#8 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 11 lines · Michal Hocko
Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

Quoted 11 lines

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

I tend to agree. Tuning based on the recent allocation pattern without frees would make more sense and also be symmetric with how free_factor works. I suspect that time-based may be heavily orientated around the will-it-scale benchmark. While I only glanced at this, a few things jumped out

1. Time-based heuristics are not ideal. congestion_wait() and friends was an obvious case where time-based heuristics fell apart even before the event it waited on was removed. For congestion, it happened to work for slow storage for a while but that was about it. For allocation stream detection, it has a similar problem. If a process is allocating heavily, then fine, if it's in bursts of less than a second more than one second apart then it will not adapt. While I do not think it is explicitly mentioned anywhere, my understanding was that heuristics like this within mm/ should be driven by explicit events as much as possible and not time.

2. If time was to be used, it would be cheaper to have the simpliest possible state tracking in the fast paths and decay any resizing of the PCP within the vmstat updates (reuse pcp->expire except it applies to local pcps). Even this is less than ideal as the PCP may be too large for short periods of time but it may also act as a backstop for worst-case behaviour

3. free_factor is an existing mechanism for detecting recent patterns and adapting the PCP sizes. The allocation side should be symmetric and the events that should drive it are "refills" on the alloc side and "drains" on the free side. Initially it might be easier to have a single parameter that scales batch and high up to a limit

4. The amount of state tracked seems excessive and increases the size of the per-cpu structure by more than 1 cache line. That in itself may not be a problem but the state is tracked on every page alloc/free that goes through the fast path and it's relatively complex to track. That is a constant penalty in fast paths that may not may not be relevant to the workload and only sustained bursty allocation streams may offset the cost.

5. Memory pressure and reclaim activity does not appear to be accounted for and it's not clear if pcp->high is bounded or if it's possible for a single PCP to hide a large number of pages from other CPUs sharing the same node. The max size of the PCP should probably be explicitly clamped.

Signature · 2 lines
Mel Gorman
SUSE Labs
#9 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 24 lines · Mel Gorman
Quoted 11 lines · Michal Hocko
Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

Quoted 11 lines

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

Quoted 3 lines

I tend to agree. Tuning based on the recent allocation pattern without frees would make more sense and also be symmetric with how free_factor works.

This sounds good to me. I will give it a try to tune PCP batch. Have some questions about how to tune PCP high based on that. Details are in the following.

Quoted 2 lines

I suspect that time-based may be heavily orientated around the will-it-scale benchmark.

I totally agree that will-it-scale isn't a real workload. So, I tried to find some more practical ones. I found that a repetitive allocation/freeing several hundreds MB pages pattern exists in kernel building and netperf/SCTP_STREAM_MANY workloads. More details can be found in my reply to Michal as follows,

https://lore.kernel.org/linux-mm/[email protected]/

Quoted 11 lines

While I only glanced at this, a few things jumped out

1. Time-based heuristics are not ideal. congestion_wait() and friends was an obvious case where time-based heuristics fell apart even before the event it waited on was removed. For congestion, it happened to work for slow storage for a while but that was about it. For allocation stream detection, it has a similar problem. If a process is allocating heavily, then fine, if it's in bursts of less than a second more than one second apart then it will not adapt. While I do not think it is explicitly mentioned anywhere, my understanding was that heuristics like this within mm/ should be driven by explicit events as much as possible and not time.

The proposed heuristics can be changed to be not time-based. When it is found that the allocation/freeing depth is larger than previous value, the PCP high can be increased immediately. We use time-based implementation to try to reduce the overhead. And, we mainly targeted long-time allocation pattern before.

Quoted 5 lines

2. If time was to be used, it would be cheaper to have the simpliest possible state tracking in the fast paths and decay any resizing of the PCP within the vmstat updates (reuse pcp->expire except it applies to local pcps). Even this is less than ideal as the PCP may be too large for short periods of time but it may also act as a backstop for worst-case behaviour

This sounds reasonable. Thanks! We will try this if we choose to use time-based implementation.

Quoted 5 lines

3. free_factor is an existing mechanism for detecting recent patterns and adapting the PCP sizes. The allocation side should be symmetric and the events that should drive it are "refills" on the alloc side and "drains" on the free side. Initially it might be easier to have a single parameter that scales batch and high up to a limit

For example, when a workload is started, several GB pages will be allocated. We will observe many "refills", and almost no "drains". So, we will scales batch and high up to a limit. When the workload exits, large number of pages of the workload will be put in PCP because the PCP high is increased. When should we decrease the PCP batch and high?

Quoted 7 lines

4. The amount of state tracked seems excessive and increases the size of the per-cpu structure by more than 1 cache line. That in itself may not be a problem but the state is tracked on every page alloc/free that goes through the fast path and it's relatively complex to track. That is a constant penalty in fast paths that may not may not be relevant to the workload and only sustained bursty allocation streams may offset the cost.

Yes. Thanks for pointing this out. We will optimize this if the other aspects of the basic idea could be accepted.

Quoted 4 lines

5. Memory pressure and reclaim activity does not appear to be accounted for and it's not clear if pcp->high is bounded or if it's possible for a single PCP to hide a large number of pages from other CPUs sharing the same node. The max size of the PCP should probably be explicitly clamped.

As in my reply to Michal's email, previously I thought ZONE_RECLAIM_ACTIVE will be set for kswap reclaiming, and PCP high will be decreased to (batch * 4) effectively. Or I miss something?

There's a upper limit for PCP high, which comes from MIN_PERCPU_PAGELIST_HIGH_FRACTION. That is, we calculate the PCP high as if percpu_pagelist_high_fraction is set to MIN_PERCPU_PAGELIST_HIGH_FRACTION (8). Then, we use that value as the upper limit for PCP high.

Best Regards,
Huang, Ying

#10 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 57 lines · Ying
Quoted 24 lines · Mel Gorman
Quoted 11 lines · Michal Hocko
Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

Quoted 11 lines

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

Quoted 3 lines

I tend to agree. Tuning based on the recent allocation pattern without frees would make more sense and also be symmetric with how free_factor works.

This sounds good to me. I will give it a try to tune PCP batch. Have some questions about how to tune PCP high based on that. Details are in the following.

Quoted 2 lines

I suspect that time-based may be heavily orientated around the will-it-scale benchmark.

I totally agree that will-it-scale isn't a real workload. So, I tried to find some more practical ones. I found that a repetitive allocation/freeing several hundreds MB pages pattern exists in kernel building and netperf/SCTP_STREAM_MANY workloads. More details can be found in my reply to Michal as follows,

https://lore.kernel.org/linux-mm/[email protected]/

Quoted 11 lines

While I only glanced at this, a few things jumped out

1. Time-based heuristics are not ideal. congestion_wait() and friends was an obvious case where time-based heuristics fell apart even before the event it waited on was removed. For congestion, it happened to work for slow storage for a while but that was about it. For allocation stream detection, it has a similar problem. If a process is allocating heavily, then fine, if it's in bursts of less than a second more than one second apart then it will not adapt. While I do not think it is explicitly mentioned anywhere, my understanding was that heuristics like this within mm/ should be driven by explicit events as much as possible and not time.

Quoted 6 lines

The proposed heuristics can be changed to be not time-based. When it is found that the allocation/freeing depth is larger than previous value, the PCP high can be increased immediately. We use time-based implementation to try to reduce the overhead. And, we mainly targeted long-time allocation pattern before.

Time simply has too many corner cases. When it's reset for example, all state is lost so patterns that are longer than the time window are unpredictable. It tends to work slightly better than time decays state rather than resets but it gets very hand-wavey.

Quoted 15 lines
Quoted 5 lines

2. If time was to be used, it would be cheaper to have the simpliest possible state tracking in the fast paths and decay any resizing of the PCP within the vmstat updates (reuse pcp->expire except it applies to local pcps). Even this is less than ideal as the PCP may be too large for short periods of time but it may also act as a backstop for worst-case behaviour

This sounds reasonable. Thanks! We will try this if we choose to use time-based implementation.

Quoted 5 lines

3. free_factor is an existing mechanism for detecting recent patterns and adapting the PCP sizes. The allocation side should be symmetric and the events that should drive it are "refills" on the alloc side and "drains" on the free side. Initially it might be easier to have a single parameter that scales batch and high up to a limit

Quoted 6 lines

For example, when a workload is started, several GB pages will be allocated. We will observe many "refills", and almost no "drains". So, we will scales batch and high up to a limit. When the workload exits, large number of pages of the workload will be put in PCP because the PCP high is increased. When should we decrease the PCP batch and high?

Honestly, I'm not 100% certain as I haven't spent time with paper to sketch out the different combinations with "all allocs" at one end, "all frees" at the other and "ratio of alloc:free" in the middle. Intuitively I would expect the time to shrink is when there is a mix.

All allocs -- maximise batch and high
All frees -- maximise batch and high
Mix -- adjust high to appoximate the minimum value of high such
that a drain/refill does not occur or rarely occurs

Batch should have a much lower maximum than high because it's a deferred cost that gets assigned to an arbitrary task. The worst case is where a process that is a light user of the allocator incurs the full cost of a refill/drain.

Again, intuitively this may be PID Control problem for the "Mix" case to estimate the size of high required to minimise drains/allocs as each drain/alloc is potentially a lock contention. The catchall for corner cases would be to decay high from vmstat context based on pcp->expires. The decay would prevent the "high" being pinned at an artifically high value without any zone lock contention for prolonged periods of time and also mitigate worst-case due to state being per-cpu. The downside is that "high" would also oscillate for a continuous steady allocation pattern as the PID control might pick an ideal value suitable for a long period of time with the "decay" disrupting that ideal value.

Quoted 11 lines
Quoted 7 lines

4. The amount of state tracked seems excessive and increases the size of the per-cpu structure by more than 1 cache line. That in itself may not be a problem but the state is tracked on every page alloc/free that goes through the fast path and it's relatively complex to track. That is a constant penalty in fast paths that may not may not be relevant to the workload and only sustained bursty allocation streams may offset the cost.

Yes. Thanks for pointing this out. We will optimize this if the other aspects of the basic idea could be accepted.

I'm not opposed to having an adaptive pcp->high in concept. I think it would be best to disable adaptive tuning if percpu_pagelist_high_fraction is set though. I expect that users of that tunable are rare and that if it *is* used that there is a very good reason for it.

Quoted 9 lines
Quoted 4 lines

5. Memory pressure and reclaim activity does not appear to be accounted for and it's not clear if pcp->high is bounded or if it's possible for a single PCP to hide a large number of pages from other CPUs sharing the same node. The max size of the PCP should probably be explicitly clamped.

As in my reply to Michal's email, previously I thought ZONE_RECLAIM_ACTIVE will be set for kswap reclaiming, and PCP high will be decreased to (batch * 4) effectively. Or I miss something?

I don't think you did, but it deserves a big comment in the tuning at minimum and potentially even disabling adaptive tuning entirely if reclaim is active.

Signature · 2 lines
Mel Gorman
SUSE Labs
#11 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 106 lines · Mel Gorman
Quoted 57 lines · Ying
Quoted 24 lines · Mel Gorman
Quoted 11 lines · Michal Hocko
Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

Quoted 11 lines

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

Quoted 3 lines

I tend to agree. Tuning based on the recent allocation pattern without frees would make more sense and also be symmetric with how free_factor works.

This sounds good to me. I will give it a try to tune PCP batch. Have some questions about how to tune PCP high based on that. Details are in the following.

Quoted 2 lines

I suspect that time-based may be heavily orientated around the will-it-scale benchmark.

I totally agree that will-it-scale isn't a real workload. So, I tried to find some more practical ones. I found that a repetitive allocation/freeing several hundreds MB pages pattern exists in kernel building and netperf/SCTP_STREAM_MANY workloads. More details can be found in my reply to Michal as follows,

https://lore.kernel.org/linux-mm/[email protected]/

Quoted 11 lines

While I only glanced at this, a few things jumped out

1. Time-based heuristics are not ideal. congestion_wait() and friends was an obvious case where time-based heuristics fell apart even before the event it waited on was removed. For congestion, it happened to work for slow storage for a while but that was about it. For allocation stream detection, it has a similar problem. If a process is allocating heavily, then fine, if it's in bursts of less than a second more than one second apart then it will not adapt. While I do not think it is explicitly mentioned anywhere, my understanding was that heuristics like this within mm/ should be driven by explicit events as much as possible and not time.

Quoted 6 lines

The proposed heuristics can be changed to be not time-based. When it is found that the allocation/freeing depth is larger than previous value, the PCP high can be increased immediately. We use time-based implementation to try to reduce the overhead. And, we mainly targeted long-time allocation pattern before.

Time simply has too many corner cases. When it's reset for example, all state is lost so patterns that are longer than the time window are unpredictable. It tends to work slightly better than time decays state rather than resets but it gets very hand-wavey.

Quoted 15 lines
Quoted 5 lines

2. If time was to be used, it would be cheaper to have the simpliest possible state tracking in the fast paths and decay any resizing of the PCP within the vmstat updates (reuse pcp->expire except it applies to local pcps). Even this is less than ideal as the PCP may be too large for short periods of time but it may also act as a backstop for worst-case behaviour

This sounds reasonable. Thanks! We will try this if we choose to use time-based implementation.

Quoted 5 lines

3. free_factor is an existing mechanism for detecting recent patterns and adapting the PCP sizes. The allocation side should be symmetric and the events that should drive it are "refills" on the alloc side and "drains" on the free side. Initially it might be easier to have a single parameter that scales batch and high up to a limit

Quoted 6 lines

For example, when a workload is started, several GB pages will be allocated. We will observe many "refills", and almost no "drains". So, we will scales batch and high up to a limit. When the workload exits, large number of pages of the workload will be put in PCP because the PCP high is increased. When should we decrease the PCP batch and high?

Honestly, I'm not 100% certain as I haven't spent time with paper to sketch out the different combinations with "all allocs" at one end, "all frees" at the other and "ratio of alloc:free" in the middle. Intuitively I would expect the time to shrink is when there is a mix.

All allocs -- maximise batch and high
All frees -- maximise batch and high
Mix -- adjust high to appoximate the minimum value of high such
that a drain/refill does not occur or rarely occurs

Batch should have a much lower maximum than high because it's a deferred cost that gets assigned to an arbitrary task. The worst case is where a process that is a light user of the allocator incurs the full cost of a refill/drain.

Quoted 10 lines

Again, intuitively this may be PID Control problem for the "Mix" case to estimate the size of high required to minimise drains/allocs as each drain/alloc is potentially a lock contention. The catchall for corner cases would be to decay high from vmstat context based on pcp->expires. The decay would prevent the "high" being pinned at an artifically high value without any zone lock contention for prolonged periods of time and also mitigate worst-case due to state being per-cpu. The downside is that "high" would also oscillate for a continuous steady allocation pattern as the PID control might pick an ideal value suitable for a long period of time with the "decay" disrupting that ideal value.

Maybe we can track the minimal value of pcp->count. If it's small enough recently, we can avoid to decay pcp->high. Because the pages in PCP are used for allocations instead of idle.

Another question is as follows.

For example, on CPU A, a large number of pages are freed, and we maximize batch and high. So, a large number of pages are put in PCP. Then, the possible situations may be,

a) a large number of pages are allocated on CPU A after some time
b) a large number of pages are allocated on another CPU B

For a), we want the pages are kept in PCP of CPU A as long as possible. For b), we want the pages are kept in PCP of CPU A as short as possible. I think that we need to balance between them. What is the reasonable time to keep pages in PCP without many allocations?

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

4. The amount of state tracked seems excessive and increases the size of the per-cpu structure by more than 1 cache line. That in itself may not be a problem but the state is tracked on every page alloc/free that goes through the fast path and it's relatively complex to track. That is a constant penalty in fast paths that may not may not be relevant to the workload and only sustained bursty allocation streams may offset the cost.

Quoted 8 lines
Quoted 3 lines

Yes. Thanks for pointing this out. We will optimize this if the other aspects of the basic idea could be accepted.

I'm not opposed to having an adaptive pcp->high in concept. I think it would be best to disable adaptive tuning if percpu_pagelist_high_fraction is set though. I expect that users of that tunable are rare and that if it *is* used that there is a very good reason for it.

OK. Will do that in the future version.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

5. Memory pressure and reclaim activity does not appear to be accounted for and it's not clear if pcp->high is bounded or if it's possible for a single PCP to hide a large number of pages from other CPUs sharing the same node. The max size of the PCP should probably be explicitly clamped.

Quoted 7 lines
Quoted 4 lines

As in my reply to Michal's email, previously I thought ZONE_RECLAIM_ACTIVE will be set for kswap reclaiming, and PCP high will be decreased to (batch * 4) effectively. Or I miss something?

I don't think you did, but it deserves a big comment in the tuning at minimum and potentially even disabling adaptive tuning entirely if reclaim is active.

Sure. Will do that.

Best Regards,
Huang, Ying

#12 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 17 lines · Ying
Quoted 4 lines · Mel Gorman

Batch should have a much lower maximum than high because it's a deferred cost that gets assigned to an arbitrary task. The worst case is where a process that is a light user of the allocator incurs the full cost of a refill/drain.

Quoted 10 lines

Again, intuitively this may be PID Control problem for the "Mix" case to estimate the size of high required to minimise drains/allocs as each drain/alloc is potentially a lock contention. The catchall for corner cases would be to decay high from vmstat context based on pcp->expires. The decay would prevent the "high" being pinned at an artifically high value without any zone lock contention for prolonged periods of time and also mitigate worst-case due to state being per-cpu. The downside is that "high" would also oscillate for a continuous steady allocation pattern as the PID control might pick an ideal value suitable for a long period of time with the "decay" disrupting that ideal value.

Quoted 3 lines

Maybe we can track the minimal value of pcp->count. If it's small enough recently, we can avoid to decay pcp->high. Because the pages in PCP are used for allocations instead of idle.

Implement as a separate patch. I suspect this type of heuristic will be very benchmark specific and the complexity may not be worth it in the general case.

Quoted 7 lines

Another question is as follows.

For example, on CPU A, a large number of pages are freed, and we maximize batch and high. So, a large number of pages are put in PCP. Then, the possible situations may be,

Quoted 8 lines

a) a large number of pages are allocated on CPU A after some time
b) a large number of pages are allocated on another CPU B

For a), we want the pages are kept in PCP of CPU A as long as possible. For b), we want the pages are kept in PCP of CPU A as short as possible. I think that we need to balance between them. What is the reasonable time to keep pages in PCP without many allocations?

This would be a case where you're relying on vmstat to drain the PCP after a period of time as it is a corner case. You cannot reasonably detect the pattern on two separate per-cpu lists without either inspecting remote CPU state or maintaining global state. Either would incur cache miss penalties that probably cost more than the heuristic saves.

Signature · 2 lines
Mel Gorman
SUSE Labs
#13 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 18 lines · Mel Gorman
Quoted 17 lines · Ying
Quoted 14 lines · Mel Gorman

Batch should have a much lower maximum than high because it's a deferred cost that gets assigned to an arbitrary task. The worst case is where a process that is a light user of the allocator incurs the full cost of a refill/drain.

Again, intuitively this may be PID Control problem for the "Mix" case to estimate the size of high required to minimise drains/allocs as each drain/alloc is potentially a lock contention. The catchall for corner cases would be to decay high from vmstat context based on pcp->expires. The decay would prevent the "high" being pinned at an artifically high value without any zone lock contention for prolonged periods of time and also mitigate worst-case due to state being per-cpu. The downside is that "high" would also oscillate for a continuous steady allocation pattern as the PID control might pick an ideal value suitable for a long period of time with the "decay" disrupting that ideal value.

Quoted 7 lines
Quoted 3 lines

Maybe we can track the minimal value of pcp->count. If it's small enough recently, we can avoid to decay pcp->high. Because the pages in PCP are used for allocations instead of idle.

Implement as a separate patch. I suspect this type of heuristic will be very benchmark specific and the complexity may not be worth it in the general case.

OK.

Quoted 9 lines
Quoted 9 lines

Another question is as follows.

For example, on CPU A, a large number of pages are freed, and we maximize batch and high. So, a large number of pages are put in PCP. Then, the possible situations may be,

a) a large number of pages are allocated on CPU A after some time
b) a large number of pages are allocated on another CPU B

Quoted 8 lines
Quoted 5 lines

For a), we want the pages are kept in PCP of CPU A as long as possible. For b), we want the pages are kept in PCP of CPU A as short as possible. I think that we need to balance between them. What is the reasonable time to keep pages in PCP without many allocations?

This would be a case where you're relying on vmstat to drain the PCP after a period of time as it is a corner case.

Yes. The remaining question is how long should "a period of time" be? If it's long, the pages in PCP can be used for allocation after some time. If it's short the pages can be put in buddy, so can be used by other workloads if needed.

Anyway, I will do some experiment for that.

Quoted 4 lines

You cannot reasonably detect the pattern on two separate per-cpu lists without either inspecting remote CPU state or maintaining global state. Either would incur cache miss penalties that probably cost more than the heuristic saves.

Yes. Totally agree.

Best Regards,
Huang, Ying

#14 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 45 lines · Ying
Quoted 18 lines · Mel Gorman
Quoted 17 lines · Ying
Quoted 14 lines · Mel Gorman

Batch should have a much lower maximum than high because it's a deferred cost that gets assigned to an arbitrary task. The worst case is where a process that is a light user of the allocator incurs the full cost of a refill/drain.

Again, intuitively this may be PID Control problem for the "Mix" case to estimate the size of high required to minimise drains/allocs as each drain/alloc is potentially a lock contention. The catchall for corner cases would be to decay high from vmstat context based on pcp->expires. The decay would prevent the "high" being pinned at an artifically high value without any zone lock contention for prolonged periods of time and also mitigate worst-case due to state being per-cpu. The downside is that "high" would also oscillate for a continuous steady allocation pattern as the PID control might pick an ideal value suitable for a long period of time with the "decay" disrupting that ideal value.

Quoted 7 lines
Quoted 3 lines

Maybe we can track the minimal value of pcp->count. If it's small enough recently, we can avoid to decay pcp->high. Because the pages in PCP are used for allocations instead of idle.

Implement as a separate patch. I suspect this type of heuristic will be very benchmark specific and the complexity may not be worth it in the general case.

OK.

Quoted 15 lines
Quoted 14 lines

Another question is as follows.

For example, on CPU A, a large number of pages are freed, and we maximize batch and high. So, a large number of pages are put in PCP. Then, the possible situations may be,

a) a large number of pages are allocated on CPU A after some time
b) a large number of pages are allocated on another CPU B

For a), we want the pages are kept in PCP of CPU A as long as possible. For b), we want the pages are kept in PCP of CPU A as short as possible. I think that we need to balance between them. What is the reasonable time to keep pages in PCP without many allocations?

Quoted 4 lines
Quoted 2 lines

This would be a case where you're relying on vmstat to drain the PCP after a period of time as it is a corner case.

Yes. The remaining question is how long should "a period of time" be?

Match the time used for draining "remote" pages from the PCP lists. The choice is arbitrary and no matter what value is chosen, it'll be possible to build an adverse workload.

Quoted 4 lines

If it's long, the pages in PCP can be used for allocation after some time. If it's short the pages can be put in buddy, so can be used by other workloads if needed.

Assume that the main reason to expire pages and put them back on the buddy list is to avoid premature allocation failures due to pages pinned on the PCP. Once pages are going back onto the buddy list and the expiry is hit, it might as well be assumed that the pages are cache-cold. Some bad corner cases should be mitigated by disabling the adapative sizing when reclaim is active. The big remaaining corner case to watch out for is where the sum of the boosted pcp->high exceeds the low watermark. If that should ever happen then potentially a premature OOM happens because the watermarks are fine so no reclaim is active but no pages are available. It may even be the case that the sum of pcp->high should not exceed *min* as that corner case means that processes may prematurely enter direct reclaim (not as bad as OOM but still bad).

Quoted 11 lines

Anyway, I will do some experiment for that.

Quoted 4 lines

You cannot reasonably detect the pattern on two separate per-cpu lists without either inspecting remote CPU state or maintaining global state. Either would incur cache miss penalties that probably cost more than the heuristic saves.

Yes. Totally agree.

Best Regards,
Huang, Ying

Signature · 2 lines
Mel Gorman
SUSE Labs
#15 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 46 lines · Mel Gorman
Quoted 45 lines · Ying
Quoted 18 lines · Mel Gorman
Quoted 17 lines · Ying
Quoted 14 lines · Mel Gorman

Batch should have a much lower maximum than high because it's a deferred cost that gets assigned to an arbitrary task. The worst case is where a process that is a light user of the allocator incurs the full cost of a refill/drain.

Again, intuitively this may be PID Control problem for the "Mix" case to estimate the size of high required to minimise drains/allocs as each drain/alloc is potentially a lock contention. The catchall for corner cases would be to decay high from vmstat context based on pcp->expires. The decay would prevent the "high" being pinned at an artifically high value without any zone lock contention for prolonged periods of time and also mitigate worst-case due to state being per-cpu. The downside is that "high" would also oscillate for a continuous steady allocation pattern as the PID control might pick an ideal value suitable for a long period of time with the "decay" disrupting that ideal value.

Quoted 7 lines
Quoted 3 lines

Maybe we can track the minimal value of pcp->count. If it's small enough recently, we can avoid to decay pcp->high. Because the pages in PCP are used for allocations instead of idle.

Implement as a separate patch. I suspect this type of heuristic will be very benchmark specific and the complexity may not be worth it in the general case.

OK.

Quoted 15 lines
Quoted 14 lines

Another question is as follows.

For example, on CPU A, a large number of pages are freed, and we maximize batch and high. So, a large number of pages are put in PCP. Then, the possible situations may be,

a) a large number of pages are allocated on CPU A after some time
b) a large number of pages are allocated on another CPU B

For a), we want the pages are kept in PCP of CPU A as long as possible. For b), we want the pages are kept in PCP of CPU A as short as possible. I think that we need to balance between them. What is the reasonable time to keep pages in PCP without many allocations?

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

This would be a case where you're relying on vmstat to drain the PCP after a period of time as it is a corner case.

Yes. The remaining question is how long should "a period of time" be?

Match the time used for draining "remote" pages from the PCP lists. The choice is arbitrary and no matter what value is chosen, it'll be possible to build an adverse workload.

OK.

Quoted 11 lines
Quoted 4 lines

If it's long, the pages in PCP can be used for allocation after some time. If it's short the pages can be put in buddy, so can be used by other workloads if needed.

Assume that the main reason to expire pages and put them back on the buddy list is to avoid premature allocation failures due to pages pinned on the PCP. Once pages are going back onto the buddy list and the expiry is hit, it might as well be assumed that the pages are cache-cold. Some bad corner cases should be mitigated by disabling the adapative sizing when reclaim is active.

Yes. This can be mitigated, but the page allocation performance may be hurt.

Quoted 7 lines

The big remaaining corner case to watch out for is where the sum of the boosted pcp->high exceeds the low watermark. If that should ever happen then potentially a premature OOM happens because the watermarks are fine so no reclaim is active but no pages are available. It may even be the case that the sum of pcp->high should not exceed *min* as that corner case means that processes may prematurely enter direct reclaim (not as bad as OOM but still bad).

Sorry, I don't understand this. When pages are moved from buddy to PCP, zone NR_FREE_PAGES will be decreased in rmqueue_bulk(). That is, pages in PCP will be counted as used instead of free. And, in zone_watermark_ok*() and zone_watermark_fast(), zone NR_FREE_PAGES is used to check watermark. So, if my understanding were correct, if the number of pages in PCP is larger than low/min watermark, we can still trigger reclaim. Whether is my understanding correct?

Quoted 8 lines
Quoted 8 lines

Anyway, I will do some experiment for that.

Quoted 4 lines

You cannot reasonably detect the pattern on two separate per-cpu lists without either inspecting remote CPU state or maintaining global state. Either would incur cache miss penalties that probably cost more than the heuristic saves.

Yes. Totally agree.

Best Regards,
Huang, Ying

#16 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 8 lines · Ying
Quoted 7 lines

The big remaaining corner case to watch out for is where the sum of the boosted pcp->high exceeds the low watermark. If that should ever happen then potentially a premature OOM happens because the watermarks are fine so no reclaim is active but no pages are available. It may even be the case that the sum of pcp->high should not exceed *min* as that corner case means that processes may prematurely enter direct reclaim (not as bad as OOM but still bad).

Quoted 8 lines

Sorry, I don't understand this. When pages are moved from buddy to PCP, zone NR_FREE_PAGES will be decreased in rmqueue_bulk(). That is, pages in PCP will be counted as used instead of free. And, in zone_watermark_ok*() and zone_watermark_fast(), zone NR_FREE_PAGES is used to check watermark. So, if my understanding were correct, if the number of pages in PCP is larger than low/min watermark, we can still trigger reclaim. Whether is my understanding correct?

You're right, I didn't check the timing of the accounting and all that occurred to me was "the timing of when watermarks trigger kswapd or direct reclaim may change as a result of PCP adaptive resizing". Even though I got the timing wrong, the shape of the problem just changes. I suspect that excessively large PCP high relative to the watermarks may mean that reclaim happens prematurely if too many pages are pinned by PCP pages as the zone free pages approaches the watermark. While disabling the adaptive resizing during reclaim will limit the worst of the problem, it may still be the case that kswapd is woken early simply because there are enough CPUs pinning pages in PCP lists. Similarly, depending on the size of pcp->high and the gap between the watermarks, it's possible for direct reclaim to happen prematurely. I could still be wrong because I'm not thinking the problem through fully, examining the code or thinking about the implementation. It's simply worth keeping in mind the impact elevated PCP high values has on the timing of watermarks failing. If it's complex enough, it may be necessary to have a separate patch dealing with the impact of elevated pcp->high on watermarks.

Signature · 2 lines
Mel Gorman
SUSE Labs
#17 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 18 lines · Mel Gorman
Quoted 8 lines · Ying
Quoted 7 lines

The big remaaining corner case to watch out for is where the sum of the boosted pcp->high exceeds the low watermark. If that should ever happen then potentially a premature OOM happens because the watermarks are fine so no reclaim is active but no pages are available. It may even be the case that the sum of pcp->high should not exceed *min* as that corner case means that processes may prematurely enter direct reclaim (not as bad as OOM but still bad).

Quoted 8 lines

Sorry, I don't understand this. When pages are moved from buddy to PCP, zone NR_FREE_PAGES will be decreased in rmqueue_bulk(). That is, pages in PCP will be counted as used instead of free. And, in zone_watermark_ok*() and zone_watermark_fast(), zone NR_FREE_PAGES is used to check watermark. So, if my understanding were correct, if the number of pages in PCP is larger than low/min watermark, we can still trigger reclaim. Whether is my understanding correct?

Quoted 7 lines

You're right, I didn't check the timing of the accounting and all that occurred to me was "the timing of when watermarks trigger kswapd or direct reclaim may change as a result of PCP adaptive resizing". Even though I got the timing wrong, the shape of the problem just changes. I suspect that excessively large PCP high relative to the watermarks may mean that reclaim happens prematurely if too many pages are pinned by PCP pages as the zone free pages approaches the watermark.

Yes. I think so too. In addition to reclaim, falling back to remote NUMA node may happen prematurely too.

Quoted 11 lines

While disabling the adaptive resizing during reclaim will limit the worst of the problem, it may still be the case that kswapd is woken early simply because there are enough CPUs pinning pages in PCP lists. Similarly, depending on the size of pcp->high and the gap between the watermarks, it's possible for direct reclaim to happen prematurely. I could still be wrong because I'm not thinking the problem through fully, examining the code or thinking about the implementation. It's simply worth keeping in mind the impact elevated PCP high values has on the timing of watermarks failing. If it's complex enough, it may be necessary to have a separate patch dealing with the impact of elevated pcp->high on watermarks.

Sure. I will keep this in mind. We may need to check zone watermark when tuning pcp->high and free some pages from PCP before falling back to other node or reclaiming.

--
Best Regards,
Huang, Ying

#18 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 28 lines · Ying
Quoted 18 lines · Mel Gorman
Quoted 8 lines · Ying
Quoted 7 lines

The big remaaining corner case to watch out for is where the sum of the boosted pcp->high exceeds the low watermark. If that should ever happen then potentially a premature OOM happens because the watermarks are fine so no reclaim is active but no pages are available. It may even be the case that the sum of pcp->high should not exceed *min* as that corner case means that processes may prematurely enter direct reclaim (not as bad as OOM but still bad).

Quoted 8 lines

Sorry, I don't understand this. When pages are moved from buddy to PCP, zone NR_FREE_PAGES will be decreased in rmqueue_bulk(). That is, pages in PCP will be counted as used instead of free. And, in zone_watermark_ok*() and zone_watermark_fast(), zone NR_FREE_PAGES is used to check watermark. So, if my understanding were correct, if the number of pages in PCP is larger than low/min watermark, we can still trigger reclaim. Whether is my understanding correct?

Quoted 7 lines

You're right, I didn't check the timing of the accounting and all that occurred to me was "the timing of when watermarks trigger kswapd or direct reclaim may change as a result of PCP adaptive resizing". Even though I got the timing wrong, the shape of the problem just changes. I suspect that excessively large PCP high relative to the watermarks may mean that reclaim happens prematurely if too many pages are pinned by PCP pages as the zone free pages approaches the watermark.

Quoted 3 lines

Yes. I think so too. In addition to reclaim, falling back to remote NUMA node may happen prematurely too.

Yes, with the added bonus that this is relatively easy to detect from the NUMA miss stats. I say "relative" because in a lot of cases, it'll be difficult to distinguish from the noise. Hence, it's better to be explicit in the change log that the potential problem is known and has been considered. That way, if bisect points the finger at adaptive resizing, there will be some notes on how to investigate the bug.

Quoted 12 lines
Quoted 11 lines

While disabling the adaptive resizing during reclaim will limit the worst of the problem, it may still be the case that kswapd is woken early simply because there are enough CPUs pinning pages in PCP lists. Similarly, depending on the size of pcp->high and the gap between the watermarks, it's possible for direct reclaim to happen prematurely. I could still be wrong because I'm not thinking the problem through fully, examining the code or thinking about the implementation. It's simply worth keeping in mind the impact elevated PCP high values has on the timing of watermarks failing. If it's complex enough, it may be necessary to have a separate patch dealing with the impact of elevated pcp->high on watermarks.

Quoted 4 lines

Sure. I will keep this in mind. We may need to check zone watermark when tuning pcp->high and free some pages from PCP before falling back to other node or reclaiming.

That would certainly be one option, a cap on adaptive resizing as memory gets lower. It's not perfect but ideally the worst-case behaviour would be that PCP adaptive sizing returns to existing behaviour when memory usage is persistently high and near watermarks within a zone.

Signature · 2 lines
Mel Gorman
SUSE Labs
#19 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

Quoted 33 lines · Mel Gorman
Quoted 28 lines · Ying
Quoted 18 lines · Mel Gorman
Quoted 8 lines · Ying
Quoted 7 lines

The big remaaining corner case to watch out for is where the sum of the boosted pcp->high exceeds the low watermark. If that should ever happen then potentially a premature OOM happens because the watermarks are fine so no reclaim is active but no pages are available. It may even be the case that the sum of pcp->high should not exceed *min* as that corner case means that processes may prematurely enter direct reclaim (not as bad as OOM but still bad).

Quoted 8 lines

Sorry, I don't understand this. When pages are moved from buddy to PCP, zone NR_FREE_PAGES will be decreased in rmqueue_bulk(). That is, pages in PCP will be counted as used instead of free. And, in zone_watermark_ok*() and zone_watermark_fast(), zone NR_FREE_PAGES is used to check watermark. So, if my understanding were correct, if the number of pages in PCP is larger than low/min watermark, we can still trigger reclaim. Whether is my understanding correct?

Quoted 7 lines

You're right, I didn't check the timing of the accounting and all that occurred to me was "the timing of when watermarks trigger kswapd or direct reclaim may change as a result of PCP adaptive resizing". Even though I got the timing wrong, the shape of the problem just changes. I suspect that excessively large PCP high relative to the watermarks may mean that reclaim happens prematurely if too many pages are pinned by PCP pages as the zone free pages approaches the watermark.

Quoted 3 lines

Yes. I think so too. In addition to reclaim, falling back to remote NUMA node may happen prematurely too.

Quoted 6 lines

Yes, with the added bonus that this is relatively easy to detect from the NUMA miss stats. I say "relative" because in a lot of cases, it'll be difficult to distinguish from the noise. Hence, it's better to be explicit in the change log that the potential problem is known and has been considered. That way, if bisect points the finger at adaptive resizing, there will be some notes on how to investigate the bug.

Sure. Will do that.

Quoted 17 lines
Quoted 12 lines
Quoted 11 lines

While disabling the adaptive resizing during reclaim will limit the worst of the problem, it may still be the case that kswapd is woken early simply because there are enough CPUs pinning pages in PCP lists. Similarly, depending on the size of pcp->high and the gap between the watermarks, it's possible for direct reclaim to happen prematurely. I could still be wrong because I'm not thinking the problem through fully, examining the code or thinking about the implementation. It's simply worth keeping in mind the impact elevated PCP high values has on the timing of watermarks failing. If it's complex enough, it may be necessary to have a separate patch dealing with the impact of elevated pcp->high on watermarks.

Quoted 4 lines

Sure. I will keep this in mind. We may need to check zone watermark when tuning pcp->high and free some pages from PCP before falling back to other node or reclaiming.

Quoted 4 lines

That would certainly be one option, a cap on adaptive resizing as memory gets lower. It's not perfect but ideally the worst-case behaviour would be that PCP adaptive sizing returns to existing behaviour when memory usage is persistently high and near watermarks within a zone.

--
Best Regards,
Huang, Ying

reviewer
#20 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

In reply to Mel Gorman
Quoted 29 lines · Mel Gorman
Quoted 11 lines · Michal Hocko
Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

Quoted 11 lines

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

I tend to agree. Tuning based on the recent allocation pattern without frees would make more sense and also be symmetric with how free_factor works. I suspect that time-based may be heavily orientated around the will-it-scale benchmark. While I only glanced at this, a few things jumped out

Quoted 9 lines

1. Time-based heuristics are not ideal. congestion_wait() and friends was an obvious case where time-based heuristics fell apart even before the event it waited on was removed. For congestion, it happened to work for slow storage for a while but that was about it. For allocation stream detection, it has a similar problem. If a process is allocating heavily, then fine, if it's in bursts of less than a second more than one second apart then it will not adapt. While I do not think it is explicitly mentioned anywhere, my understanding was that heuristics like this within mm/ should be driven by explicit events as much as possible and not time.

Agreed. I would also like to point out that it is also important to realize those events that we should care about. Remember the primary motivation of the tuning is to reduce the lock contention. That being said, it is less of a problem to have stream or bursty demand for memory if that doesn't really cause the said contention, right? So any auto-tuning should consider that as well and do not inflate the batch in an absense of the contention. That of course means that a solely deallocation based monitoring.

Signature · 2 lines
Michal Hocko
SUSE Labs
#21 lore raw

Re: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning

In reply to Michal Hocko
Quoted 11 lines · Michal Hocko
Quoted 9 lines · Huang Ying

To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch.

The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching.

Quoted 10 lines

1s is an ethernity from the allocation POV. Is a time based sampling really a good choice? I would have expected a natural allocation/freeing feedback mechanism. I.e. double the batch size when the batch is consumed and it requires to be refilled and shrink it under memory pressure (GFP_NOWAIT allocation fails) or when the surplus grows too high over batch (e.g. twice as much). Have you considered something as simple as that?
Quite honestly I am not sure time based approach is a good choice because memory consumptions tends to be quite bulky (e.g. application starts or workload transitions based on requests).

If my understanding were correct, we are targeting different allocation and freeing patterns.

Your target pattern (A):

- allocate several GB or more (application starts) in short time
- allocate and free small number of pages in long time
- free several GB or more (application exits) in short time

My target pattern (B):

- allocate several hundreds MB, free small number, in short time
- free several hundreds MB, allocate small number, in short time
- repeat the above pattern for long time

For pattern A, my patchset will almost not help it. It may be helpful to increase PCP batch (I will do some test to verify that). But, it may be not a good idea to increase PCP high too much? Because we need to put more than several GB in PCP for long time. At the same time, these memory may be needed by other CPUs. It's hard to determine how long should we keep these pages in PCP.

My patchset will help pattern B via identify the allocation/freeing depth and increasing PCP high to hold several hundreds MB in PCP. Then most allocation and freeing needs not to call buddy. And when the pattern completes, the PCP high will be restored and pages will be put back in buddy.

I can observe the pattern B in kernel building and netperf/SCTP_STREAM_MANY workload. So, the zone lock contention or page allocation/freeing latency reduction can be observed. For example, the alloc_count for kbuild is as follows,

        +-------------------------------------------------------------------+   
  90000 |-+    +      +     +      +      +      +      +     +      +     E|   
        |                        mm_pcp_alloc_count.0.2.alloc_count ***E****|   
  85000 |-+                                                **E           * *|   
        |              EE           E             EE    E**  *          E  *|   
  80000 |-+            EE          **            * *  **     *         *   *|   
        |         E    EEEE       E *    E      E  * *       *     E   *   *|   
  75000 |-+      E*    EE *       E *   ** E    *  EE        *    E*  E    *|   
        |       E * E *   *      *  *  * * *   E   E         *   * * E*    *|   
  70000 |-+    EE E EE    *E     E  * E  *E*   E             *   * * E     E|   
        |      E  E EE    *EE   E   *E   E*EEEE              E EE  *EE      |   
  65000 |EEE**EE  EEE     EE*  EE   EE   E EEE               EEEE  EE       |   
        |                 E * E             EE               EEE   E        |   
  60000 |-+                 *E*                                             |   
        |                   EE                                              |   
  55000 |-+    +      +     EE     +      +      +      +     +      +      |   
        +-------------------------------------------------------------------+   
        74     75     76    77     78     79     80     81    82     83     84  

The X axis is time in second, the Y axis is alloc count (start from 0, ++ for allocation, -- for freeing). From the figure you can find that from 75s on, the allocation depth and freeing depth is about 30000, while repetitive period is about 1s.

IMHO, both pattern A and pattern B exists in reality.

Quoted 18 lines
Quoted 18 lines

To detect the repetitive allocation and freeing pattern, the alloc/free depth is calculated for each tuning period (1 second) on each CPU. To calculate the alloc/free depth, we track the alloc count. Which increases for page allocation from PCP and decreases for page freeing to PCP. The alloc depth is the maximum alloc count difference between the later large value and former small value. While, the free depth is the maximum alloc count difference between the former large value and the later small value.

Then, the average alloc/free depth in multiple tuning periods is calculated, with the old alloc/free depth decay in the average gradually.

Finally, the PCP high is set to be the smaller value of average alloc depth and average free depth, after clamped between the default and the max PCP high. In this way, pure allocation or freeing will not enlarge the PCP high because PCP doesn't help.

Quoted 4 lines
Quoted 2 lines

We have tested the algorithm with several workloads on Intel's 2-socket server machines.

How does this scheme deal with memory pressure?

If my understanding were correct, ZONE_RECLAIM_ACTIVE will be set when kswapd is waken up (balance_pgdat()->set_reclaim_active()). Then (pcp->batch * 4) will be used as PCP high (free_unref_page_commit() -> nr_pcp_high()). So, the pages in PCP will be reduced.

Best Regards,
Huang, Ying