re-shrink 'struct page' when SLUB is on.
Subsystems (8)
cover re-shrink 'struct page' when SLUB is on.
This is a minor update from the last version. The most notable thing is that I was able to demonstrate that maintaining the cmpxchg16 optimization has _some_ value.
Otherwise, the code changes are just a few minor cleanups.
---
SLUB depends on a 16-byte cmpxchg for an optimization which allows it to not disable interrupts in its fast path. This optimization has some small but measurable benefits:
http://lkml.kernel.org/r/[email protected]
In order to get guaranteed 16-byte alignment (required by the hardware on x86), 'struct page' is padded out from 56 to 64 bytes.
Those 8-bytes matter. We've gone to great lengths to keep 'struct page' small in the past. It's a shame that we bloat it now just for alignment reasons when we have extra space. Plus, bloating such a commonly-touched structure *HAS* cache footprint implications.
These patches attempt _internal_ alignment instead of external alignment for slub.
I also got a bug report from some folks running a large database benchmark. Their old kernel uses slab and their new one uses slub. They were swapping and couldn't figure out why. It turned out to be the 2GB of RAM that the slub padding wastes on their system.
On my box, that 2GB cost about $200 to populate back when we bought it. I want my $200 back.
This set takes me from 16909584K of reserved memory at boot down to 14814472K, so almost *exactly* 2GB of savings! It also helps performance, presumably because it touches 14% fewer struct page cachelines. A 30GB dd to a ramfs file:
dd if=/dev/zero of=bigfile bs=$((1<<30)) count=30
is sped up by about 4.4% in my testing.
I've run this through its paces and have not had stability issues with it. It definitely needs some more testing, but it's definitely ready for a wider audience.
I also wrote up a document describing 'struct page's layout:
PATCH 1/9 mm: slab/slub: use page->list consistently instead of page->lru
From: Dave Hansen <[email protected]>
'struct page' has two list_head fields: 'lru' and 'list'. Conveniently, they are unioned together. This means that code can use them interchangably, which gets horribly confusing like with this nugget from slab.c:
Quoted 3 lines
list_del(&page->lru); if (page->active == cachep->num) list_add(&page->list, &n->slabs_full);
This patch makes the slab and slub code use page->list universally instead of mixing ->list and ->lru.
It also adds some comments to attempt to keep new users from picking up uses of ->list.
So, the new rule is: page->list is what the slabs use. page->lru is for everybody else. This is a pretty arbitrary rule, but we need _something_. Maybe we should just axe the ->list one and make the sl?bs use ->lru.
---
linux.git-davehans/include/linux/mm_types.h | 5 ++ linux.git-davehans/mm/slab.c | 50 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 26 deletions(-)
include/linux/mm_types.h renamed from include/linux/mm_types.h~make-slab-use-page-lru-vs-list-consistently +4 -1
@@ -124,6 +124,8 @@ struct page {union {struct list_head lru;/* Pageout list, eg. active_list * protected by zone->lru_lock ! +* Can be used as a generic list +* by the page owner. */ struct {/* slub per cpu partial pages */ struct page *next;/* Next partial slab */
@@ -136,7 +138,8 @@ struct page {#endif }; -struct list_head list;/* slobs list of pages */ +struct list_head list;/* sl[aou]bs list of pages. +* do not use outside of slabs */ struct slab *slab_page;/* slab fields */ struct rcu_head rcu_head;/* Used by SLAB * when destroying via RCU
mm/slab.c renamed from mm/slab.c~make-slab-use-page-lru-vs-list-consistently +25 -25
170 lines not on this page · raw message
_
PATCH 2/9 mm: blk-mq: uses page->list incorrectly
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
'struct page' has two list_head fields: 'lru' and 'list'. Conveniently, they are unioned together. This means that code can use them interchangably, which gets horribly confusing.
The blk-mq made the logical decision to try to use page->list. But, that field was actually introduced just for the slub code. ->lru is the right field to use outside of slab/slub.
---
linux.git-davehans/block/blk-mq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
block/blk-mq.c renamed from block/blk-mq.c~blk-mq-uses-page-list-incorrectly +3 -3
@@ -1091,8 +1091,8 @@ static void blk_mq_free_rq_map(struct blstruct page *page;while (!list_empty (&hctx->page_list)) { - page = list_first_entry(&hctx->page_list, struct page, list); -list_del_init (&page->list); + page = list_first_entry(&hctx->page_list, struct page, lru); +list_del_init (&page->lru);__free_pages (page, page->private); }
@@ -1156,7 +1156,7 @@ static int blk_mq_init_rq_map(struct blkbreak ; page->private = this_order; -list_add_tail (&page->list, &hctx->page_list); +list_add_tail (&page->lru, &hctx->page_list); p =page_address (page); entries_per_page =order_to_size (this_order) / rq_size;
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 3/9 mm: page->pfmemalloc only used by slab/skb
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
page->pfmemalloc does not deserve a spot in 'struct page'. It is only used transiently _just_ after a page leaves the buddy allocator.
Instead of declaring a union, we move its functionality behind a few quick accessor functions. This way we could also much more easily audit that it is being used correctly in debugging scenarios. For instance, we could store a magic number in there which could never get reused as a page->index and check that the magic number exists in page_pfmemalloc().
---
linux.git-davehans/include/linux/mm.h | 17 +++++++++++++++++ linux.git-davehans/include/linux/mm_types.h | 9 --------- linux.git-davehans/include/linux/skbuff.h | 10 +++++----- linux.git-davehans/mm/page_alloc.c | 2 +- linux.git-davehans/mm/slab.c | 4 ++-- linux.git-davehans/mm/slub.c | 2 +- 6 files changed, 26 insertions(+), 18 deletions(-)
include/linux/mm.h renamed from include/linux/mm.h~page_pfmemalloc-only-used-by-slab +17 -0
@@ -2011,5 +2011,22 @@ void __init setup_nr_node_ids(void);static inline void setup_nr_node_ids (void ) {}#endif +/* +* If set by the page allocator, ALLOC_NO_WATERMARKS was set and the +* low watermark was not met implying that the system is under some +* pressure. The caller should try ensure this page is only used to +* free other pages. Currently only used by sl[au]b. Note that +* this is only valid for a short time after the page returns +* from the allocator. +*/ +static inline int page_pfmemalloc (struct page *page) +{ +return !!page->index; +} +static inline void set_page_pfmemalloc (struct page *page,int pfmemalloc) +{ + page->index = pfmemalloc; +} +#endif /* __KERNEL__ */ #endif /* _LINUX_MM_H */
include/linux/mm_types.h renamed from include/linux/mm_types.h~page_pfmemalloc-only-used-by-slab +0 -9
@@ -61,15 +61,6 @@ struct page {union {pgoff_t index;/* Our offset within mapping. */ void *freelist;/* sl[aou]b first free object */ -bool pfmemalloc;/* If set by the page allocator, -* ALLOC_NO_WATERMARKS was set -* and the low watermark was not -* met implying that the system -* is under some pressure. The -* caller should try ensure -* this page is only used to -* free other pages. -*/ };union {
include/linux/skbuff.h renamed from include/linux/skbuff.h~page_pfmemalloc-only-used-by-slab +5 -5
@@ -1322,11 +1322,11 @@ static inline void __skb_fill_page_desc(skb_frag_t *frag = &skb_shinfo (skb)->frags[i];/* -* Propagate page->pfmemalloc to the skb if we can. The problem is +* Propagate page_pfmemalloc() to the skb if we can. The problem is * that not all callers have unique ownership of the page. If * pfmemalloc is set, we check the mapping as a mapping implies * page->index is set (index and pfmemalloc share space). -* If it's a valid mapping, we cannot use page->pfmemalloc but we +* If it's a valid mapping, we cannot use page_pfmemalloc() but we * do not lose pfmemalloc information as the pages would not be * allocated using __GFP_MEMALLOC. */
@@ -1335,7 +1335,7 @@ static inline void __skb_fill_page_desc(skb_frag_size_set (frag, size); page =compound_head (page); -if (page->pfmemalloc && !page->mapping) +if (page_pfmemalloc (page) && !page->mapping) skb->pfmemalloc = true; }
@@ -1922,7 +1922,7 @@ static inline struct page *__skb_alloc_p gfp_mask |= __GFP_MEMALLOC; page =alloc_pages_node (NUMA_NO_NODE , gfp_mask, order); -if (skb && page && page->pfmemalloc) +if (skb && page &&page_pfmemalloc (page)) skb->pfmemalloc = true;return page;
@@ -1951,7 +1951,7 @@ static inline struct page *__skb_alloc_pstatic inline void skb_propagate_pfmemalloc (struct page *page,struct sk_buff *skb) { -if (page && page->pfmemalloc) +if (page &&page_pfmemalloc (page)) skb->pfmemalloc = true; }
mm/page_alloc.c renamed from mm/page_alloc.c~page_pfmemalloc-only-used-by-slab +1 -1
@@ -2040,7 +2040,7 @@ this_zone_full:* memory. The caller should avoid the page being used * for !PFMEMALLOC purposes. */ - page->pfmemalloc = !!(alloc_flags &ALLOC_NO_WATERMARKS ); +set_page_pfmemalloc (page, alloc_flags &ALLOC_NO_WATERMARKS );return page; }
mm/slab.c renamed from mm/slab.c~page_pfmemalloc-only-used-by-slab +2 -2
@@ -1672,7 +1672,7 @@ static struct page *kmem_getpages(struct }/* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ -if (unlikely (page->pfmemalloc)) +if (unlikely (page_pfmemalloc (page))) pfmemalloc_active = true; nr_pages = (1 << cachep->gfporder);
@@ -1683,7 +1683,7 @@ static struct page *kmem_getpages(structadd_zone_page_state (page_zone (page),NR_SLAB_UNRECLAIMABLE , nr_pages);__SetPageSlab (page); -if (page->pfmemalloc) +if (page_pfmemalloc (page))SetPageSlabPfmemalloc (page);memcg_bind_pages (cachep, cachep->gfporder);
mm/slub.c renamed from mm/slub.c~page_pfmemalloc-only-used-by-slab +1 -1
@@ -1403,7 +1403,7 @@ static struct page *new_slab(struct kmemmemcg_bind_pages (s, order); page->slab_cache = s;__SetPageSlab (page); -if (page->pfmemalloc) +if (page_pfmemalloc (page))SetPageSlabPfmemalloc (page); start =page_address (page);
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 4/9 mm: slabs: reset page at free
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
In order to simplify 'struct page', we will shortly be moving some fields around. This causes slub's ->freelist usage to impinge on page->mapping's storage space. The buddy allocator wants ->mapping to be NULL when a page is handed back, so we have to make sure that it is cleared.
Note that slab already doeds this, so just create a common helper and have all the slabs do it this way. ->mapping is right next to ->flags, so it's virtually guaranteed to be in the L1 at this point, so this shouldn't cost very much to do in practice.
Other allocators and users of 'struct page' may also want to call this if they use parts of 'struct page' for nonstandard purposes.
---
linux.git-davehans/include/linux/mm.h | 11 +++++++++++ linux.git-davehans/mm/slab.c | 3 +-- linux.git-davehans/mm/slob.c | 2 +- linux.git-davehans/mm/slub.c | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-)
include/linux/mm.h renamed from include/linux/mm.h~slub-reset-page-at-free +11 -0
@@ -2028,5 +2028,16 @@ static inline void set_page_pfmemalloc(s page->index = pfmemalloc; } +/* +* Custom allocators (like the slabs) use 'struct page' fields +* for all kinds of things. This resets the page's state so that +* the buddy allocator will be happy with it. +*/ +static inline void allocator_reset_page (struct page *page) +{ + page->mapping =NULL ; +page_mapcount_reset (page); +} +#endif /* __KERNEL__ */ #endif /* _LINUX_MM_H */
mm/slab.c renamed from mm/slab.c~slub-reset-page-at-free +1 -2
@@ -1718,8 +1718,7 @@ static void kmem_freepages(struct kmem_cBUG_ON (!PageSlab (page));__ClearPageSlabPfmemalloc (page);__ClearPageSlab (page); -page_mapcount_reset (page); - page->mapping =NULL ; +allocator_reset_page (page);memcg_release_pages (cachep, cachep->gfporder);if (current->reclaim_state)
mm/slob.c renamed from mm/slob.c~slub-reset-page-at-free +1 -1
@@ -360,7 +360,7 @@ static void slob_free(void *block, int sclear_slob_page_free (sp);spin_unlock_irqrestore (&slob_lock, flags);__ClearPageSlab (sp); -page_mapcount_reset (sp); +allocator_reset_page (sp);slob_free_pages (b,0 );return ; }
mm/slub.c renamed from mm/slub.c~slub-reset-page-at-free +1 -1
@@ -1452,7 +1452,7 @@ static void __free_slab(struct kmem_cach__ClearPageSlab (page);memcg_release_pages (s, order); -page_mapcount_reset (page); +allocator_reset_page (page);if (current->reclaim_state) current->reclaim_state->reclaimed_slab += pages;__free_memcg_kmem_pages (page, order);
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 5/9 mm: rearrange struct page
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
To make the layout of 'struct page' look nicer, I broke up a few of the unions. But, this has a cost: things that were guaranteed to line up before might not any more. To make up for that, some BUILD_BUG_ON()s are added to manually check for the alignment dependencies.
This makes it *MUCH* more clear how the first few fields of 'struct page' get used by the slab allocators.
---
linux.git-davehans/include/linux/mm_types.h | 101 ++++++++++++++-------------- linux.git-davehans/mm/slab.c | 6 - linux.git-davehans/mm/slab_common.c | 17 ++++ linux.git-davehans/mm/slob.c | 25 +++--- 4 files changed, 84 insertions(+), 65 deletions(-)
include/linux/mm_types.h renamed from include/linux/mm_types.h~rearrange-struct-page +51 -50
@@ -46,27 +46,60 @@ struct page {unsigned long flags;/* Atomic flags, some possibly * updated asynchronously */ union { -struct address_space *mapping;/* If low bit clear, points to -* inode address_space, or NULL. -* If page mapped as anonymous -* memory, low bit is set, and -* it points to anon_vma object: -* see PAGE_MAPPING_ANON below. -*/ -void *s_mem;/* slab first object */ - }; - -/* Second double word */ -struct { -union { + struct /* the normal uses */ { pgoff_t index; /* Our offset within mapping. */ -void *freelist;/* sl[aou]b first free object */ + /* + * mapping: If low bit clear, points to + * inode address_space, or NULL. If page + * mapped as anonymous memory, low bit is + * set, and it points to anon_vma object: + * see PAGE_MAPPING_ANON below. + */ + struct address_space *mapping; + /* + * Count of ptes mapped in mms, to show when page + * is mapped & limit reverse map searches. + * + * Used also for tail pages refcounting instead + * of _count. Tail pages cannot be mapped and + * keeping the tail page _count zero at all times + * guarantees get_page_unless_zero() will never + * succeed on tail pages. + */ + atomic_t _mapcount; + atomic_t _count; + }; /* end of the "normal" use */ + + struct { /* SLUB */ + void *unused; + void *freelist; + unsigned inuse:16; + unsigned objects:15; + unsigned frozen:1; + atomic_t dontuse_slub_count; }; - -union { + struct { /* SLAB */ + void *s_mem; + void *slab_freelist; + unsigned int active; + atomic_t dontuse_slab_count; + }; + struct { /* SLOB */ + void *slob_unused; + void *slob_freelist; + unsigned int units; + atomic_t dontuse_slob_count; + }; + /* + * This is here to help the slub code deal with + * its inuse/objects/frozen bitfields as a single + * blob. + */ + struct { /* slub helpers */ + void *slubhelp_unused; + void *slubhelp_freelist; #if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ) -/* Used for cmpxchg_double in slub */ + defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE) unsigned long counters; #else /*
@@ -76,38 +109,6 @@ struct page {*/ unsigned counters;#endif - -struct { - -union { -/* -* Count of ptes mapped in -* mms, to show when page is -* mapped & limit reverse map -* searches. -* -* Used also for tail pages -* refcounting instead of -* _count. Tail pages cannot -* be mapped and keeping the -* tail page _count zero at -* all times guarantees -* get_page_unless_zero() will -* never succeed on tail -* pages. -*/ -atomic_t _mapcount; - -struct {/* SLUB */ -unsigned inuse:16 ; -unsigned objects:15 ; -unsigned frozen:1 ; - }; -int units;/* SLOB */ - }; -atomic_t _count;/* Usage count, see below. */ - }; -unsigned int active;/* SLAB */ }; };
mm/slab.c renamed from mm/slab.c~rearrange-struct-page +3 -3
@@ -1955,7 +1955,7 @@ static void slab_destroy(struct kmem_cac {void *freelist; - freelist = page->freelist; + freelist = page->slab_freelist;slab_destroy_debugcheck (cachep, page);if (unlikely (cachep->flags &SLAB_DESTROY_BY_RCU )) {struct rcu_head *head;
@@ -2543,7 +2543,7 @@ static void *alloc_slabmgmt(struct kmem_static inline unsigned int *slab_freelist (struct page *page) { -return (unsigned int *)(page->freelist); +return (unsigned int *)(page->slab_freelist); } static void cache_init_objs(struct kmem_cache *cachep,
@@ -2648,7 +2648,7 @@ static void slab_map_pages(struct kmem_c void *freelist) { page->slab_cache = cache; - page->freelist = freelist; + page->slab_freelist = freelist; }/*
mm/slab_common.c renamed from mm/slab_common.c~rearrange-struct-page +17 -0
@@ -658,3 +658,20 @@ static int __init slab_proc_init(void) }module_init (slab_proc_init);#endif /* CONFIG_SLABINFO */ +#define SLAB_PAGE_CHECK (field1, field2) \ + BUILD_BUG_ON(offsetof(struct page, field1) != \ + offsetof(struct page, field2)) +/* +* To make the layout of 'struct page' look nicer, we've broken +* up a few of the unions. Folks declaring their own use of the +* first few fields need to make sure that their use does not +* interfere with page->_count. This ensures that the individual +* users' use actually lines up with the real ->_count. +*/ +void slab_build_checks (void ) +{ +SLAB_PAGE_CHECK (_count, dontuse_slab_count); +SLAB_PAGE_CHECK (_count, dontuse_slub_count); +SLAB_PAGE_CHECK (_count, dontuse_slob_count); +} +
mm/slob.c renamed from mm/slob.c~rearrange-struct-page +13 -12
@@ -219,7 +219,8 @@ static void *slob_page_alloc(struct pageslob_t *prev, *cur, *aligned =NULL ;int delta =0 , units =SLOB_UNITS (size); -for (prev =NULL , cur = sp->freelist; ; prev = cur, cur =slob_next (cur)) { +for (prev =NULL , cur = sp->slob_freelist; ; + prev = cur, cur =slob_next (cur)) {slobidx_t avail =slob_units (cur);if (align) {
@@ -243,12 +244,12 @@ static void *slob_page_alloc(struct pageif (prev)set_slob (prev,slob_units (prev), next);else - sp->freelist = next; + sp->slob_freelist = next; }else {/* fragment */ if (prev)set_slob (prev,slob_units (prev), cur + units);else - sp->freelist = cur + units; + sp->slob_freelist = cur + units;set_slob (cur + units, avail - units, next); }
@@ -321,7 +322,7 @@ static void *slob_alloc(size_t size, gfpspin_lock_irqsave (&slob_lock, flags); sp->units =SLOB_UNITS (PAGE_SIZE ); - sp->freelist = b; + sp->slob_freelist = b;INIT_LIST_HEAD (&sp->list);set_slob (b,SLOB_UNITS (PAGE_SIZE ), b +SLOB_UNITS (PAGE_SIZE ));set_slob_page_free (sp, slob_list);
@@ -368,7 +369,7 @@ static void slob_free(void *block, int sif (!slob_page_free (sp)) {/* This slob page is about to become partially free. Easy! */ sp->units = units; - sp->freelist = b; + sp->slob_freelist = b;set_slob (b, units, (void *)((unsigned long )(b +SLOB_UNITS (PAGE_SIZE )) &PAGE_MASK ));
@@ -388,15 +389,15 @@ static void slob_free(void *block, int s*/ sp->units += units; -if (b < (slob_t *)sp->freelist) { -if (b + units == sp->freelist) { - units +=slob_units (sp->freelist); - sp->freelist =slob_next (sp->freelist); +if (b < (slob_t *)sp->slob_freelist) { +if (b + units == sp->slob_freelist) { + units +=slob_units (sp->slob_freelist); + sp->slob_freelist =slob_next (sp->slob_freelist); } -set_slob (b, units, sp->freelist); - sp->freelist = b; +set_slob (b, units, sp->slob_freelist); + sp->slob_freelist = b; }else { - prev = sp->freelist; + prev = sp->slob_freelist; next =slob_next (prev);while (b > next) { prev = next;
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 6/9 mm: slub: rearrange 'struct page' fields
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
SLUB has some very unique alignment constraints it places on 'struct page'. Break those out in to a separate structure which will not pollute 'struct page'.
This structure will be moved around inside 'struct page' at runtime in the next patch, so it is necessary to break it out for those uses as well.
Vim pattern used for the renames:
%s/\(page\|new\)\(->\|\.\)\(freelist\|counters\)/slub_data(\1)\2\3/g
---
linux.git-davehans/include/linux/mm_types.h | 66 ++++--- linux.git-davehans/mm/slab_common.c | 29 ++- linux.git-davehans/mm/slub.c | 253 ++++++++++++++-------------- 3 files changed, 192 insertions(+), 156 deletions(-)
include/linux/mm_types.h renamed from include/linux/mm_types.h~slub-rearrange +38 -28
@@ -23,6 +23,43 @@struct address_space ; +struct slub_data { +void *unused; +void *freelist; +union { +struct { +unsigned inuse:16 ; +unsigned objects:15 ; +unsigned frozen:1 ; +atomic_t dontuse_slub_count; + }; +/* +* ->counters is used to make it easier to copy +* all of the above counters in one chunk. +* The actual counts are never accessed via this. +*/ +#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ + defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ) +unsigned long counters; +#else +/* +* Keep _count separate from slub cmpxchg_double data. +* As the rest of the double word is protected by +* slab_lock but _count is not. +*/ +struct { +unsigned counters; +/* +* This isn't used directly, but declare it here +* for clarity since it must line up with _count +* from 'struct page' +*/ +atomic_t separate_count; + }; +#endif + }; +}; +#define USE_SPLIT_PTE_PTLOCKS (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)#define USE_SPLIT_PMD_PTLOCKS (USE_SPLIT_PTE_PTLOCKS && \ IS_ENABLED(CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK))
@@ -70,14 +107,7 @@ struct page {atomic_t _count; };/* end of the "normal" use */ -struct {/* SLUB */ -void *unused; -void *freelist; -unsigned inuse:16 ; -unsigned objects:15 ; -unsigned frozen:1 ; -atomic_t dontuse_slub_count; - }; +struct slub_data slub_data;struct {/* SLAB */ void *s_mem;void *slab_freelist;
@@ -90,26 +120,6 @@ struct page {unsigned int units;atomic_t dontuse_slob_count; }; -/* -* This is here to help the slub code deal with -* its inuse/objects/frozen bitfields as a single -* blob. -*/ -struct {/* slub helpers */ -void *slubhelp_unused; -void *slubhelp_freelist; -#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ) -unsigned long counters; -#else -/* -* Keep _count separate from slub cmpxchg_double data. -* As the rest of the double word is protected by -* slab_lock but _count is not. -*/ -unsigned counters; -#endif - }; };/* Third double word block */
mm/slab_common.c renamed from mm/slab_common.c~slub-rearrange +24 -5
@@ -658,20 +658,39 @@ static int __init slab_proc_init(void) }module_init (slab_proc_init);#endif /* CONFIG_SLABINFO */ +#define SLAB_PAGE_CHECK (field1, field2) \ BUILD_BUG_ON(offsetof(struct page, field1) != \ offsetof(struct page, field2))/* * To make the layout of 'struct page' look nicer, we've broken -* up a few of the unions. Folks declaring their own use of the -* first few fields need to make sure that their use does not -* interfere with page->_count. This ensures that the individual -* users' use actually lines up with the real ->_count. +* up a few of the unions. But, this has made it hard to see if +* any given use will interfere with page->_count. +* +* To work around this, each user declares their own _count field +* and we check them at build time to ensure that the independent +* definitions actually line up with the real ->_count. */ void slab_build_checks (void ) {SLAB_PAGE_CHECK (_count, dontuse_slab_count); -SLAB_PAGE_CHECK (_count, dontuse_slub_count); +SLAB_PAGE_CHECK (_count, slub_data.dontuse_slub_count);SLAB_PAGE_CHECK (_count, dontuse_slob_count); + +/* +* When doing a double-cmpxchg, the slub code sucks in +* _count. But, this is harmless since if _count is +* modified, the cmpxchg will fail. When not using a +* real cmpxchg, the slub code uses a lock. But, _count +* is not modified under that lock and updates can be +* lost if they race with one of the "faked" cmpxchg +* under that lock. This makes sure that the space we +* carve out for _count in that case actually lines up +* with the real _count. +*/ +#if !(defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ + defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE )) +SLAB_PAGE_CHECK (_count, slub_data.separate_count); +#endif }
mm/slub.c renamed from mm/slub.c~slub-rearrange +130 -123
596 lines not on this page · raw message
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 7/9 mm: slub: abstract out double cmpxchg option
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
I found this useful to have in my testing. I would like to have it available for a bit, at least until other folks have had a chance to do some testing with it.
We should probably just pull the help text and the description out of this so that folks are not prompted for it instead of ripping it out entirely.
---
linux.git-davehans/include/linux/mm_types.h | 3 +-- linux.git-davehans/init/Kconfig | 2 +- linux.git-davehans/mm/slub.c | 9 +++------ 3 files changed, 5 insertions(+), 9 deletions(-)
include/linux/mm_types.h renamed from include/linux/mm_types.h~abstract-out-slub-double-cmpxchg-operation +1 -2
@@ -38,8 +38,7 @@ struct slub_data {* all of the above counters in one chunk. * The actual counts are never accessed via this. */ -#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ) +#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE )unsigned long counters;#else /*
init/Kconfig renamed from init/Kconfig~abstract-out-slub-double-cmpxchg-operation +1 -1
@@ -841,7 +841,7 @@ config SLUB_CPU_PARTIAL config SLUB_ATTEMPT_CMPXCHG_DOUBLE default y - depends on SLUB && HAVE_CMPXCHG_DOUBLE + depends on SLUB && HAVE_CMPXCHG_DOUBLE && HAVE_ALIGNED_STRUCT_PAGE bool "SLUB: attempt to use double-cmpxchg operations" help Some CPUs support instructions that let you do a large double-word
mm/slub.c renamed from mm/slub.c~abstract-out-slub-double-cmpxchg-operation +3 -6
@@ -368,8 +368,7 @@ static inline bool __cmpxchg_double_slab const char *n) { VM_BUG_ON(!irqs_disabled()); -#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE) +#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE) if (s->flags & __CMPXCHG_DOUBLE) { if (cmpxchg_double(&slub_data(page)->freelist, &slub_data(page)->counters, freelist_old, counters_old,
@@ -404,8 +403,7 @@ static inline bool cmpxchg_double_slab(s void *freelist_new, unsigned long counters_new, const char *n) { -#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE) +#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE) if (s->flags & __CMPXCHG_DOUBLE) { if (cmpxchg_double(&slub_data(page)->freelist, &slub_data(page)->counters, freelist_old, counters_old,
@@ -3085,8 +3083,7 @@ static int kmem_cache_open(struct kmem_c } } -#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ) +#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE )if (system_has_cmpxchg_double () && (s->flags &SLAB_DEBUG_FLAGS ) ==0 )/* Enable fast mode */ s->flags |= __CMPXCHG_DOUBLE;
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 8/9 mm: slub: remove 'struct page' alignment restrictions
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
SLUB depends on a 16-byte cmpxchg for an optimization. In order to get guaranteed 16-byte alignment (required by the hardware on x86), 'struct page' is padded out from 56 to 64 bytes.
Those 8-bytes matter. We've gone to great lengths to keep 'struct page' small in the past. It's a shame that we bloat it now just for alignment reasons when we have *extra* space. Also, increasing the size of 'struct page' by 14% makes it 14% more likely that we will miss a cacheline when fetching it.
This patch takes an unused 8-byte area of slub's 'struct page' and reuses it to internally align to the 16-bytes that we need.
Note that this also gets rid of the ugly slub #ifdef that we use to segregate ->counters and ->_count for cases where we need to manipulate ->counters without the benefit of a hardware cmpxchg.
This patch takes me from 16909584K of reserved memory at boot down to 14814472K, so almost *exactly* 2GB of savings! It also helps performance, presumably because of that 14% fewer cacheline effect. A 30GB dd to a ramfs file:
dd if=/dev/zero of=bigfile bs=$((1<<30)) count=30
is sped up by about 4.4% in my testing.
The value of maintaining the cmpxchg16 operation can be demonstrated in some tiny little microbenchmarks, so it is probably something we should keep around instead of just using the spinlock for everything:
http://lkml.kernel.org/r/[email protected]
---
linux.git-davehans/arch/Kconfig | 8 ---- linux.git-davehans/arch/s390/Kconfig | 1 linux.git-davehans/arch/x86/Kconfig | 1 linux.git-davehans/include/linux/mm_types.h | 55 +++++++--------------------- linux.git-davehans/init/Kconfig | 2 - linux.git-davehans/mm/slab_common.c | 10 +++-- linux.git-davehans/mm/slub.c | 4 ++ 7 files changed, 26 insertions(+), 55 deletions(-)
arch/Kconfig renamed from arch/Kconfig~remove-struct-page-alignment-restrictions +0 -8
@@ -289,14 +289,6 @@ config HAVE_RCU_TABLE_FREE config ARCH_HAVE_NMI_SAFE_CMPXCHG bool -config HAVE_ALIGNED_STRUCT_PAGE - bool - help - This makes sure that struct pages are double word aligned and that - e.g. the SLUB allocator can perform double word atomic operations - on a struct page for better performance. However selecting this - might increase the size of a struct page by a word. - config HAVE_CMPXCHG_LOCAL bool
arch/s390/Kconfig renamed from arch/s390/Kconfig~remove-struct-page-alignment-restrictions +0 -1
@@ -102,7 +102,6 @@ config S390 select GENERIC_FIND_FIRST_BIT select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL - select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_SECCOMP
arch/x86/Kconfig renamed from arch/x86/Kconfig~remove-struct-page-alignment-restrictions +0 -1
@@ -77,7 +77,6 @@ config X86 select HAVE_PERF_USER_STACK_DUMP select HAVE_DEBUG_KMEMLEAK select ANON_INODES - select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_CMPXCHG_LOCAL select HAVE_ARCH_KMEMCHECK select HAVE_USER_RETURN_NOTIFIER
include/linux/mm_types.h renamed from include/linux/mm_types.h~remove-struct-page-alignment-restrictions +15 -40
97 lines not on this page · raw message
init/Kconfig renamed from init/Kconfig~remove-struct-page-alignment-restrictions +1 -1
@@ -841,7 +841,7 @@ config SLUB_CPU_PARTIAL config SLUB_ATTEMPT_CMPXCHG_DOUBLE default y - depends on SLUB && HAVE_CMPXCHG_DOUBLE && HAVE_ALIGNED_STRUCT_PAGE + depends on SLUB && HAVE_CMPXCHG_DOUBLE bool "SLUB: attempt to use double-cmpxchg operations" help Some CPUs support instructions that let you do a large double-word
mm/slab_common.c renamed from mm/slab_common.c~remove-struct-page-alignment-restrictions +6 -4
@@ -674,7 +674,6 @@ module_init(slab_proc_init);void slab_build_checks (void ) {SLAB_PAGE_CHECK (_count, dontuse_slab_count); -SLAB_PAGE_CHECK (_count, slub_data.dontuse_slub_count);SLAB_PAGE_CHECK (_count, dontuse_slob_count);/*
@@ -688,9 +687,12 @@ void slab_build_checks(void)* carve out for _count in that case actually lines up * with the real _count. */ -#if !(defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ))SLAB_PAGE_CHECK (_count, slub_data.separate_count); -#endif + +/* +* We need at least three double-words worth of space to +* ensure that we can align to a double-wordk internally. +*/ +BUILD_BUG_ON (sizeof (struct slub_data ) !=sizeof (unsigned long ) *3 ); }
mm/slub.c renamed from mm/slub.c~remove-struct-page-alignment-restrictions +4 -0
@@ -239,7 +239,11 @@ static inline struct kmem_cache_node *gestatic inline struct slub_data *slub_data (struct page *page) { +int doubleword_bytes =BITS_PER_LONG *2 /8 ;void *ptr = &page->slub_data; +#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE ) + ptr =PTR_ALIGN (ptr, doubleword_bytes); +#endif return ptr; }
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 9/9 mm: slub: cleanups after code churn
In reply to Dave HansenFrom: Dave Hansen <[email protected]>
I added a bunch of longer than 80 column lines and other various messes. But, doing line-to-line code replacements makes the previous patch much easier to audit. I stuck the cleanups in here instead.
The slub code also delcares a bunch of 'struct page's on the stack. Now that 'struct slub_data' is separate, we can declare those smaller structures instead. This ends up saving us a couple hundred bytes in object size.
Doing all the work of doing the pointer alignment operations over and over costs us some code size. In the end (not this patch alone), we take slub.o's size from 26672->27168, so about 500 bytes. But, on an 8GB system, we saved about 256k in 'struct page' overhead. That's a pretty good tradeoff.
---
linux.git-davehans/mm/slub.c | 129 ++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 61 deletions(-)
mm/slub.c renamed from mm/slub.c~slub-cleanups +68 -61
@@ -257,7 +257,8 @@ static inline int check_valid_pointer(streturn 1 ; base =page_address (page); -if (object < base || object >= base +slub_data (page)->objects * s->size || +if (object < base || + object >= base +slub_data (page)->objects * s->size || (object - base) % s->size) {return 0 ; }
@@ -374,16 +375,17 @@ static inline bool __cmpxchg_double_slabVM_BUG_ON (!irqs_disabled ());#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE )if (s->flags & __CMPXCHG_DOUBLE) { -if (cmpxchg_double (&slub_data (page)->freelist, &slub_data (page)->counters, - freelist_old, counters_old, - freelist_new, counters_new)) -return 1 ; +if (cmpxchg_double (&slub_data (page)->freelist, + &slub_data (page)->counters, + freelist_old, counters_old, + freelist_new, counters_new)) +return 1 ; } else#endif {slab_lock (page);if (slub_data (page)->freelist == freelist_old && -slub_data (page)->counters == counters_old) { +slub_data (page)->counters == counters_old) {slub_data (page)->freelist = freelist_new;slub_data (page)->counters = counters_new;slab_unlock (page);
@@ -407,11 +409,12 @@ static inline bool cmpxchg_double_slab(s void *freelist_new, unsigned long counters_new, const char *n) { + struct slub_data *sd = slub_data(page);#if defined(CONFIG_SLUB_ATTEMPT_CMPXCHG_DOUBLE )if (s->flags & __CMPXCHG_DOUBLE) { - if (cmpxchg_double(&slub_data(page)->freelist, &slub_data(page)->counters, -freelist_old ,counters_old , - freelist_new, counters_new)) +if (cmpxchg_double (&sd->freelist, &sd->counters, + freelist_old, counters_old, + freelist_new, counters_new))return 1 ; } else#endif
@@ -420,10 +423,10 @@ static inline bool cmpxchg_double_slab(slocal_irq_save (flags);slab_lock (page); -if (slub_data (page)->freelist == freelist_old && -slub_data (page)->counters == counters_old) { -slub_data (page)->freelist = freelist_new; -slub_data (page)->counters = counters_new; +if (sd->freelist == freelist_old && + sd->counters == counters_old) { + sd->freelist = freelist_new; + sd->counters = counters_new;slab_unlock (page);local_irq_restore (flags);return 1 ;
@@ -859,7 +862,8 @@ static int check_slab(struct kmem_cache }if (slub_data (page)->inuse >slub_data (page)->objects) {slab_err (s, page,"inuse %u > max %u" , - s->name,slub_data (page)->inuse,slub_data (page)->objects); + s->name,slub_data (page)->inuse, +slub_data (page)->objects);return 0 ; }/* Slab_pad_check fixes things up after itself */
@@ -890,7 +894,8 @@ static int on_freelist(struct kmem_cache }else {slab_err (s, page,"Freepointer corrupt" );slub_data (page)->freelist =NULL ; -slub_data (page)->inuse =slub_data (page)->objects; +slub_data (page)->inuse = +slub_data (page)->objects;slab_fix (s,"Freelist cleared" );return 0 ; }
@@ -913,7 +918,8 @@ static int on_freelist(struct kmem_cache }if (slub_data (page)->inuse !=slub_data (page)->objects - nr) {slab_err (s, page,"Wrong object count. Counter is %d but " -"counted were %d" ,slub_data (page)->inuse,slub_data (page)->objects - nr); +"counted were %d" ,slub_data (page)->inuse, +slub_data (page)->objects - nr);slub_data (page)->inuse =slub_data (page)->objects - nr;slab_fix (s,"Object count adjusted." ); }
@@ -1550,7 +1556,7 @@ static inline void *acquire_slab(struct {void *freelist;unsigned long counters; -struct page new; +struct slub_data new;/* * Zap the freelist and set the frozen bit.
@@ -1795,8 +1801,8 @@ static void deactivate_slab(struct kmem_enum slab_modes l =M_NONE , m =M_NONE ;void *nextfree;int tail =DEACTIVATE_TO_HEAD ; -struct page new; -struct page old; +struct slub_data new; +struct slub_data old;if (slub_data (page)->freelist) {stat (s,DEACTIVATE_REMOTE_FREES );
@@ -1819,13 +1825,13 @@ static void deactivate_slab(struct kmem_ prior =slub_data (page)->freelist; counters =slub_data (page)->counters;set_freepointer (s, freelist, prior); -slub_data (&new)->counters = counters; -slub_data (&new)->inuse--; -VM_BUG_ON (!slub_data (&new)->frozen); + new.counters = counters; + new.inuse--; +VM_BUG_ON (!new.frozen); }while (!__cmpxchg_double_slab (s, page, prior, counters, - freelist,slub_data (&new)->counters, + freelist, new.counters,"drain percpu freelist" )); freelist = nextfree;
@@ -1847,24 +1853,24 @@ static void deactivate_slab(struct kmem_*/ redo: -slub_data (&old)->freelist =slub_data (page)->freelist; -slub_data (&old)->counters =slub_data (page)->counters; -VM_BUG_ON (!slub_data (&old)->frozen); + old.freelist =slub_data (page)->freelist; + old.counters =slub_data (page)->counters; +VM_BUG_ON (!old.frozen);/* Determine target state of the slab */ -slub_data (&new)->counters =slub_data (&old)->counters; + new.counters = old.counters;if (freelist) { -slub_data (&new)->inuse--; -set_freepointer (s, freelist,slub_data (&old)->freelist); -slub_data (&new)->freelist = freelist; + new.inuse--; +set_freepointer (s, freelist, old.freelist); + new.freelist = freelist; }else -slub_data (&new)->freelist =slub_data (&old)->freelist; + new.freelist = old.freelist; -slub_data (&new)->frozen =0 ; + new.frozen =0 ; -if (!slub_data (&new)->inuse && n->nr_partial > s->min_partial) +if (!new.inuse && n->nr_partial > s->min_partial) m =M_FREE ; -else if (slub_data (&new)->freelist) { +else if (new.freelist) { m =M_PARTIAL ;if (!lock) { lock =1 ;
@@ -1913,8 +1919,8 @@ redo: l = m;if (!__cmpxchg_double_slab (s, page, -slub_data (&old)->freelist,slub_data (&old)->counters, -slub_data (&new)->freelist,slub_data (&new)->counters, + old.freelist, old.counters, + new.freelist, new.counters,"unfreezing slab" )) goto redo;
@@ -1943,8 +1949,8 @@ static void unfreeze_partials(struct kmestruct page *page, *discard_page =NULL ;while ((page = c->partial)) { -struct page new; -struct page old; +struct slub_data new; +struct slub_data old; c->partial = page->next;
@@ -1959,21 +1965,21 @@ static void unfreeze_partials(struct kmedo { -slub_data (&old)->freelist =slub_data (page)->freelist; -slub_data (&old)->counters =slub_data (page)->counters; + old.freelist =slub_data (page)->freelist; + old.counters =slub_data (page)->counters;VM_BUG_ON (!slub_data (&old)->frozen); -slub_data (&new)->counters =slub_data (&old)->counters; -slub_data (&new)->freelist =slub_data (&old)->freelist; + new.counters = old.counters; + new.freelist = old.freelist;slub_data (&new)->frozen =0 ; }while (!__cmpxchg_double_slab (s, page, -slub_data (&old)->freelist,slub_data (&old)->counters, -slub_data (&new)->freelist,slub_data (&new)->counters, + old.freelist, old.counters, + new.freelist, new.counters,"unfreezing slab" )); -if (unlikely (!slub_data (&new)->inuse && n->nr_partial > s->min_partial)) { +if (unlikely (!new.inuse && n->nr_partial > s->min_partial)) { page->next = discard_page; discard_page = page; }else {
@@ -2225,7 +2231,7 @@ static inline bool pfmemalloc_match(stru*/ static inline void *get_freelist (struct kmem_cache *s,struct page *page) { -struct page new; +struct slub_data new;unsigned long counters;void *freelist;
@@ -2233,15 +2239,15 @@ static inline void *get_freelist(struct freelist =slub_data (page)->freelist; counters =slub_data (page)->counters; -slub_data (&new)->counters = counters; -VM_BUG_ON (!slub_data (&new)->frozen); + new.counters = counters; +VM_BUG_ON (!new.frozen); -slub_data (&new)->inuse =slub_data (page)->objects; -slub_data (&new)->frozen = freelist !=NULL ; + new.inuse =slub_data (page)->objects; + new.frozen = freelist !=NULL ; }while (!__cmpxchg_double_slab (s, page, freelist, counters, -NULL ,slub_data (&new)->counters, +NULL , new.counters,"get_freelist" ));return freelist;
@@ -2526,7 +2532,7 @@ static void __slab_free(struct kmem_cachvoid *prior;void **object = (void *)x;int was_frozen; -struct page new; +struct slub_data new;unsigned long counters;struct kmem_cache_node *n =NULL ;unsigned long uninitialized_var (flags );
@@ -2545,10 +2551,10 @@ static void __slab_free(struct kmem_cach prior =slub_data (page)->freelist; counters =slub_data (page)->counters;set_freepointer (s, object, prior); -slub_data (&new)->counters = counters; - was_frozen =slub_data (&new)->frozen; -slub_data (&new)->inuse--; -if ((!slub_data (&new)->inuse || !prior) && !was_frozen) { + new.counters = counters; + was_frozen = new.frozen; + new.inuse--; +if ((!new.inuse || !prior) && !was_frozen) {if (kmem_cache_has_cpu_partial (s) && !prior)
@@ -2558,7 +2564,7 @@ static void __slab_free(struct kmem_cach* We can defer the list move and instead * freeze it. */ -slub_data (&new)->frozen =1 ; + new.frozen =1 ; else { /* Needs to be taken off a list */
@@ -2578,7 +2584,7 @@ static void __slab_free(struct kmem_cach }while (!cmpxchg_double_slab (s, page, prior, counters, - object,slub_data (&new)->counters, + object, new.counters,"__slab_free" ));if (likely (!n)) {
@@ -2587,7 +2593,7 @@ static void __slab_free(struct kmem_cach* If we just froze the page then put it onto the * per cpu partial list. */ -if (slub_data (&new)->frozen && !was_frozen) { +if (new.frozen && !was_frozen) {put_cpu_partial (s, page,1 );stat (s,CPU_PARTIAL_FREE ); }
@@ -2600,7 +2606,7 @@ static void __slab_free(struct kmem_cachreturn ; } -if (unlikely (!slub_data (&new)->inuse && n->nr_partial > s->min_partial)) +if (unlikely (!new.inuse && n->nr_partial > s->min_partial)) goto slab_empty;/*
@@ -3423,7 +3429,8 @@ int kmem_cache_shrink(struct kmem_cache* list_lock. ->inuse here is the upper limit. */ list_for_each_entry_safe (page, t, &n->partial, lru) { -list_move (&page->lru, slabs_by_inuse +slub_data (page)->inuse); +list_move (&page->lru, slabs_by_inuse + +slub_data (page)->inuse);if (!slub_data (page)->inuse) n->nr_partial--; }
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerQuoted 6 lines · On Fri, 03 Jan 2014 10:01:47 -0800 Dave Hansen <[email protected]> wrote:
This is a minor update from the last version. The most notable thing is that I was able to demonstrate that maintaining the cmpxchg16 optimization has _some_ value.
Otherwise, the code changes are just a few minor cleanups.
Quoted 7 lines
---
SLUB depends on a 16-byte cmpxchg for an optimization which allows it to not disable interrupts in its fast path. This optimization has some small but measurable benefits:
http://lkml.kernel.org/r/[email protected]
So really the only significant benefit from the cmpxchg16 is with cache-cold eight-byte kmalloc/kfree? 8% faster in this case? But with cache-hot kmalloc/kfree the benefit of cmpxchg16 is precisely zero.
This is really weird and makes me suspect a measurement glitch.
Even if this 8% is real, it's unclear that it's worth all the complexity the cmpxchg16 adds.
It would be really useful (hint :)) if we were to know exactly where that 8% is coming from - perhaps it's something which is not directly related to the cmpxchg16, and we can fix it separately.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 14 lines · Andrew Morton
Quoted 12 lines · On Fri, 03 Jan 2014 10:01:47 -0800 Dave Hansen <[email protected]> wrote:
This is a minor update from the last version. The most notable thing is that I was able to demonstrate that maintaining the cmpxchg16 optimization has _some_ value.
Otherwise, the code changes are just a few minor cleanups.
---
SLUB depends on a 16-byte cmpxchg for an optimization which allows it to not disable interrupts in its fast path. This optimization has some small but measurable benefits:
Quoted 5 lines
Quoted 1 line
http://lkml.kernel.org/r/[email protected]So really the only significant benefit from the cmpxchg16 is with cache-cold eight-byte kmalloc/kfree? 8% faster in this case? But with cache-hot kmalloc/kfree the benefit of cmpxchg16 is precisely zero.
Hello,
I guess that cmpxchg16 is not used in this cache-hot kmalloc/kfree test, because kfree would be done in free fast-path. In this case, this_cpu_cmpxchg_double() would be called, so you cannot find any effect of cmpxchg16.
Thanks.
Quoted 16 lines
This is really weird and makes me suspect a measurement glitch.
Even if this 8% is real, it's unclear that it's worth all the complexity the cmpxchg16 adds.
It would be really useful (hint :)) if we were to know exactly where that 8% is coming from - perhaps it's something which is not directly related to the cmpxchg16, and we can fix it separately.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 8 lines · Joonsoo Kim
Quoted 7 lines · Andrew Morton
Quoted 5 lines · On Fri, 03 Jan 2014 10:01:47 -0800 Dave Hansen <[email protected]> wrote:
SLUB depends on a 16-byte cmpxchg for an optimization which allows it to not disable interrupts in its fast path. This optimization has some small but measurable benefits:
http://lkml.kernel.org/r/[email protected]
Quoted 8 lines
Quoted 3 lines
So really the only significant benefit from the cmpxchg16 is with cache-cold eight-byte kmalloc/kfree? 8% faster in this case? But with cache-hot kmalloc/kfree the benefit of cmpxchg16 is precisely zero.
I guess that cmpxchg16 is not used in this cache-hot kmalloc/kfree test, because kfree would be done in free fast-path. In this case,
this_cpu_cmpxchg_double()would be called, so you cannot find any effect of cmpxchg16.
That's a good point. I also confirmed this theory with the free_{fast,slow}path slub counters. So, I ran another round of tests.
One important difference from the last round: I'm now writing to each allocation. I originally did this so that I could store the allocations in a linked-list, but I also realized that it's important. It's rare in practice to do an allocation and not write _something_ to it. This change adds a bit of cache pressure which changed the results pretty substantially.
I tested 4 cases, all of these on the "cache-cold kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):
http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.png
There are a few important takeaways here:
1. The double-cmpxchg optimization has a measurable benefit
2. 64-byte 'struct page' is faster than the 56-byte one independent of the cmpxchg optimization. Maybe because foo/sizeof(struct page) is then a simple shift.
3. My new code is probably _slightly_ slower than the existing code, but still has the huge space savings
4. All of these deltas are greatly magnified here and are hard or impossible to demonstrate in practice.
Why does the double-cmpxchg help? The extra cache references that it takes to go out and touch the paravirt structures and task struct to disable interrupts in the spinlock cases start to show up and hurt our allocation rates by about 30%. This advantage starts to evaporate when there is more noise in the caches, or when we start to run the tests across more cores.
But the real question here is whether we can shrink 'struct page'. The existing (64-byte struct page) slub code wins on allocations under 256b by as much as 5% (the 32-byte kmalloc()), but my new code wins on allocations over 1k. 4k allocations just happen to be the most common on my systems, and they're also very near the "sweet spot" for the new code. But, the delta here is _much_ smaller that it was in the spinlock vs. cmpxchg cases. This advantage also evaporates when we run things across more cores or in less synthetic benchmarks.
I also explored that 5% hit that my code caused in the 32-byte allocation case. It looked to me to be mostly explained by the code that I added. There were more instructions executed and the cycles-per-instruction went down. This looks to be mostly due to a ~15% increase in branch misses, probably from the increased code size and complexity.
This is the perf stat output for a run doing 16.8M kmalloc(32)/kfree()'s:
vanilla:
Quoted 2 lines
883,412 LLC-loads # 0.296 M/sec [39.76%]
566,546 LLC-load-misses # 64.13% of all LL-cache hits [39.98%]patched:
Quoted 2 lines
556,751 LLC-loads # 0.186 M/sec [39.86%]
339,106 LLC-load-misses # 60.91% of all LL-cache hits [39.72%]My best guess is that most of the LLC references are going out and touching the struct pages for slab management. It's why we see such a big change.
Andrew Morton
maintainerQuoted 28 lines · On Fri, 10 Jan 2014 12:52:32 -0800 Dave Hansen <[email protected]> wrote:
Quoted 8 lines · Joonsoo Kim
Quoted 7 lines · Andrew Morton
Quoted 5 lines · On Fri, 03 Jan 2014 10:01:47 -0800 Dave Hansen <[email protected]> wrote:
SLUB depends on a 16-byte cmpxchg for an optimization which allows it to not disable interrupts in its fast path. This optimization has some small but measurable benefits:
http://lkml.kernel.org/r/[email protected]Quoted 8 lines
Quoted 3 lines
So really the only significant benefit from the cmpxchg16 is with cache-cold eight-byte kmalloc/kfree? 8% faster in this case? But with cache-hot kmalloc/kfree the benefit of cmpxchg16 is precisely zero.
I guess that cmpxchg16 is not used in this cache-hot kmalloc/kfree test, because kfree would be done in free fast-path. In this case,
this_cpu_cmpxchg_double()would be called, so you cannot find any effect of cmpxchg16.That's a good point. I also confirmed this theory with the free_{fast,slow}path slub counters. So, I ran another round of tests.
One important difference from the last round: I'm now writing to each allocation. I originally did this so that I could store the allocations in a linked-list, but I also realized that it's important. It's rare in practice to do an allocation and not write _something_ to it. This change adds a bit of cache pressure which changed the results pretty substantially.
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.png
So we're converging on the most complex option. argh.
Quoted 10 lines
There are a few important takeaways here:
1. The double-cmpxchg optimization has a measurable benefit
2. 64-byte 'struct page' is faster than the 56-byte one independent of the cmpxchg optimization. Maybe because foo/sizeof(struct page) is then a simple shift.
3. My new code is probably _slightly_ slower than the existing code, but still has the huge space savings
4. All of these deltas are greatly magnified here and are hard or impossible to demonstrate in practice.
Quoted 4 lines
Why does the double-cmpxchg help? The extra cache references that it takes to go out and touch the paravirt structures and task struct to disable interrupts in the spinlock cases start to show up and hurt our allocation rates by about 30%.
So all this testing was performed in a VM? If so, how much is that likely to have impacted the results?
Quoted 24 lines
This advantage starts to evaporate when
there is more noise in the caches, or when we start to run the tests across more cores.But the real question here is whether we can shrink 'struct page'. The existing (64-byte struct page) slub code wins on allocations under 256b by as much as 5% (the 32-byte
kmalloc()), but my new code wins on allocations over 1k. 4k allocations just happen to be the most common on my systems, and they're also very near the "sweet spot" for the new code. But, the delta here is _much_ smaller that it was in the spinlock vs. cmpxchg cases. This advantage also evaporates when we run things across more cores or in less synthetic benchmarks.I also explored that 5% hit that my code caused in the 32-byte allocation case. It looked to me to be mostly explained by the code that I added. There were more instructions executed and the cycles-per-instruction went down. This looks to be mostly due to a ~15% increase in branch misses, probably from the increased code size and complexity.
This is the perf stat output for a run doing 16.8M kmalloc(32)/kfree()'s:
vanilla:Quoted 2 lines
883,412 LLC-loads # 0.296 M/sec [39.76%] 566,546 LLC-load-misses # 64.13% of all LL-cache hits [39.98%]
Quoted 7 lines
patched:
Quoted 2 lines
556,751 LLC-loads # 0.186 M/sec [39.86%] 339,106 LLC-load-misses # 60.91% of all LL-cache hits [39.72%]My best guess is that most of the LLC references are going out and touching the struct pages for slab management. It's why we see such a big change.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 7 lines · Andrew Morton
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.pngSo we're converging on the most complex option. argh.
Yeah, looks that way.
Quoted 2 lines
So all this testing was performed in a VM? If so, how much is that likely to have impacted the results?
Nope, none of it was in a VM. All the results here are from bare-metal.
Quoted 10 lines · Dave Hansen
Quoted 7 lines · Andrew Morton
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.pngSo we're converging on the most complex option. argh.
Yeah, looks that way.
Seems like a reasonable compromise between memory usage and allocation speed.
Christoph?
Pekka
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 8 lines · Pekka Enberg
Quoted 7 lines · Dave Hansen
Quoted 6 lines · Andrew Morton
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.png
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
So we're converging on the most complex option. argh.
Yeah, looks that way.
Seems like a reasonable compromise between memory usage and allocation speed.
Christoph?
Fundamentally I think this is good. I need to look at the details but I am only going to be able to do that next week when I am back in the office.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 12 lines · Christoph Lameter
Quoted 10 lines · Pekka Enberg
Quoted 9 lines · Dave Hansen
Quoted 7 lines · Andrew Morton
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.pngSo we're converging on the most complex option. argh.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Yeah, looks that way.
Seems like a reasonable compromise between memory usage and allocation speed.
Christoph?
Fundamentally I think this is good. I need to look at the details but I am only going to be able to do that next week when I am back in the office.
Hello,
I have another guess about the performance result although I didn't look at these patches in detail. I guess that performance win of 64-byte sturct on small allocations can be caused by low latency when accessing slub's metadata, that is, struct page.
Following is pages per slab via '/proc/slabinfo'.
size pages per slab
...
256 1 512 1 1024 2 2048 4 4096 8 8192 8
We only touch one struct page on small allocation.
In 64-byte case, we always use one cacheline for touching struct page, since it is aligned to cacheline size. However, in 56-byte case, we possibly use two cachelines because struct page isn't aligned to cacheline size.
This aspect can change on large allocation cases. For example, consider 4096-byte allocation case. In 64-byte case, it always touches 8 cachelines for metadata, however, in 56-byte case, it touches 7 or 8 cachelines since 8 struct page occupies 8 * 56 bytes memory, that is, 7 cacheline size.
This guess may be wrong, so if you think it wrong, please ignore it. :)
And I have another opinion on this patchset. Diminishing struct page size will affect other usecases beside the slub. As we know, Dave found this by doing sequential 'dd'. I think that it may be the best case for 56-byte case. If we randomly touch the struct page, this un-alignment can cause regression since touching the struct page will cause two cachline misses. So, I think that it is better to get more benchmark results to this patchset for convincing ourselves. If possible, how about asking Fengguang to run whole set of his benchmarks before going forward?
Thanks.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 52 lines · Joonsoo Kim
Quoted 12 lines · Christoph Lameter
Quoted 10 lines · Pekka Enberg
Quoted 9 lines · Dave Hansen
Quoted 7 lines · Andrew Morton
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.pngSo we're converging on the most complex option. argh.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Yeah, looks that way.
Seems like a reasonable compromise between memory usage and allocation speed.
Christoph?
Fundamentally I think this is good. I need to look at the details but I am only going to be able to do that next week when I am back in the office.
Hello,
I have another guess about the performance result although I didn't look at these patches in detail. I guess that performance win of 64-byte sturct on small allocations can be caused by low latency when accessing slub's metadata, that is, struct page.
Following is pages per slab via '
/proc/slabinfo'.size pages per slab
...256 1 512 1 1024 2 2048 4 4096 8 8192 8We only touch one struct page on small allocation.
In 64-byte case, we always use one cacheline for touching struct page, since it is aligned to cacheline size. However, in 56-byte case, we possibly use two cachelines because struct page isn't aligned to cacheline size.This aspect can change on large allocation cases. For example, consider 4096-byte allocation case. In 64-byte case, it always touches 8 cachelines for metadata, however, in 56-byte case, it touches 7 or 8 cachelines since 8 struct page occupies 8 * 56 bytes memory, that is, 7 cacheline size.
This guess may be wrong, so if you think it wrong, please ignore it. :)
Quoted 8 lines
And I have another opinion on this patchset. Diminishing struct page size will affect other usecases beside the slub. As we know, Dave found this by doing sequential 'dd'. I think that it may be the best case for 56-byte case. If we randomly touch the struct page, this un-alignment can cause regression since touching the struct page will cause two cachline misses. So, I think that it is better to get more benchmark results to this patchset for convincing ourselves. If possible, how about asking Fengguang to run whole set of his benchmarks before going forward?
Cc'ing him.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 6 lines
Quoted 4 lines
So, I think
that it is better to get more benchmark results to this patchset for convincing ourselves. If possible, how about asking Fengguang to run whole set of his benchmarks before going forward?Cc'ing him.
My pleasure. Is there a git tree for the patches? Git trees are most convenient for running automated tests and bisects.
Thanks,
Fengguang
Quoted 9 lines · Fengguang Wu
Quoted 6 lines
Quoted 4 lines
So, I think
that it is better to get more benchmark results to this patchset for convincing ourselves. If possible, how about asking Fengguang to run whole set of his benchmarks before going forward?Cc'ing him.
My pleasure. Is there a git tree for the patches? Git trees are most convenient for running automated tests and bisects.
Here's a branch:
https://github.com/hansendc/linux/tree/slub-reshrink-for-Fengguang-20140113
My patches are not broken out in there, but that's all the code that needs to get tested.
Quoted 4 lines · Joonsoo Kim
We only touch one struct page on small allocation.
In 64-byte case, we always use one cacheline for touching struct page, since it is aligned to cacheline size. However, in 56-byte case, we possibly use two cachelines because struct page isn't aligned to cacheline size.
I think you're completely correct that this can _happen_, but I'm a bit unconvinced that what you're talking about is the thing which dominates the results. I'm sure it plays a role, but the tests I was doing were doing tens of millions of allocations and touching a _lot_ of 'struct pages'. I would not expect these effects to be observable across such a large sample of pages.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
One easy way to shrink struct page is to simply remove the feature. The patchset looked a bit complicated and does many other things.
Subject: slub: Remove struct page alignment restriction by dropping cmpxchg_double on struct page fields
Remove the logic that will do cmpxchg_doubles on struct page fields with the requirement of double word alignment. This allows struct page to shrink by 8 bytes.
include/linux/mm_types.h +1 -15
@@ -73,18 +73,12 @@ struct page { }; union { -#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE ) && \ - defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE ) -/* Used for cmpxchg_double in slub */ -unsigned long counters; -#else /* * Keep _count separate from slub cmpxchg_double data. * As the rest of the double word is protected by * slab_lock but _count is not. */ unsigned counters; -#endif struct {
@@ -195,15 +189,7 @@ struct page {#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS int _last_cpupid;#endif -} -/* -* The struct page can be forced to be double word aligned so that atomic ops -* on double words work. The SLUB allocator can make use of such a feature. -*/ -#ifdef CONFIG_HAVE_ALIGNED_STRUCT_PAGE -__aligned(2 * sizeof(unsigned long)) -#endif -; +};struct page_frag {struct page *page;
mm/slub.c +20 -78
164 lines not on this page · raw message
arch/Kconfig +0 -8
@@ -289,14 +289,6 @@ config HAVE_RCU_TABLE_FREE config ARCH_HAVE_NMI_SAFE_CMPXCHG bool -config HAVE_ALIGNED_STRUCT_PAGE - bool - help - This makes sure that struct pages are double word aligned and that - e.g. the SLUB allocator can perform double word atomic operations - on a struct page for better performance. However selecting this - might increase the size of a struct page by a word. - config HAVE_CMPXCHG_LOCAL bool
arch/s390/Kconfig +0 -1
@@ -102,7 +102,6 @@ config S390 select GENERIC_FIND_FIRST_BIT select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL - select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_TRACEHOOK
arch/x86/Kconfig +0 -1
@@ -77,7 +77,6 @@ config X86 select HAVE_PERF_USER_STACK_DUMP select HAVE_DEBUG_KMEMLEAK select ANON_INODES - select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_CMPXCHG_LOCAL select HAVE_CMPXCHG_DOUBLE select HAVE_ARCH_KMEMCHECK
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 2 lines · Christoph Lameter
One easy way to shrink struct page is to simply remove the feature. The patchset looked a bit complicated and does many other things.
Sure. There's a clear path if you only care about 'struct page' size, or if you only care about making the slub fast path as fast as possible.
We've got three variables, though:
1. slub fast path speed
2. space overhead from 'struct page'
3. code complexity.
Arranged in three basic choices:
1. Big 'struct page', fast, medium complexity code
2. Small 'struct page', slow, lowest complexity
3. Small 'struct page', fast, highest complexity, risk of new code
The question is what we should do by _default_, and what we should be recommending for our customers via the distros. Are you saying that you think we should completely rule out even having option 1 in mainline?
Quoted 9 lines · Dave Hansen
Quoted 2 lines · Christoph Lameter
One easy way to shrink struct page is to simply remove the feature. The patchset looked a bit complicated and does many other things.
Sure. There's a clear path if you only care about 'struct page' size, or if you only care about making the slub fast path as fast as possible.
We've got three variables, though:1. slub fast path speed
The fast path does use this_cpu_cmpxchg_double which is something different from a cmpxchg_double and its not used on struct page.
Quoted 4 lines
Arranged in three basic choices:
1. Big 'struct page', fast, medium complexity code
2. Small 'struct page', slow, lowest complexity
The numbers that I see seem to indicate that a big struct page means slow.
Quoted 3 lines
The question is what we should do by _default_, and what we should be recommending for our customers via the distros. Are you saying that you think we should completely rule out even having option 1 in mainline?
If option 1 is slower than option 2 then we do not need it.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 6 lines · Christoph Lameter
Quoted 4 lines · Dave Hansen
Quoted 2 lines · Christoph Lameter
One easy way to shrink struct page is to simply remove the feature. The patchset looked a bit complicated and does many other things.
Quoted 8 lines
Quoted 5 lines
Sure. There's a clear path if you only care about 'struct page' size, or if you only care about making the slub fast path as fast as possible.
We've got three variables, though:1. slub fast path speed
The fast path does use
this_cpu_cmpxchg_doublewhich is something different from acmpxchg_doubleand its not used on struct page.
Yeah, I'm confusing the two. I might as well say: "slub speed when touching 'struct page'"
Quoted 6 lines
Quoted 4 lines
Arranged in three basic choices:
1. Big 'struct page', fast, medium complexity code
2. Small 'struct page', slow, lowest complexityThe numbers that I see seem to indicate that a big struct page means slow.
This was a really tight loop where the caches are really hot, but it did show the large 'struct page' winning:
http://sr71.net/~dave/intel/slub/slub-perf-20140109.png
As I said in the earlier description, the paravirt code doing interrupt disabling was what really hurt the two spinlock cases.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 7 lines · Dave Hansen
This was a really tight loop where the caches are really hot, but it did show the large 'struct page' winning:
http://sr71.net/~dave/intel/slub/slub-perf-20140109.pngAs I said in the earlier description, the paravirt code doing interrupt disabling was what really hurt the two spinlock cases.
Hrm... Ok. in that case the additional complexity may be justified.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Dave: Please resend the newest form of the patchset. I do not have patch 0 and 1 and I do not see them on linux-mm.
Quoted 12 lines · Christoph Lameter
Quoted 10 lines · Pekka Enberg
Quoted 9 lines · Dave Hansen
Quoted 7 lines · Andrew Morton
Quoted 5 lines
I tested 4 cases, all of these on the "cache-cold
kfree()" case. The first 3 are with vanilla upstream kernel source. The 4th is patched with my new slub code (all single-threaded):http://www.sr71.net/~dave/intel/slub/slub-perf-20140109.pngSo we're converging on the most complex option. argh.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Yeah, looks that way.
Seems like a reasonable compromise between memory usage and allocation speed.
Christoph?
Fundamentally I think this is good. I need to look at the details but I am only going to be able to do that next week when I am back in the office.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>