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.
Możesz powiedzieć kilka słów o tym, jakie zachowanie chcesz zobaczyć. Uważam, że nie jest to całkowicie jasne. – NPE
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
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. –