Kontekst zmienia się znacznie wolniej w nowych jądrach Linuksa

99

Chcemy zaktualizować system operacyjny na naszych serwerach z Ubuntu 10.04 LTS do Ubuntu 12.04 LTS. Niestety, wydaje się, że opóźnienie uruchomienia wątku, który stał się gotowy do uruchomienia, znacznie wzrosło z jądra 2.6 do jądra 3.2. W rzeczywistości trudno uwierzyć w liczby latencji, które otrzymujemy.

Powiem bardziej szczegółowo o teście. Mamy program, który uruchamia dwa wątki. Pierwszy wątek pobiera bieżący czas (w taktach przy użyciu RDTSC), a następnie co sekundę sygnalizuje zmienną warunkową. Drugi wątek czeka na zmienną warunkową i budzi się, gdy zostanie zasygnalizowana. Następnie pobiera aktualny czas (w taktach przy użyciu RDTSC). Różnica między czasem w drugim wątku a czasem w pierwszym wątku jest obliczana i wyświetlana na konsoli. Po tym drugi wątek jeszcze raz czeka na zmienną warunku. Zostanie ponownie zasygnalizowany przez pierwszy wątek po około drugim przejściu.

Tak więc, w skrócie, otrzymujemy w rezultacie wątek do komunikacji między wątkami za pośrednictwem pomiaru opóźnień zmiennych warunkowych raz na sekundę.

W jądrze 2.6.32 to opóźnienie jest gdzieś w granicach 2,8-3,5 nas, co jest rozsądne. W jądrze 3.2.0 to opóźnienie wzrosło do około 40-100 us. Wykluczyłem wszelkie różnice sprzętowe między dwoma hostami. Działają na identycznym sprzęcie (procesory z podwójnym gniazdem X5687 {Westmere-EP} pracujące z częstotliwością 3,6 GHz z hiperwątkowością, prędkością skoku i wyłączonymi wszystkimi stanami C). Aplikacja testowa zmienia koligację wątków, aby uruchamiać je na niezależnych rdzeniach fizycznych tego samego gniazda (tj. Pierwszy wątek jest uruchamiany na rdzeniu 0, a drugi na rdzeniu 1), więc nie ma odbijania wątków rdzenie lub odbijanie / komunikacja między gniazdami.

Jedyną różnicą między tymi dwoma hostami jest to, że na jednym działa Ubuntu 10.04 LTS z jądrem 2.6.32-28 (szybka zmiana kontekstu), a na drugim działa najnowszy Ubuntu 12.04 LTS z jądrem 3.2.0-23 (powolny kontekst Skrzynka z wyłącznikami). Wszystkie ustawienia BIOS i sprzęt są identyczne.

Czy zaszły jakieś zmiany w jądrze, które mogłyby wyjaśnić to absurdalne spowolnienie czasu, w którym zaplanowano uruchomienie wątku?

Aktualizacja: Jeśli chcesz uruchomić test na swoim hoście i kompilacji systemu Linux, opublikowałem kod do wklejania do wglądu. Połącz z:

g++ -O3 -o test_latency test_latency.cpp -lpthread

Uruchom z (zakładając, że masz co najmniej dwurdzeniowy box):

./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1

Aktualizacja 2 : Po długich przeszukiwaniu parametrów jądra, postach na temat zmian w jądrze i osobistych badaniach, zorientowałem się, na czym polega problem i opublikowałem rozwiązanie jako odpowiedź na to pytanie.

Michael Goldshteyn
źródło
1
zgaduję, ale może zmiana parametru z /proc/sys/kernel/*może zadziałać? Jeśli znajdziesz coś, co działa, umieść tę konfigurację /etc/sysctl.conflub plik, /etc/sysctl.d/aby zachować ją po ponownym uruchomieniu.
Carlos Campderrós
1
Porównałem / proc / sys / kernel między dwoma hostami, ale nie widzę żadnych znaczących różnic, szczególnie w jakichkolwiek elementach konfiguracyjnych związanych z planowaniem.
Michael Goldshteyn
Jak przez mgłę przypominam sobie plotkę, że RDTSC niekoniecznie jest odpowiednio zsynchronizowana między rdzeniami, ale spodziewałbym się, że gdyby był to problem, zobaczyłbyś odwrócenie czasu. Czy próbowałeś manipulować podobieństwami, aby uruchomić oba wątki na tym samym rdzeniu i zobaczyć, co się stanie?
David Given
W przypadku rdzeni Intela ta nowa funkcja RDTSC działa bezbłędnie na rdzeniach, zwłaszcza rdzeniach tego samego procesora (tj. Tego samego gniazda). Co ciekawe, jeśli oba wątki są uruchomione na tym samym rdzeniu, opóźnienia spadają do 4-10 nas na nowszym jądrze i ok. 3 nas na starszym jądrze.
Michael Goldshteyn
Tylko ogólny komentarz - poleganie na synchronizacji TSC jest w najlepszym razie niejasne, chociaż w twoim konkretnym przypadku, ponieważ używasz dwóch rdzeni na jednym fizycznym chipie, powinno to faktycznie działać.
twalberg

Odpowiedzi:

95

Rozwiązanie problemu z wydajnością budzenia złego wątku w najnowszych jądrach wiąże się z przejściem na intel_idlesterownik cpuidle z acpi_idle, sterownik używany w starszych jądrach. Niestety intel_idlesterownik ignoruje konfigurację BIOS-u użytkownika dla stanów C i tańczy do własnej melodii . Innymi słowy, nawet jeśli całkowicie wyłączysz wszystkie stany C w BIOS-ie twojego komputera (lub serwera), ten sterownik nadal będzie wymuszał ich włączenie w okresach krótkiej nieaktywności, które prawie zawsze mają miejsce, chyba że syntetyczny test porównawczy zużywający cały rdzeń (np. ) biegnie. Możesz monitorować przejścia stanu C, a także inne przydatne informacje związane z częstotliwościami procesora, używając wspaniałego narzędzia Google i7z na większości kompatybilnych urządzeń.

Aby zobaczyć, który sterownik cpuidle jest obecnie aktywny w twojej konfiguracji, po prostu znajdź current_driverplik w cpuidlesekcji w /sys/devices/system/cpunastępujący sposób:

cat /sys/devices/system/cpu/cpuidle/current_driver

Jeśli chcesz, aby Twój nowoczesny system operacyjny Linux miał jak najmniejsze opóźnienie przełączania kontekstu, dodaj następujące parametry rozruchowe jądra, aby wyłączyć wszystkie te funkcje oszczędzania energii:

W systemie Ubuntu 12.04 możesz to zrobić, dodając je do GRUB_CMDLINE_LINUX_DEFAULTwpisu /etc/default/grubi uruchamiając update-grub. Parametry rozruchu do dodania to:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

Oto krwawe szczegóły dotyczące tego, co robią trzy opcje rozruchu:

Ustawienie intel_idle.max_cstatena zero albo przywróci sterownik cpuidle do acpi_idle(przynajmniej zgodnie z dokumentacją opcji), albo całkowicie go wyłączy. Na moim komputerze jest całkowicie wyłączony (tj. Wyświetlenie current_driverpliku w /sys/devices/system/cpu/cpuidledaje wyjście none). W tym przypadku druga opcja rozruchu nie processor.max_cstate=0jest konieczna. Jednak dokumentacja stwierdza, że ​​ustawienie max_cstate na zero dla intel_idlesterownika powinno przywrócić system operacyjny do acpi_idlesterownika. Dlatego na wszelki wypadek wstawiłem drugą opcję rozruchu.

processor.max_cstateOpcja określa maksymalny stan C dla acpi_idlekierowcy do zera, mam nadzieję, że wyłączenie go również. Nie mam systemu, na którym mógłbym to przetestować, ponieważ intel_idle.max_cstate=0całkowicie wyłącza sterownik cpuidle na całym dostępnym mi sprzęcie. Jednakże, jeśli instalacja nie powracają z wami intel_idle, aby acpi_idletylko z pierwszej opcji bootowania, proszę dać mi znać, jeśli druga opcja, processor.max_cstatenie to, co zostało udokumentowane zrobić w komentarzach, tak że mogę zaktualizować tę odpowiedź.

Wreszcie ostatni z trzech parametrów, idle=pollto prawdziwa świnia mocy. Wyłączy C1 / C1E, co spowoduje usunięcie ostatniego pozostałego bitu opóźnienia kosztem znacznie większego zużycia energii, więc używaj tego tylko wtedy, gdy jest to naprawdę konieczne. Dla większości będzie to przesada, ponieważ opóźnienie C1 * nie jest aż tak duże. Używając mojej aplikacji testowej działającej na sprzęcie, który opisałem w pierwotnym pytaniu, opóźnienie wzrosło z 9 do 3 nas. Jest to z pewnością znacząca redukcja w przypadku aplikacji wrażliwych na duże opóźnienia (np. Handel finansowy, precyzyjna telemetria / śledzenie, akwizycja danych o wysokiej częstotliwości itp.), Ale może nie być warta poniesionych strat energii elektrycznej w przypadku zdecydowanej większości aplikacje komputerowe. Jedynym sposobem, aby mieć pewność, jest profilowanie poprawy wydajności aplikacji w porównaniu z

Aktualizacja:

Po dodatkowych testach z różnymi idle=*parametrami odkryłem, że ustawienie idlena, mwaitjeśli jest obsługiwane przez twój sprzęt, jest znacznie lepszym pomysłem. Wydaje się, że użycie MWAIT/MONITORinstrukcji pozwala procesorowi wejść do C1E bez żadnego zauważalnego opóźnienia dodawania do czasu wybudzania wątku. Dzięki idle=mwaittemu uzyskasz niższą temperaturę procesora (w porównaniu do idle=poll), mniejsze zużycie energii i nadal zachowasz doskonałe niskie opóźnienia pętli bezczynności odpytywania. Dlatego mój zaktualizowany zalecany zestaw parametrów rozruchowych dla niskiego opóźnienia wątku procesora w oparciu o te ustalenia to:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=mwait

Użycie idle=mwaitzamiast zamiast idle=pollmoże również pomóc w zainicjowaniu Turbo Boost (pomagając procesorowi utrzymać się poniżej jego TDP [Thermal Design Power]) i hiperwątkowości (dla której MWAIT jest idealnym mechanizmem, który nie zużywa całego fizycznego rdzenia, a jednocześnie unikanie wyższych stanów C). Jednak nie zostało to jeszcze udowodnione w testach, które będę nadal robić.

Aktualizacja 2:

Opcja mwaitidle została usunięta z nowszych jąder 3.x (podziękowania dla użytkownika ck_ za aktualizację). To daje nam dwie możliwości:

idle=halt- Powinien działać równie dobrze mwait, ale przetestuj, aby upewnić się, że tak jest w przypadku twojego sprzętu. HLTInstrukcja jest niemal równoznaczne z MWAITz nutą państwowej 0. Problem tkwi w tym, że przerwanie jest wymagane, aby wydostać się z państwa HLT, podczas zapisu do pamięci (lub przerwanie) może być używany, aby wydostać się z państwa MWAIT. W zależności od tego, czego używa jądro Linuksa w swojej pętli bezczynności, może to zwiększyć wydajność MWAIT. Tak więc, jak powiedziałem, przetestuj / profil i zobacz, czy spełnia twoje potrzeby dotyczące opóźnienia ...

i

idle=poll - Opcja o najwyższej wydajności kosztem mocy i ciepła.

Michael Goldshteyn
źródło
Przepraszam, ale dlaczego spodziewałeś się, że stany C będą zarządzane przez oprogramowanie układowe? Stany zawieszenia są stanami środowiska uruchomieniowego i są zarządzane przez system operacyjny zgodnie z projektem. Jak odkryłeś, jeśli nie chcesz wstrzymać działania, nie używaj go.
Andy Ross
6
Przepraszamy, ale C stwierdza, że ​​EIST i C1E można wyłączyć w BIOS-ie. Oczekuję, że system operacyjny będzie respektować moje ustawienia BIOS. Jest to szczególnie prawdziwe, biorąc pod uwagę horrendalne narzędzia i dokumentację w tym przypadku.
Michael Goldshteyn
4
Może wyłączony przez twoje biografie. Nie znam niczego w odpowiedniej specyfikacji, która tego wymaga. Przepraszam, ale "oczekiwanie" czegokolwiek z BIOS-u będzie Cię wielokrotnie gryźć. Najlepsze, co oprogramowanie układowe może zrobić w nowoczesnym komputerze, to nic. Przykro mi, że byłeś zaskoczony, ale szczerze mówiąc to jest błąd użytkownika. Twój punkt odniesienia mierzył czasy zawieszenia i wznowienia.
Andy Ross
19
Jedną z ról wyboru funkcji BIOS jest włączanie / wyłączanie urządzeń. W niektórych przypadkach te wybory są wymuszone w systemie operacyjnym (np. USB na płycie głównej, eSATA i NIC). W innych przypadkach oczekuje się, że system operacyjny będzie szanował Twoje życzenia (np. EIST, stany C, hiperwątkowość, wyłączenie wykonywania, AES-NI, wirtualizacja itp.). BIOS zapewnia pojedynczą centralną powierzchnię wyboru urządzenia / funkcji, która jest neutralna dla systemu operacyjnego. Pozwala to użytkownikowi zainstalować wiele (być może bardzo różnych) systemów operacyjnych na hoście, które używają tych samych funkcji sprzętowych. Jednak ta odpowiedź jest subiektywna, więc będę musiał się zgodzić, aby się nie zgodzić.
Michael Goldshteyn
1
idle = mwait nie jest już obsługiwane w ostatnim jądrze 3.x. lkml.org/lkml/2013/2/10/21 jakieś alternatywne porady?
ck_
8

Być może to, co stało się wolniejsze, to futex, element składowy zmiennych warunkowych. To rzuci trochę światła:

strace -r ./test_latency 0 1 &> test_latency_strace & sleep 8 && killall test_latency

następnie

for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done

który pokaże mikrosekundy potrzebne dla interesujących wywołań systemowych, posortowane według czasu.

W jądrze 2.6.32

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000140 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000129 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000124 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000119 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000106 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000103 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000102 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000125 futex(0x7f98ce4c0b88, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000042 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000038 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000030 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000029 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 0
 0.000028 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000027 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000018 futex(0x7fff82f0ec3c, FUTEX_WAKE_PRIVATE, 1) = 0
nanosleep
 0.000027 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, 0x7fff82f0eb40) = ? ERESTART_RESTARTBLOCK (To be restarted)
 0.000017 nanosleep({1, 0}, {1, 0}) = 0
rt_sig
 0.000045 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000040 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000038 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000033 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigaction(SIGRT_1, {0x37f8c052b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000027 rt_sigaction(SIGRTMIN, {0x37f8c05370, [], SA_RESTORER|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000023 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0

W jądrze 3.1.9

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000129 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000126 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000122 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000115 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000114 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000112 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000109 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000139 futex(0x3f8b8f2fb0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000043 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000041 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000036 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
nanosleep
 0.000025 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000022 nanosleep({1, 0}, {0, 3925413}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
 0.000021 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
rt_sig
 0.000045 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000044 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000043 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000040 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000038 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000037 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigaction(SIGRT_1, {0x3f892067b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000024 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigaction(SIGRTMIN, {0x3f89206720, [], SA_RESTORER|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0

Znalazłem ten 5-letni raport o błędzie, który zawiera test wydajności „ping ponga”, który porównuje

  1. jednowątkowy mutex libpthread
  2. Zmienna warunkowa libpthread
  3. zwykłe stare sygnały Unixowe

Musiałem dodać

#include <stdint.h>

w celu kompilacji, co zrobiłem za pomocą tego polecenia

g++ -O3 -o condvar-perf condvar-perf.cpp -lpthread -lrt

W jądrze 2.6.32

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29085 us; per iteration:   29 ns / 9.4e-05 context switches.
c.v. ping-pong test   elapsed:  4771993 us; per iteration: 4771 ns / 4.03 context switches.
signal ping-pong test elapsed:  8685423 us; per iteration: 8685 ns / 4.05 context switches.

W jądrze 3.1.9

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26811 us; per iteration:   26 ns / 8e-06 context switches.
c.v. ping-pong test   elapsed: 10930794 us; per iteration: 10930 ns / 4.01 context switches.
signal ping-pong test elapsed: 10949670 us; per iteration: 10949 ns / 4.01 context switches.

Dochodzę do wniosku, że przełączanie kontekstu między jądrem 2.6.32 a 3.1.9 rzeczywiście zwolniło, chociaż nie tak bardzo, jak obserwuje się w jądrze 3.2. Zdaję sobie sprawę, że to jeszcze nie odpowiada na twoje pytanie, będę dalej kopać.

Edycja: Odkryłem, że zmiana priorytetu procesu w czasie rzeczywistym (obu wątków) poprawia wydajność w wersji 3.1.9, aby dopasować ją do wersji 2.6.32. Jednak ustawienie tego samego priorytetu na 2.6.32 powoduje spowolnienie ... idź do figury - przyjrzę się temu bardziej.

Oto moje wyniki teraz:

W jądrze 2.6.32

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29629 us; per iteration:   29 ns / 0.000418 context switches.
c.v. ping-pong test   elapsed:  6225637 us; per iteration: 6225 ns / 4.1 context switches.
signal ping-pong test elapsed:  5602248 us; per iteration: 5602 ns / 4.09 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29049 us; per iteration:   29 ns / 0.000407 context switches.
c.v. ping-pong test   elapsed: 16131360 us; per iteration: 16131 ns / 4.29 context switches.
signal ping-pong test elapsed: 11817819 us; per iteration: 11817 ns / 4.16 context switches.
$ 

W jądrze 3.1.9

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26830 us; per iteration:   26 ns / 5.7e-05 context switches.
c.v. ping-pong test   elapsed: 12812788 us; per iteration: 12812 ns / 4.01 context switches.
signal ping-pong test elapsed: 13126865 us; per iteration: 13126 ns / 4.01 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    27025 us; per iteration:   27 ns / 3.7e-05 context switches.
c.v. ping-pong test   elapsed:  5099885 us; per iteration: 5099 ns / 4 context switches.
signal ping-pong test elapsed:  5508227 us; per iteration: 5508 ns / 4 context switches.
$ 
amdn
źródło
Uruchomiłem go na Fedorze i CentOS, nie mam Ubuntu. Opublikuję wyniki.
amdn
OK, uruchomiłem go na obu hostach (tj. I na różnych jądrach) i wyniki nie wykazują prawie żadnych różnic. Tak więc ten test nie wykazał żadnych różnic. Czas połączenia futex różni się na czwartym miejscu po przecinku - nieznaczny spadek wydajności. Eee, czekaj, czy liczby całkowite są w sekundach? Właśnie widziałem, że opublikowałeś swoje wyniki i są one podobne do moich ...
Michael Goldshteyn
Ok, to wyklucza implementację futexu - wróciliśmy do twojej teorii przełączania kontekstu ... nie krępuj się usunąć tę odpowiedź, ponieważ naprawdę należy do komentarzy ... Chciałem tylko mieć możliwość formatowania poleceń.
amdn
Tak, czasy są w sekundach ... wywołania do futex, które trwają dłużej niż sekundę, dotyczą wątku oczekującego na warunek.
amdn
A co, jeśli cokolwiek uzyskasz z wyników?
Michael Goldshteyn
1

Możesz również zobaczyć procesory klikające w dół w nowszych procesach i jądrach Linuksa ze względu na sterownik pstate , który jest oddzielny od c- States . Więc dodatkowo, aby to wyłączyć, należy wykonać następujący parametr jądra:

intel_pstate=disable

Kyle Brandt
źródło