2012-11-08 23 views
14

Ho notato che quando si contendono i futux di Linux, il sistema impiega MOLTO tempo negli spinlock. Ho notato che questo è un problema anche quando i futex non vengono utilizzati direttamente, ma anche quando si chiamano malloc/free, rand, le chiamate mutex glib e altre chiamate di sistema/libreria che effettuano chiamate al futex. Esiste lo ANY modo di sbarazzarsi di questo comportamento?Utilizzo elevato della CPU di sistema quando si contende il futex

Sto usando CentOS 6.3 con kernel 2.6.32-279.9.1.el6.x86_64. Ho anche provato l'ultimo kernel stabile 3.6.6 scaricato direttamente da kernel.org.

Originariamente, il problema si verificava su un server 24-core con 16 GB di RAM. Il processo ha 700 thread. I dati raccolti con "perf record" mostrano che lo spinlock è chiamato dal futex chiamato da __lll_lock_wait_private e __lll_unlock_wake_private, e sta consumando il 50% del tempo della CPU. Quando ho interrotto il processo con gdb, i backtraces hanno mostrato che le chiamate a __lll_lock_wait_private __lll_unlock_wake_private sono fatte da malloc e gratuite.

Stavo cercando di ridurre il problema, quindi ho scritto un semplice programma che mostra che sono davvero i futex a causare il problema dello spinlock.

Inizio 8 thread, con ogni thread nel seguente modo:

//... 
    static GMutex *lMethodMutex = g_mutex_new(); 
    while (true) 
    { 
     static guint64 i = 0; 
     g_mutex_lock (lMethodMutex); 
     // Perform any operation in the user space that needs to be protected. 
     // The operation itself is not important. It's the taking and releasing 
     // of the mutex that matters. 
     ++i; 
     g_mutex_unlock (lMethodMutex); 
    } 
    //... 

Sto facendo funzionare questo su una macchina a 8-core, con un sacco di RAM.

Utilizzando "top", ho osservato che la macchina è inattiva al 10%, 10% nella modalità utente e 90% nella modalità di sistema.

Utilizzando "top perf", ho osservato quanto segue:

50.73% [kernel]    [k] _spin_lock 
11.13% [kernel]    [k] hpet_msi_next_event 
    2.98% libpthread-2.12.so  [.] pthread_mutex_lock 
    2.90% libpthread-2.12.so  [.] pthread_mutex_unlock 
    1.94% libpthread-2.12.so  [.] __lll_lock_wait 
    1.59% [kernel]    [k] futex_wake 
    1.43% [kernel]    [k] __audit_syscall_exit 
    1.38% [kernel]    [k] copy_user_generic_string 
    1.35% [kernel]    [k] system_call 
    1.07% [kernel]    [k] schedule 
    0.99% [kernel]    [k] hash_futex 

mi aspetterei questo codice di spendere un po ' tempo nella spinlock, dato che il codice futex deve acquisire la coda futex attesa. Mi aspetto anche che il codice passi del tempo nel sistema, poiché in questo frammento di codice c'è pochissimo codice in esecuzione nello spazio utente. Tuttavia, il 50% del tempo trascorso nello spinlock sembra essere eccessivo, specialmente quando questo tempo di cpu è necessario per fare altro lavoro utile.

+1

Si potrebbe voler dire alcune parole su quale comportamento si vorrebbe vedere. Sento che questo non è completamente chiaro. – NPE

+0

L'uso di un mutex o di un futex per incrementare simultaneamente una variabile come nell'esempio precedente è un po 'sciocco, in quanto ciò può essere fatto direttamente con un incremento atomico (da qualche parte da 50 a 500 volte più efficiente). Nel codice "reale", cioè il codice che effettivamente fa qualcosa, trovo che la congestione e il tempo sprecato girano su dettagli piuttosto illeggibili. Il codice reale non compete per un blocco da una mezza dozzina di thread alla volta. – Damon

+1

Originariamente, ho notato che questo era un problema anche quando i futex non vengono chiamati direttamente dal codice utente; questo accade quando si chiamano chiamate malloc/free, rand, glib mutex e altre chiamate di sistema/libreria che effettuano chiamate al futex. Il frammento di codice fornito nella descrizione del problema è solo per dimostrare l'insorgenza del problema e in nessun modo rappresenta un lavoro utile. In effetti, il codice tra le chiamate a mutex può essere qualsiasi codice utente. –

risposta

3

Ho avuto anche problemi simili. La mia esperienza è che potresti notare un impatto sulle prestazioni o addirittura dei deadlock quando blocchi e sblocchi molto, a seconda della versione di libc e di molte altre cose oscure (ad esempio chiamate a fork() come here).

This guy risolto i suoi problemi di prestazioni passando a tcmalloc, che potrebbe essere una buona idea comunque a seconda del caso d'uso. Potrebbe valere la pena anche per te.

Per me, ho visto un deadlock riproducibile quando ho avuto più thread che eseguivano un sacco di blocchi e sblocco. Stavo usando un rootfs di Debian 5.0 (sistema embedded) con una libc dal 2010, e il problema è stato risolto aggiornando a Debian 6.0.

+0

Ho provato jemalloc e il problema non si sta verificando più. Questo non è sorprendente, dal momento che jemalloc si basa molto meno sul blocco delle arene rispetto a glibc. Questo, tuttavia, non risolve completamente il problema, poiché la causa principale del problema è che lo spinlock del futex è tenuto per troppo tempo, facendo sì che tutti gli altri thread in esecuzione si accumulino aspettando che lo spinlock venga rilasciato (come dimostrato dal mio piccolo frammento di codice nella descrizione originale del problema). –