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

slob: reduce list scanning

The version of SLOB in -mm always scans its free list from the beginning,
which results in small allocations and free segments clustering at the
beginning of the list over time. This causes the average search to scan
over a large stretch at the beginning on each allocation.

By starting each page search where the last one left off, we evenly
distribute the allocations and greatly shorten the average search.

Without this patch, kernel compiles on a 1.5G machine take a large amount
of system time for list scanning. With this patch, compiles are within a
few seconds of performance of a SLAB kernel with no notable change in
system time.

Signed-off-by: Matt Mackall <mpm@selenic.com>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Matt Mackall and committed by
Linus Torvalds
d6269543 41f9dc5c

+16 -5
+16 -5
mm/slob.c
··· 293 293 static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) 294 294 { 295 295 struct slob_page *sp; 296 + struct list_head *prev; 296 297 slob_t *b = NULL; 297 298 unsigned long flags; 298 299 ··· 308 307 if (node != -1 && page_to_nid(&sp->page) != node) 309 308 continue; 310 309 #endif 310 + /* Enough room on this page? */ 311 + if (sp->units < SLOB_UNITS(size)) 312 + continue; 311 313 312 - if (sp->units >= SLOB_UNITS(size)) { 313 - b = slob_page_alloc(sp, size, align); 314 - if (b) 315 - break; 316 - } 314 + /* Attempt to alloc */ 315 + prev = sp->list.prev; 316 + b = slob_page_alloc(sp, size, align); 317 + if (!b) 318 + continue; 319 + 320 + /* Improve fragment distribution and reduce our average 321 + * search time by starting our next search here. (see 322 + * Knuth vol 1, sec 2.5, pg 449) */ 323 + if (free_slob_pages.next != prev->next) 324 + list_move_tail(&free_slob_pages, prev->next); 325 + break; 317 326 } 318 327 spin_unlock_irqrestore(&slob_lock, flags); 319 328