ipc/sem.c: sem optimise undo list search
Nick Piggin [Wed, 16 Dec 2009 00:47:28 +0000 (16:47 -0800)]
Around a month ago, there was some discussion about an improvement of the
sysv sem algorithm: Most (at least: some important) users only use simple
semaphore operations, therefore it's worthwile to optimize this use case.

This patch:

Move last looked up sem_undo struct to the head of the task's undo list.
Attempt to move common entries to the front of the list so search time is
reduced.  This reduces lookup_undo on oprofile of problematic SAP workload
by 30% (see patch 4 for a description of SAP workload).

Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
Cc: Pierre Peiffer <peifferp@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

ipc/sem.c

index 2f2a479..cb0070e 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -962,17 +962,31 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
        return 0;
 }
 
-static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
+static struct sem_undo *__lookup_undo(struct sem_undo_list *ulp, int semid)
 {
-       struct sem_undo *walk;
+       struct sem_undo *un;
 
-       list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) {
-               if (walk->semid == semid)
-                       return walk;
+       list_for_each_entry_rcu(un, &ulp->list_proc, list_proc) {
+               if (un->semid == semid)
+                       return un;
        }
        return NULL;
 }
 
+static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
+{
+       struct sem_undo *un;
+
+       assert_spin_locked(&ulp->lock);
+
+       un = __lookup_undo(ulp, semid);
+       if (un) {
+               list_del_rcu(&un->list_proc);
+               list_add_rcu(&un->list_proc, &ulp->list_proc);
+       }
+       return un;
+}
+
 /**
  * find_alloc_undo - Lookup (and if not present create) undo array
  * @ns: namespace
@@ -1308,7 +1322,7 @@ void exit_sem(struct task_struct *tsk)
                if (IS_ERR(sma))
                        continue;
 
-               un = lookup_undo(ulp, semid);
+               un = __lookup_undo(ulp, semid);
                if (un == NULL) {
                        /* exit_sem raced with IPC_RMID+semget() that created
                         * exactly the same semid. Nothing to do.