Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: only force scan in reclaim when none of the LRUs are big enough.

Prior to this change, we would decide whether to force scan a LRU during
reclaim if that LRU itself was too small for the current priority.
However, this can lead to the file LRU getting force scanned even if
there are a lot of anonymous pages we can reclaim, leading to hot file
pages getting needlessly reclaimed.

To address this, we instead only force scan when none of the reclaimable
LRUs are big enough.

Gives huge improvements with zswap. For example, when doing -j20 kernel
build in a 500MB container with zswap enabled, runtime (in seconds) is
greatly reduced:

x without this change
+ with this change
N Min Max Median Avg Stddev
x 5 700.997 790.076 763.928 754.05 39.59493
+ 5 141.634 197.899 155.706 161.9 21.270224
Difference at 95.0% confidence
-592.15 +/- 46.3521
-78.5293% +/- 6.14709%
(Student's t, pooled s = 31.7819)

Should also give some improvements in regular (non-zswap) swap cases.

Yes, hughd found significant speedup using regular swap, with several
memcgs under pressure; and it should also be effective in the non-memcg
case, whenever one or another zone LRU is forced too small.

Signed-off-by: Suleiman Souhlal <suleiman@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Suleiman Souhlal <suleiman@google.com>
Cc: Mel Gorman <mgorman@suse.de>
Acked-by: Rik van Riel <riel@redhat.com>
Acked-by: Rafael Aquini <aquini@redhat.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Yuanhan Liu <yuanhan.liu@linux.intel.com>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Bob Liu <bob.liu@oracle.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Luigi Semenzato <semenzato@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Suleiman Souhlal and committed by
Linus Torvalds
6f04f48d c86c97ff

+39 -27
+39 -27
mm/vmscan.c
··· 1866 1866 bool force_scan = false; 1867 1867 unsigned long ap, fp; 1868 1868 enum lru_list lru; 1869 + bool some_scanned; 1870 + int pass; 1869 1871 1870 1872 /* 1871 1873 * If the zone or memcg is small, nr[l] can be 0. This ··· 1991 1989 fraction[1] = fp; 1992 1990 denominator = ap + fp + 1; 1993 1991 out: 1994 - for_each_evictable_lru(lru) { 1995 - int file = is_file_lru(lru); 1996 - unsigned long size; 1997 - unsigned long scan; 1992 + some_scanned = false; 1993 + /* Only use force_scan on second pass. */ 1994 + for (pass = 0; !some_scanned && pass < 2; pass++) { 1995 + for_each_evictable_lru(lru) { 1996 + int file = is_file_lru(lru); 1997 + unsigned long size; 1998 + unsigned long scan; 1998 1999 1999 - size = get_lru_size(lruvec, lru); 2000 - scan = size >> sc->priority; 2000 + size = get_lru_size(lruvec, lru); 2001 + scan = size >> sc->priority; 2001 2002 2002 - if (!scan && force_scan) 2003 - scan = min(size, SWAP_CLUSTER_MAX); 2003 + if (!scan && pass && force_scan) 2004 + scan = min(size, SWAP_CLUSTER_MAX); 2004 2005 2005 - switch (scan_balance) { 2006 - case SCAN_EQUAL: 2007 - /* Scan lists relative to size */ 2008 - break; 2009 - case SCAN_FRACT: 2006 + switch (scan_balance) { 2007 + case SCAN_EQUAL: 2008 + /* Scan lists relative to size */ 2009 + break; 2010 + case SCAN_FRACT: 2011 + /* 2012 + * Scan types proportional to swappiness and 2013 + * their relative recent reclaim efficiency. 2014 + */ 2015 + scan = div64_u64(scan * fraction[file], 2016 + denominator); 2017 + break; 2018 + case SCAN_FILE: 2019 + case SCAN_ANON: 2020 + /* Scan one type exclusively */ 2021 + if ((scan_balance == SCAN_FILE) != file) 2022 + scan = 0; 2023 + break; 2024 + default: 2025 + /* Look ma, no brain */ 2026 + BUG(); 2027 + } 2028 + nr[lru] = scan; 2010 2029 /* 2011 - * Scan types proportional to swappiness and 2012 - * their relative recent reclaim efficiency. 2030 + * Skip the second pass and don't force_scan, 2031 + * if we found something to scan. 2013 2032 */ 2014 - scan = div64_u64(scan * fraction[file], denominator); 2015 - break; 2016 - case SCAN_FILE: 2017 - case SCAN_ANON: 2018 - /* Scan one type exclusively */ 2019 - if ((scan_balance == SCAN_FILE) != file) 2020 - scan = 0; 2021 - break; 2022 - default: 2023 - /* Look ma, no brain */ 2024 - BUG(); 2033 + some_scanned |= !!scan; 2025 2034 } 2026 - nr[lru] = scan; 2027 2035 } 2028 2036 } 2029 2037