optimise sbitmap deferred clear
Subsystems (2)
cover optimise sbitmap deferred clear
In short, sbitmap_deferred_clear() lights up pretty much in my profiler, so these optimisations gave me ~1% more t-put. All the heavy stuff is in [2/2]. Would love someones eye to check it.
I also want to replace cmpxchg() in that function with a single atomic and, that's slightly lighter and also transfers it from lock-free to wait-free, that's pretty neat.
The problem is that apparently there is a non-atomic_t atomic and, and atomic_t is unsigned int but all the bitmap do unsigned long. Advice is welcome.
Pavel Begunkov (2):
sbitmap: optimise sbitmap_deferred_clear()
sbitmap: remove swap_lock
include/linux/sbitmap.h | 5 ----- lib/sbitmap.c | 21 +++++++-------------- 2 files changed, 7 insertions(+), 19 deletions(-)
Signature · 1 line
2.24.0
PATCH 1/2 sbitmap: optimise sbitmap_deferred_clear()
Because of spinlocks and atomics sbitmap_deferred_clear() have to reload &sb->map[index] on each access even though the map address won't change. Hint it by explicitly caching it in a variable.
--- lib/sbitmap.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
lib/sbitmap.c +7 -6
@@ -14,30 +14,31 @@*/ static inline bool sbitmap_deferred_clear (struct sbitmap *sb,int index) { +struct sbitmap_word *map = &sb->map[index];unsigned long mask, val;bool ret = false;unsigned long flags; -spin_lock_irqsave (&sb->map[index].swap_lock, flags); +spin_lock_irqsave (&map->swap_lock, flags); -if (!sb->map[index].cleared) +if (!map->cleared) goto out_unlock;/* * First get a stable cleared mask, setting the old mask to 0. */ - mask =xchg (&sb->map[index].cleared,0 ); + mask =xchg (&map->cleared,0 );/* * Now clear the masked bits in our free word */ do { - val = sb->map[index].word; - }while (cmpxchg (&sb->map[index].word, val, val & ~mask) != val); + val = map->word; + }while (cmpxchg (&map->word, val, val & ~mask) != val); ret = true; out_unlock: -spin_unlock_irqrestore (&sb->map[index].swap_lock, flags); +spin_unlock_irqrestore (&map->swap_lock, flags);return ret; }
Signature · 1 line
2.24.0
PATCH 2/2 sbitmap: remove swap_lock
In reply to Pavel Begunkovmap->swap_lock serialises concurrent calls to sbitmap_deferred_clear(), however that function is already works in atomic fashion and guarantees to not loose bits while applying map->cleared bitmask.
Remove spinlocking in sbitmap_deferred_clear(). For a one-threaded tag allocation heavy test on top of nullblk it yields ~1.0-1.5% t-put increase, and according to perf 3% -> 1.5% cycle reduction of sbitmap_get().
--- include/linux/sbitmap.h | 5 ----- lib/sbitmap.c | 14 +++----------- 2 files changed, 3 insertions(+), 16 deletions(-)
include/linux/sbitmap.h +0 -5
@@ -32,11 +32,6 @@ struct sbitmap_word {* @cleared: word holding cleared bits */ unsigned long cleared____cacheline_aligned_in_smp ; - -/** -* @swap_lock: Held while swapping word <-> cleared -*/ -spinlock_t swap_lock; }____cacheline_aligned_in_smp ;/**
lib/sbitmap.c +3 -11
@@ -16,13 +16,9 @@ static inline bool sbitmap_deferred_clear(struct sbitmap *sb, int index) {struct sbitmap_word *map = &sb->map[index];unsigned long mask, val; -bool ret = false; -unsigned long flags; -spin_lock_irqsave (&map->swap_lock, flags); - -if (!map->cleared) - goto out_unlock; +if (!READ_ONCE (map->cleared)) +return false;/* * First get a stable cleared mask, setting the old mask to 0.
@@ -36,10 +32,7 @@ static inline bool sbitmap_deferred_clear(struct sbitmap *sb, int index) val = map->word; }while (cmpxchg (&map->word, val, val & ~mask) != val); - ret = true; -out_unlock: -spin_unlock_irqrestore (&map->swap_lock, flags); -return ret; +return true; } int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,
@@ -81,7 +74,6 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,for (i =0 ; i < sb->map_nr; i++) { sb->map[i].depth =min (depth, bits_per_word); depth -= sb->map[i].depth; -spin_lock_init (&sb->map[i].swap_lock); }return 0 ; }
Signature · 1 line
2.24.0