Il contesto cambia molto più lentamente nei nuovi kernel Linux


99

Stiamo cercando di aggiornare il sistema operativo sui nostri server da Ubuntu 10.04 LTS a Ubuntu 12.04 LTS. Sfortunatamente, sembra che la latenza per eseguire un thread che è diventato eseguibile sia notevolmente aumentata dal kernel 2.6 al kernel 3.2. In effetti, i numeri di latenza che stiamo ottenendo sono difficili da credere.

Lasciami essere più specifico sul test. Abbiamo un programma che esegue due thread. Il primo thread ottiene l'ora corrente (in tick utilizzando RDTSC) e quindi segnala una variabile di condizione una volta al secondo. Il secondo thread attende la variabile di condizione e si sveglia quando viene segnalato. Quindi ottiene l'ora corrente (in tick utilizzando RDTSC). La differenza tra l'ora nel secondo thread e l'ora nel primo thread viene calcolata e visualizzata nella console. Dopo questo il secondo thread attende ancora una volta la variabile di condizione. Verrà segnalato nuovamente dal primo thread dopo circa un secondo passaggio.

Quindi, in poche parole, otteniamo una comunicazione da thread a thread tramite la misurazione della latenza della variabile di condizione una volta al secondo.

Nel kernel 2.6.32, questa latenza è dell'ordine di 2.8-3.5 us, il che è ragionevole. Nel kernel 3.2.0, questa latenza è aumentata da qualche parte nell'ordine di 40-100 us. Ho escluso qualsiasi differenza di hardware tra i due host. Funzionano su hardware identico (processori X5687 {Westmere-EP} a doppio socket che funzionano a 3,6 GHz con hyperthreading, speedtep e tutti gli stati C disattivati). L'app di test modifica l'affinità dei thread per eseguirli su core fisici indipendenti dello stesso socket (ovvero, il primo thread viene eseguito su Core 0 e il secondo thread viene eseguito su Core 1), quindi non vi è alcun rimbalzo dei thread su core o rimbalzare / comunicazione tra i socket.

L'unica differenza tra i due host è che uno esegue Ubuntu 10.04 LTS con kernel 2.6.32-28 (la casella di commutazione rapida del contesto) e l'altro esegue l'ultima Ubuntu 12.04 LTS con kernel 3.2.0-23 (il contesto lento interruttore). Tutte le impostazioni e l'hardware del BIOS sono identici.

Sono state apportate modifiche al kernel che potrebbero spiegare questo ridicolo rallentamento del tempo necessario per la pianificazione dell'esecuzione di un thread?

Aggiornamento: se desideri eseguire il test sul tuo host e sulla build di Linux, ho pubblicato il codice in pastebin per la tua lettura. Compila con:

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

Esegui con (supponendo che tu abbia almeno una scatola dual-core):

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

Aggiornamento 2 : Dopo molte ricerche tra i parametri del kernel, post sulle modifiche al kernel e ricerche personali, ho capito qual è il problema e ho pubblicato la soluzione come risposta a questa domanda.


1
solo una supposizione, ma forse la modifica di un parametro da /proc/sys/kernel/*potrebbe funzionare? Se trovi qualcosa che funziona, inserisci quella configurazione /etc/sysctl.confo un file /etc/sysctl.d/per farlo persistere durante i riavvii.
Carlos Campderrós

1
Ho confrontato / proc / sys / kernel tra i due host, ma non vedo differenze significative, specialmente in qualsiasi elemento di configurazione relativo alla pianificazione.
Michael Goldshteyn

Ricordo vagamente una voce secondo cui RDTSC non è necessariamente sincronizzato correttamente tra i core, ma mi aspetterei che se questo fosse un problema vedresti un'inversione del tempo. Hai provato a manipolare le affinità per eseguire entrambi i thread sullo stesso core e vedere cosa succede?
David dato il

Sui core Intel questo nuovo, RDTSC funziona perfettamente tra i core, in particolare i core sulla stessa CPU (cioè, lo stesso socket). È interessante notare che se entrambi i thread vengono eseguiti sullo stesso core, le latenze scendono a 4-10 us sul kernel più recente e ca. 3 noi sul vecchio kernel.
Michael Goldshteyn

Solo un commento generale: affidarsi ai TSC per essere sincronizzati è incerto nella migliore delle ipotesi, anche se nel tuo caso specifico, dal momento che stai utilizzando due core su un chip fisico, dovrebbe effettivamente funzionare.
twalberg

Risposte:


95

La soluzione al problema delle prestazioni di risveglio dei thread difettosi nei kernel recenti ha a che fare con il passaggio al intel_idledriver cpuidle da acpi_idle, il driver utilizzato nei kernel più vecchi. Purtroppo, il intel_idledriver ignora la configurazione del BIOS dell'utente per gli stati C e balla al suo ritmo . In altre parole, anche se disabiliti completamente tutti gli stati C nel BIOS del tuo PC (o server), questo driver li forzerà comunque durante periodi di breve inattività, che si verificano quasi sempre a meno che un benchmark sintetico che consuma tutto il core (ad esempio, stress ) è in esecuzione. Puoi monitorare le transizioni di stato C, insieme ad altre informazioni utili relative alle frequenze del processore, utilizzando il meraviglioso strumento Google i7z sull'hardware più compatibile.

Per vedere quale driver cpuidle è attualmente attivo nella tua configurazione, seleziona semplicemente il current_driverfile nella cpuidlesezione /sys/devices/system/cpucome segue:

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

Se desideri che il tuo sistema operativo Linux moderno abbia la latenza di cambio di contesto più bassa possibile, aggiungi i seguenti parametri di avvio del kernel per disabilitare tutte queste funzionalità di risparmio energetico:

Su Ubuntu 12.04, puoi farlo aggiungendoli alla GRUB_CMDLINE_LINUX_DEFAULTvoce in /etc/default/grube quindi eseguendo update-grub. I parametri di avvio da aggiungere sono:

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

Ecco i dettagli cruenti su ciò che fanno le tre opzioni di avvio:

L'impostazione intel_idle.max_cstatea zero ripristinerà il tuo driver cpuidle acpi_idle(almeno secondo la documentazione dell'opzione) o lo disabiliterà completamente. Sulla mia scatola è completamente disabilitato (cioè, la visualizzazione del current_driverfile in /sys/devices/system/cpu/cpuidleproduce un output di none). In questo caso la seconda opzione di avvio processor.max_cstate=0non è necessaria. Tuttavia, la documentazione afferma che l'impostazione di max_cstate su zero per il intel_idledriver dovrebbe ripristinare il sistema operativo al acpi_idledriver. Pertanto, ho inserito la seconda opzione di avvio per ogni evenienza.

L' processor.max_cstateopzione imposta lo stato C massimo per il acpi_idledriver a zero, si spera anche disabilitandolo. Non ho un sistema su cui posso testarlo, perché intel_idle.max_cstate=0mette completamente fuori uso il driver cpuidle su tutto l'hardware a mia disposizione. Tuttavia, se la tua installazione ti riporta da intel_idlea acpi_idlecon solo la prima opzione di avvio, fammi sapere se la seconda opzione ha processor.max_cstatefatto ciò che è stato documentato fare nei commenti in modo che io possa aggiornare questa risposta.

Infine, l'ultimo dei tre parametri, idle=pollè un vero maiale di potere. Disattiverà C1 / C1E, che rimuoverà l'ultimo bit di latenza rimanente a scapito di un consumo energetico molto maggiore, quindi usalo solo quando è veramente necessario. Per la maggior parte questo sarà eccessivo, poiché la latenza C1 * non è poi così grande. Utilizzando la mia applicazione di test in esecuzione sull'hardware che ho descritto nella domanda originale, la latenza è passata da 9 a 3 noi. Questa è certamente una riduzione significativa per applicazioni altamente sensibili alla latenza (ad esempio, trading finanziario, telemetria / tracciamento ad alta precisione, acquisizione dati ad alta frequenza, ecc.), Ma potrebbe non valere la perdita di energia elettrica sostenuta per la stragrande maggioranza dei app desktop. L'unico modo per saperlo con certezza è profilare il miglioramento delle prestazioni della tua applicazione vs.

Aggiornare:

Dopo ulteriori test con vari idle=*parametri, ho scoperto che l'impostazione idlesu mwaitse supportata dal tuo hardware è un'idea molto migliore. Sembra che l'uso delle MWAIT/MONITORistruzioni consenta alla CPU di entrare in C1E senza che venga aggiunta alcuna latenza evidente al tempo di attivazione del thread. Con idle=mwait, otterrai temperature della CPU più basse (rispetto a idle=poll), meno consumo di energia e manterrai comunque le eccellenti basse latenze di un ciclo di polling inattivo. Pertanto, il mio set aggiornato di parametri di avvio consigliato per una bassa latenza di attivazione del thread della CPU in base a questi risultati è:

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

L'uso di idle=mwaitinvece di idle=pollpuò anche aiutare con l'avvio del Turbo Boost (aiutando la CPU a rimanere al di sotto del suo TDP [Thermal Design Power]) e hyperthreading (per cui MWAIT è il meccanismo ideale per non consumare un intero core fisico mentre allo stesso tempo tempo evitando gli stati C superiori). Tuttavia, ciò deve ancora essere dimostrato nei test, cosa che continuerò a fare.

Aggiornamento 2:

L' mwaitopzione idle è stata rimossa dai kernel 3.x più recenti (grazie all'utente ck_ per l'aggiornamento). Questo ci lascia con due opzioni:

idle=halt- Dovrebbe funzionare allo stesso modo mwait, ma verifica che sia così per il tuo hardware. L' HLTistruzione è quasi equivalente a una MWAITcon suggerimento di stato 0. Il problema sta nel fatto che è necessario un interrupt per uscire da uno stato HLT, mentre una scrittura in memoria (o interrupt) può essere utilizzata per uscire dallo stato MWAIT. A seconda di ciò che il kernel Linux utilizza nel suo ciclo di inattività, questo può rendere MWAIT potenzialmente più efficiente. Quindi, come ho detto, prova / profilo e vedi se soddisfa le tue esigenze di latenza ...

e

idle=poll - L'opzione più performante, a scapito di potenza e calore.


Scusa, ma perché ti aspettavi che gli stati C fossero gestiti dal firmware? Gli stati di sospensione sono stati di runtime e vengono gestiti dal sistema operativo in base alla progettazione. Come hai scoperto, se non vuoi la sospensione del runtime non usarla.
Andy Ross

6
Siamo spiacenti, ma gli stati C, EIST e C1E possono essere disattivati ​​nel BIOS. Mi aspetto che il sistema operativo rispetti le mie impostazioni del BIOS. Ciò è particolarmente vero, dati gli strumenti e la documentazione orrendi in questo caso.
Michael Goldshteyn

4
Spento tramite il tuo BIOS, forse. Non so nulla in una specifica pertinente che lo richieda. Scusa, ma "aspettarmi" qualcosa dal BIOS ti morderà ripetutamente. La cosa migliore che il firmware può fare in un PC moderno è niente. Mi dispiace che tu sia rimasto sorpreso, ma francamente questo è un errore dell'utente. Il tuo benchmark misurava i tempi di sospensione e ripresa.
Andy Ross

19
Uno dei ruoli della selezione delle funzionalità del BIOS è abilitare / disabilitare i dispositivi. In alcuni casi queste selezioni vengono forzate sul sistema operativo (ad esempio, USB sulla scheda madre, eSATA e NIC). In altri ci si aspetta che il sistema operativo rispetti i tuoi desideri (ad esempio, EIST, stati C, Hyperthreading, Execute Disable, AES-NI, Virtualization, ecc ...). Il BIOS fornisce un'unica superficie centrale per la selezione di dispositivi / funzionalità che è indipendente dal sistema operativo. Ciò consente all'utente di installare più sistemi operativi (forse molto diversi) sull'host che utilizzano tutti le stesse funzionalità hardware. Tuttavia, questa risposta è soggettiva, quindi dovrà accettare di non essere d'accordo.
Michael Goldshteyn

1
idle = mwait non è più supportato nel recente kernel 3.x lkml.org/lkml/2013/2/10/21 qualche consiglio alternativo?
ck_

8

Forse ciò che è diventato più lento è il futex, l'elemento costitutivo delle variabili di condizione. Questo farà luce:

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

poi

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

che mostrerà i microsecondi presi per le chiamate di sistema interessanti, ordinate per tempo.

Sul kernel 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

Sul kernel 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

Ho trovato questo bug report di 5 anni che contiene un test delle prestazioni "ping pong" che confronta

  1. mutex libpthread a thread singolo
  2. variabile di condizione libpthread
  3. semplici vecchi segnali Unix

Ho dovuto aggiungere

#include <stdint.h>

per compilare, cosa che ho fatto con questo comando

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

Sul kernel 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.

Sul kernel 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.

Concludo che tra il kernel 2.6.32 e il 3.1.9 il cambio di contesto è effettivamente rallentato, sebbene non tanto quanto si osserva nel kernel 3.2. Mi rendo conto che questo non risponde ancora alla tua domanda, continuerò a scavare.

Modifica: ho scoperto che la modifica della priorità in tempo reale del processo (entrambi i thread) migliora le prestazioni su 3.1.9 per abbinare 2.6.32. Tuttavia, impostare la stessa priorità su 2.6.32 lo fa rallentare ... vai a capire - esaminerò di più.

Ecco i miei risultati ora:

Sul kernel 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.
$ 

Sul kernel 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.
$ 

L'ho eseguito su Fedora e CentOS, non ho Ubuntu. Pubblicherò i miei risultati.
amdn

OK, l'ho eseguito su entrambi gli host (cioè e kernel diversi) ei risultati non mostrano quasi nessuna disparità. Quindi, questo test non ha evidenziato differenze. Il tempo di chiamata futex differisce nella quarta cifra decimale: una riduzione insignificante delle prestazioni. Ehm aspetta, i numeri interi sono in secondi? Ho appena visto che hai pubblicato i tuoi risultati e sembrano simili ai miei ...
Michael Goldshteyn

Ok, questo esclude l'implementazione del futex: siamo tornati alla tua teoria del cambio di contesto ... sentiti libero di eliminare questa risposta poiché appartiene davvero ai commenti ... Volevo solo la possibilità di formattare i comandi.
amdn

Sì, i tempi sono in secondi ... le chiamate a futex che durano più di un secondo sono per il thread in attesa della condizione.
amdn

Quindi, cosa raccogli dai risultati?
Michael Goldshteyn

1

Potresti anche vedere i processori che fanno clic in basso nei processi più recenti e nei kernel Linux a causa del driver pstate che è separato dagli stati c. Quindi, in aggiunta, per disabilitare questo, hai il seguente parametro del kernel:

intel_pstate=disable

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.