[RFC PATCH v1 07/50] mm/slab: Use simpler Fisher-Yates shuffle

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In addition to using reciprocal_scale rather than %, use
the initialize-while-shuffling form of Fisher-Yates.

Rather than swapping list[i] and list[rand] immediately after
initializing list[i] = i, copy list[i] = list[rand] and then
initialize list[rand] = i.

Note that 0 <= rand <= i, so if rand == i, the first step copies
uninitialized memory to itself before the second step initializes it.

This whole pre-computed shuffle list algorithm really needs a more
extensive overhaul.  It's basically a very-special-purpose PRNG
created to amortize the overhead of get_random_int().  But there
are more efficient ways to use the 32 random bits that returns
than just choosing a random starting point modulo cachep->num.

Signed-off-by: George Spelvin <lkml@xxxxxxx>
Cc: Thomas Garnier <thgarnie@xxxxxxxxxx>
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: linux-mm@xxxxxxxxx
---
 mm/slab.c        | 25 +++++++++----------------
 mm/slab_common.c | 15 +++++++--------
 2 files changed, 16 insertions(+), 24 deletions(-)

diff --git a/mm/slab.c b/mm/slab.c
index a89633603b2d7..d9499d54afa59 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2404,7 +2404,7 @@ static bool freelist_state_initialize(union freelist_init_state *state,
 	} else {
 		state->list = cachep->random_seq;
 		state->count = count;
-		state->pos = rand % count;
+		state->pos = reciprocal_scale(rand, count);
 		ret = true;
 	}
 	return ret;
@@ -2418,20 +2418,13 @@ static freelist_idx_t next_random_slot(union freelist_init_state *state)
 	return state->list[state->pos++];
 }
 
-/* Swap two freelist entries */
-static void swap_free_obj(struct page *page, unsigned int a, unsigned int b)
-{
-	swap(((freelist_idx_t *)page->freelist)[a],
-		((freelist_idx_t *)page->freelist)[b]);
-}
-
 /*
  * Shuffle the freelist initialization state based on pre-computed lists.
  * return true if the list was successfully shuffled, false otherwise.
  */
 static bool shuffle_freelist(struct kmem_cache *cachep, struct page *page)
 {
-	unsigned int objfreelist = 0, i, rand, count = cachep->num;
+	unsigned int objfreelist = 0, i, count = cachep->num;
 	union freelist_init_state state;
 	bool precomputed;
 
@@ -2456,14 +2449,14 @@ static bool shuffle_freelist(struct kmem_cache *cachep, struct page *page)
 	 * Later use a pre-computed list for speed.
 	 */
 	if (!precomputed) {
-		for (i = 0; i < count; i++)
-			set_free_obj(page, i, i);
-
 		/* Fisher-Yates shuffle */
-		for (i = count - 1; i > 0; i--) {
-			rand = prandom_u32_state(&state.rnd_state);
-			rand %= (i + 1);
-			swap_free_obj(page, i, rand);
+		set_free_obj(page, 0, 0);
+		for (i = 1; i < count; i++) {
+			unsigned int rand = prandom_u32_state(&state.rnd_state);
+
+			rand = reciprocal_scale(rand, i + 1);
+			set_free_obj(page, i, get_free_obj(page, rand));
+			set_free_obj(page, rand, i);
 		}
 	} else {
 		for (i = 0; i < count; i++)
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 0d95ddea13b0d..67908fc842d98 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -1349,17 +1349,16 @@ EXPORT_SYMBOL(kmalloc_order_trace);
 static void freelist_randomize(struct rnd_state *state, unsigned int *list,
 			       unsigned int count)
 {
-	unsigned int rand;
 	unsigned int i;
 
-	for (i = 0; i < count; i++)
-		list[i] = i;
-
 	/* Fisher-Yates shuffle */
-	for (i = count - 1; i > 0; i--) {
-		rand = prandom_u32_state(state);
-		rand %= (i + 1);
-		swap(list[i], list[rand]);
+	list[0] = 0;
+	for (i = 1; i < count; i++) {
+		unsigned int rand = prandom_u32_state(state);
+
+		rand = reciprocal_scale(rand, i + 1);
+		list[i] = list[rand];
+		list[rand] = i;
 	}
 }
 
-- 
2.26.0





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux