diff mbox series

[RFC,10/10] autonuma, memory tiering: Adjust hot threshold automatically

Message ID 20191101075727.26683-11-ying.huang@intel.com (mailing list archive)
State New, archived
Headers show
Series autonuma: Optimize memory placement in memory tiering system | expand

Commit Message

Huang, Ying Nov. 1, 2019, 7:57 a.m. UTC
From: Huang Ying <ying.huang@intel.com>

It isn't easy for the administrator to determine the hot threshold.
So in this patch, a method to adjust the hot threshold automatically
is implemented.  The basic idea is to control the number of the
candidate promotion pages to match the promotion rate limit.  If the
hint page fault latency of a page is less than the hot threshold, we
will try to promote the page, that is, the page is the candidate
promotion page.

If the number of the candidate promotion pages in the statistics
interval is much higher than the promotion rate limit, the hot
threshold will be lowered to reduce the number of the candidate
promotion pages.  Otherwise, the hot threshold will be raised to
increase the number of the candidate promotion pages.

To make the above method works, in each statistics interval, the total
number of the pages to check (on which the hint page faults occur) and
the hot/cold distribution need to be stable.  Because the page tables
are scanned linearly in autonuma, but the hot/cold distribution isn't
uniform along the address.  The statistics interval should be larger
than the autonuma scan period.  So in the patch, the max scan period
is used as statistics interval and it works well in our tests.

The sysctl knob kernel.numa_balancing_hot_threshold_ms becomes the
initial value and max value of the hot threshold.

The patch improves the score of pmbench memory accessing benchmark
with 80:20 read/write ratio and normal access address distribution by
5.5% with 24.6% fewer NUMA page migrations on a 2 socket Intel server
with Optance DC Persistent Memory.  Because it improves the accuracy
of the hot page selection.

Signed-off-by: "Huang, Ying" <ying.huang@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mm@kvack.org
---
 include/linux/mmzone.h |  3 +++
 kernel/sched/fair.c    | 48 ++++++++++++++++++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 4 deletions(-)

Comments

Peter Zijlstra Nov. 1, 2019, 9:31 a.m. UTC | #1
On Fri, Nov 01, 2019 at 03:57:27PM +0800, Huang, Ying wrote:

> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 0a83e9cf6685..22bdbb7afac2 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -1486,6 +1486,41 @@ static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
>  	return true;
>  }
>  
> +#define NUMA_MIGRATION_ADJUST_STEPS	16
> +
> +static void numa_migration_adjust_threshold(struct pglist_data *pgdat,
> +					    unsigned long rate_limit,
> +					    unsigned long ref_threshold)
> +{
> +	unsigned long now = jiffies, last_threshold_jiffies;
> +	unsigned long unit_threshold, threshold;
> +	unsigned long try_migrate, ref_try_migrate, mdiff;
> +
> +	last_threshold_jiffies = pgdat->autonuma_threshold_jiffies;
> +	if (now > last_threshold_jiffies +
> +	    msecs_to_jiffies(sysctl_numa_balancing_scan_period_max) &&
> +	    cmpxchg(&pgdat->autonuma_threshold_jiffies,
> +		    last_threshold_jiffies, now) == last_threshold_jiffies) {

That is seriously unreadable gunk.

> +
> +		ref_try_migrate = rate_limit *
> +			sysctl_numa_balancing_scan_period_max / 1000;
> +		try_migrate = node_page_state(pgdat, NUMA_TRY_MIGRATE);
> +		mdiff = try_migrate - pgdat->autonuma_threshold_try_migrate;
> +		unit_threshold = ref_threshold / NUMA_MIGRATION_ADJUST_STEPS;
> +		threshold = pgdat->autonuma_threshold;
> +		if (!threshold)
> +			threshold = ref_threshold;
> +		if (mdiff > ref_try_migrate * 11 / 10)
> +			threshold = max(threshold - unit_threshold,
> +					unit_threshold);
> +		else if (mdiff < ref_try_migrate * 9 / 10)
> +			threshold = min(threshold + unit_threshold,
> +					ref_threshold);

And that is violating codingstyle.

> +		pgdat->autonuma_threshold_try_migrate = try_migrate;
> +		pgdat->autonuma_threshold = threshold;
> +	}
> +}


Maybe if you use variable names that are slightly shorter than half your
line length?
Huang, Ying Nov. 4, 2019, 6:11 a.m. UTC | #2
Peter Zijlstra <peterz@infradead.org> writes:

> On Fri, Nov 01, 2019 at 03:57:27PM +0800, Huang, Ying wrote:
>
>> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
>> index 0a83e9cf6685..22bdbb7afac2 100644
>> --- a/kernel/sched/fair.c
>> +++ b/kernel/sched/fair.c
>> @@ -1486,6 +1486,41 @@ static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
>>  	return true;
>>  }
>>  
>> +#define NUMA_MIGRATION_ADJUST_STEPS	16
>> +
>> +static void numa_migration_adjust_threshold(struct pglist_data *pgdat,
>> +					    unsigned long rate_limit,
>> +					    unsigned long ref_threshold)
>> +{
>> +	unsigned long now = jiffies, last_threshold_jiffies;
>> +	unsigned long unit_threshold, threshold;
>> +	unsigned long try_migrate, ref_try_migrate, mdiff;
>> +
>> +	last_threshold_jiffies = pgdat->autonuma_threshold_jiffies;
>> +	if (now > last_threshold_jiffies +
>> +	    msecs_to_jiffies(sysctl_numa_balancing_scan_period_max) &&
>> +	    cmpxchg(&pgdat->autonuma_threshold_jiffies,
>> +		    last_threshold_jiffies, now) == last_threshold_jiffies) {
>
> That is seriously unreadable gunk.

The basic idea here is to adjust hot threshold every
sysctl_numa_balancing_scan_period_max.  Because the application
accessing pattern may have spatial locality, and autonuma scans address
space linearly.  In general, the statistics of NUMA hint page fault
latency isn't stable in arbitrary internal (such as 1 s, or 200 ms,
etc).  But the scanning of the whole address space of the application is
expected to be finished within sysctl_numa_balancing_scan_period_max.
The statistics of NUMA hint page fault latency is expected to be much
more stable for the whole application.  So we choose to adjust hot
threshold every sysctl_numa_balancing_scan_period_max.

I will add comments for this in the next version.

>> +
>> +		ref_try_migrate = rate_limit *
>> +			sysctl_numa_balancing_scan_period_max / 1000;
>> +		try_migrate = node_page_state(pgdat, NUMA_TRY_MIGRATE);
>> +		mdiff = try_migrate - pgdat->autonuma_threshold_try_migrate;
>> +		unit_threshold = ref_threshold / NUMA_MIGRATION_ADJUST_STEPS;
>> +		threshold = pgdat->autonuma_threshold;
>> +		if (!threshold)
>> +			threshold = ref_threshold;
>> +		if (mdiff > ref_try_migrate * 11 / 10)
>> +			threshold = max(threshold - unit_threshold,
>> +					unit_threshold);
>> +		else if (mdiff < ref_try_migrate * 9 / 10)
>> +			threshold = min(threshold + unit_threshold,
>> +					ref_threshold);
>
> And that is violating codingstyle.

You mean I need to use braces here?

>> +		pgdat->autonuma_threshold_try_migrate = try_migrate;
>> +		pgdat->autonuma_threshold = threshold;
>> +	}
>> +}
>
>
> Maybe if you use variable names that are slightly shorter than half your
> line length?

Sure.  I will use shorter variable name in the next version.

Best Regards,
Huang, Ying
Peter Zijlstra Nov. 4, 2019, 8:49 a.m. UTC | #3
On Mon, Nov 04, 2019 at 02:11:11PM +0800, Huang, Ying wrote:
> Peter Zijlstra <peterz@infradead.org> writes:
> 
> > On Fri, Nov 01, 2019 at 03:57:27PM +0800, Huang, Ying wrote:
> >
> >> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> >> index 0a83e9cf6685..22bdbb7afac2 100644
> >> --- a/kernel/sched/fair.c
> >> +++ b/kernel/sched/fair.c
> >> @@ -1486,6 +1486,41 @@ static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
> >>  	return true;
> >>  }
> >>  
> >> +#define NUMA_MIGRATION_ADJUST_STEPS	16
> >> +
> >> +static void numa_migration_adjust_threshold(struct pglist_data *pgdat,
> >> +					    unsigned long rate_limit,
> >> +					    unsigned long ref_threshold)
> >> +{
> >> +	unsigned long now = jiffies, last_threshold_jiffies;
> >> +	unsigned long unit_threshold, threshold;
> >> +	unsigned long try_migrate, ref_try_migrate, mdiff;
> >> +
> >> +	last_threshold_jiffies = pgdat->autonuma_threshold_jiffies;
> >> +	if (now > last_threshold_jiffies +
> >> +	    msecs_to_jiffies(sysctl_numa_balancing_scan_period_max) &&
> >> +	    cmpxchg(&pgdat->autonuma_threshold_jiffies,
> >> +		    last_threshold_jiffies, now) == last_threshold_jiffies) {
> >
> > That is seriously unreadable gunk.
> 
> The basic idea here is to adjust hot threshold every

Oh, I figured out what it does, but it's just really hard to read
because of those silly variable names.

This was just a first quick read through of the patches, and stuff like
this annoys me no end. I did start a rewrite with more sensible variable
names, but figured this might not be time for that.

I still need to think and review the whole concept in more detail, now
that I've read the patches. But I need to chase regressions first :/

FWIW, can you post a SLIT / NUMA distance table for such a system?
Huang, Ying Nov. 4, 2019, 10:12 a.m. UTC | #4
Peter Zijlstra <peterz@infradead.org> writes:

> On Mon, Nov 04, 2019 at 02:11:11PM +0800, Huang, Ying wrote:
>> Peter Zijlstra <peterz@infradead.org> writes:
>> 
>> > On Fri, Nov 01, 2019 at 03:57:27PM +0800, Huang, Ying wrote:
>> >
>> >> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
>> >> index 0a83e9cf6685..22bdbb7afac2 100644
>> >> --- a/kernel/sched/fair.c
>> >> +++ b/kernel/sched/fair.c
>> >> @@ -1486,6 +1486,41 @@ static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
>> >>  	return true;
>> >>  }
>> >>  
>> >> +#define NUMA_MIGRATION_ADJUST_STEPS	16
>> >> +
>> >> +static void numa_migration_adjust_threshold(struct pglist_data *pgdat,
>> >> +					    unsigned long rate_limit,
>> >> +					    unsigned long ref_threshold)
>> >> +{
>> >> +	unsigned long now = jiffies, last_threshold_jiffies;
>> >> +	unsigned long unit_threshold, threshold;
>> >> +	unsigned long try_migrate, ref_try_migrate, mdiff;
>> >> +
>> >> +	last_threshold_jiffies = pgdat->autonuma_threshold_jiffies;
>> >> +	if (now > last_threshold_jiffies +
>> >> +	    msecs_to_jiffies(sysctl_numa_balancing_scan_period_max) &&
>> >> +	    cmpxchg(&pgdat->autonuma_threshold_jiffies,
>> >> +		    last_threshold_jiffies, now) == last_threshold_jiffies) {
>> >
>> > That is seriously unreadable gunk.
>> 
>> The basic idea here is to adjust hot threshold every
>
> Oh, I figured out what it does, but it's just really hard to read
> because of those silly variable names.
>
> This was just a first quick read through of the patches, and stuff like
> this annoys me no end. I did start a rewrite with more sensible variable
> names, but figured this might not be time for that.

Sorry about the poor naming.  That is always hard for me.

> I still need to think and review the whole concept in more detail, now
> that I've read the patches. But I need to chase regressions first :/

Thanks for your help!

> FWIW, can you post a SLIT / NUMA distance table for such a system?

Sure.  Will send you as attachment in another email.

Best Regards,
Huang, Ying
Huang, Ying Nov. 21, 2019, 8:38 a.m. UTC | #5
Hi, Peter,

Peter Zijlstra <peterz@infradead.org> writes:

> I still need to think and review the whole concept in more detail, now
> that I've read the patches. But I need to chase regressions first :/

Have you found some time to review the concept?

Just want to check the status.  Please don't regard this as pushing.

Best Regards,
Huang, Ying
diff mbox series

Patch

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 46382b058546..afd56541252c 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -772,6 +772,9 @@  typedef struct pglist_data {
 #ifdef CONFIG_NUMA_BALANCING
 	unsigned long autonuma_jiffies;
 	unsigned long autonuma_try_migrate;
+	unsigned long autonuma_threshold_jiffies;
+	unsigned long autonuma_threshold_try_migrate;
+	unsigned long autonuma_threshold;
 #endif
 	/* Fields commonly accessed by the page reclaim scanner */
 	struct lruvec		lruvec;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 0a83e9cf6685..22bdbb7afac2 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1486,6 +1486,41 @@  static bool numa_migration_check_rate_limit(struct pglist_data *pgdat,
 	return true;
 }
 
+#define NUMA_MIGRATION_ADJUST_STEPS	16
+
+static void numa_migration_adjust_threshold(struct pglist_data *pgdat,
+					    unsigned long rate_limit,
+					    unsigned long ref_threshold)
+{
+	unsigned long now = jiffies, last_threshold_jiffies;
+	unsigned long unit_threshold, threshold;
+	unsigned long try_migrate, ref_try_migrate, mdiff;
+
+	last_threshold_jiffies = pgdat->autonuma_threshold_jiffies;
+	if (now > last_threshold_jiffies +
+	    msecs_to_jiffies(sysctl_numa_balancing_scan_period_max) &&
+	    cmpxchg(&pgdat->autonuma_threshold_jiffies,
+		    last_threshold_jiffies, now) == last_threshold_jiffies) {
+
+		ref_try_migrate = rate_limit *
+			sysctl_numa_balancing_scan_period_max / 1000;
+		try_migrate = node_page_state(pgdat, NUMA_TRY_MIGRATE);
+		mdiff = try_migrate - pgdat->autonuma_threshold_try_migrate;
+		unit_threshold = ref_threshold / NUMA_MIGRATION_ADJUST_STEPS;
+		threshold = pgdat->autonuma_threshold;
+		if (!threshold)
+			threshold = ref_threshold;
+		if (mdiff > ref_try_migrate * 11 / 10)
+			threshold = max(threshold - unit_threshold,
+					unit_threshold);
+		else if (mdiff < ref_try_migrate * 9 / 10)
+			threshold = min(threshold + unit_threshold,
+					ref_threshold);
+		pgdat->autonuma_threshold_try_migrate = try_migrate;
+		pgdat->autonuma_threshold = threshold;
+	}
+}
+
 bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 				int src_nid, int dst_cpu, unsigned long addr,
 				int flags)
@@ -1501,7 +1536,7 @@  bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 	if (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING &&
 	    next_promotion_node(src_nid) != -1) {
 		struct pglist_data *pgdat;
-		unsigned long rate_limit, latency, threshold;
+		unsigned long rate_limit, latency, threshold, def_threshold;
 
 		pgdat = NODE_DATA(dst_nid);
 		if (pgdat_free_space_enough(pgdat))
@@ -1511,16 +1546,21 @@  bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 		if (!(flags & TNF_YOUNG))
 			return false;
 
-		threshold = msecs_to_jiffies(
+		def_threshold = msecs_to_jiffies(
 			sysctl_numa_balancing_hot_threshold);
+		rate_limit = sysctl_numa_balancing_rate_limit <<
+			(20 - PAGE_SHIFT);
+		numa_migration_adjust_threshold(pgdat, rate_limit,
+						def_threshold);
+
+		threshold = pgdat->autonuma_threshold;
+		threshold = threshold ? : def_threshold;
 		if (flags & TNF_WRITE)
 			threshold *= 2;
 		latency = numa_hint_fault_latency(p, addr);
 		if (latency > threshold)
 			return false;
 
-		rate_limit = sysctl_numa_balancing_rate_limit <<
-			(20 - PAGE_SHIFT);
 		return numa_migration_check_rate_limit(pgdat, rate_limit,
 						       hpage_nr_pages(page));
 	}