2012-11-08 20 views
14

Zauważyłem, że kiedy futexy linuxowe są kontestowane, system spędza dużo czasu w spinlockach. Zauważyłem, że jest to problem, nawet gdy futeksy nie są używane bezpośrednio, ale także podczas wywoływania wywołań funkcji malloc/free, rand, glib mutex i innych wywołań systemowych/bibliotecznych, które wywołują wywołania futex. Czy istnieje sposób na pozbycie się tego zachowania?Wysokie użycie procesora w systemie podczas rywalizacji futex

Używam CentOS 6.3 z jądrem 2.6.32-279.9.1.el6.x86_64. Próbowałem także najnowszego stabilnego jądra 3.6.6 pobranego bezpośrednio z kernel.org.

Oryginalnie, problem wystąpił na serwerze 24-rdzeniowym z 16 GB pamięci RAM. Proces ma 700 wątków. Dane zebrane za pomocą "perf record" pokazują, że spinlock jest wywoływany z futex zwany z __lll_lock_wait_private i __lll_unlock_wake_private, i zżera 50% czasu procesora. Kiedy zatrzymałem proces z gdb, ślady śledzenia pokazały, że wywołania __lll_lock_wait_private __lll_unlock_wake_private są wykonywane z malloc i za darmo.

Próbowałem zmniejszyć problem, więc napisałem prosty program, który pokazuje, że to faktycznie futexy, które powodują problem spinlock.

start 8 wątków, z każdego wątku w następujący sposób:

//... 
    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); 
    } 
    //... 

Używam tego na maszynie 8-core, z dużą ilością pamięci RAM.

Korzystając z "góry", zauważyłem, że maszyna jest 10% bezczynności, 10% w trybie użytkownika i 90% w trybie systemowym.

Korzystanie perf „top”, zaobserwowałem następujące:

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 

Spodziewam się ten kod, aby spędzić trochęczas w spinlock, ponieważ kod futex musi nabyć kolejkę futex czekać. Oczekuję też, że kod poświęci trochę czasu w systemie, ponieważ w tym fragmencie kodu jest bardzo mało kodu uruchomionego w przestrzeni użytkownika. Jednak 50% czasu spędzonego na spinlock wydaje się być przesadne, szczególnie gdy ten czas procesora jest potrzebny do wykonania innej użytecznej pracy.

+1

Możesz powiedzieć kilka słów o tym, jakie zachowanie chcesz zobaczyć. Uważam, że nie jest to całkowicie jasne. – NPE

+0

Używanie muteksu lub futex do równoczesnej inkrementacji zmiennej, jak w powyższym przykładzie, jest nieco głupie, ponieważ można to zrobić bezpośrednio z przyrostem atomowym (gdzieś od 50 do 500 razy bardziej wydajnym). W "prawdziwym" kodzie, tj. W kodzie, który faktycznie coś robi, znajduję zatory i czas marnowany na przestawianie raczej nieczytelnych szczegółów. Prawdziwy kod nie konkuruje o blokadę z kilku wątków na raz. – Damon

+1

Początkowo zauważyłem, że jest to problem, nawet gdy futexy nie są wywoływane bezpośrednio z kodu użytkownika; dzieje się tak podczas wywoływania wywołań mutokodajnych malloc/free, rand, glib oraz innych wywołań systemowych/bibliotecznych, które wywołują wywołania futex. Fragment kodu podany w opisie problemu ma jedynie na celu wykazanie wystąpienia problemu i nie stanowi żadnej użytecznej pracy. W rzeczywistości kod pomiędzy wywołaniami muteksa może być dowolnym kodem użytkownika. –

Odpowiedz

3

Wystąpiłem również w podobnych sprawach. Z mojego doświadczenia wynika, że ​​podczas blokowania i odblokowywania możesz zobaczyć uderzenie wydajności, a nawet zakleszczenia, w zależności od wersji libc i wielu innych niejasnych rzeczy (np. Wywołania fork(), takie jak here).

This guy rozwiązał swoje problemy z wydajnością, przełączając się na tcmalloc, co może być dobrym pomysłem i tak w zależności od przypadku użycia. Warto spróbować także dla ciebie.

Dla mnie, widziałem powtarzalny zakleszczenie, gdy miałem wiele wątków robiących dużo blokowania i odblokowywania. Używałem rootfs Debiana 5.0 (system wbudowany) z biblioteką libc z 2010 roku, a problem został rozwiązany przez uaktualnienie do wersji 6.0 Debiana.

+0

Próbowałem jemalloc, a problem już się nie dzieje. Nie jest to zaskakujące, ponieważ jemalloc polega znacznie mniej na blokowaniu aren niż glibc. To jednak nie rozwiązuje całkowicie problemu, ponieważ podstawową przyczyną problemu jest to, że spinlock futex jest utrzymywany przez zbyt długi czas, powodując, że wszystkie pozostałe wątki wykonawcze piętrzą się czekając na zwolnienie blokady (o czym świadczy mój mały fragment kodu w oryginalnym opisie problemu). –