2013-08-26 14 views
5

Sto eseguendo il porting di un codice in Windows e ho trovato che il threading è estremamente lento. L'attività richiede 300 secondi su Windows (con due xeon E5-2670 8 core 2.6ghz = 16 core) e 3,5 secondi su linux (xeon E5-1607 4 core 3ghz). Utilizzando vs2012 express.Porting di thread su Windows. Le sezioni critiche sono molto lente

Ho 32 thread che chiamano EnterCriticalSection(), eseguono un lavoro di 80 byte di un file std :: stack, LeaveCriticalSection e fanno un po 'di lavoro (250k di lavoro in totale).

Prima e dopo ogni chiamata di sezione critica, stampo l'ID del filetto e l'ora corrente.

  • Il tempo di attesa per il blocco di un singolo filo è ~ 160ms
  • a comparsa il lavoro dallo stack prende ~ 3 ms
  • congedo Calling prende ~ 3 ms
  • Il lavoro prende ~ 1 ms

(all'incirca lo stesso per Debug/Rilascio, Debug impiega un po 'di più. Mi piacerebbe essere in grado di profilare correttamente il codice: P)

Commentare la chiamata di lavoro rende l'intero processo di 2 secondi (ancora più di Linux).

Ho provato sia queryperformancecounter che timeGetTime, entrambi forniscono lo stesso risultato.

AFAIK il lavoro non effettua mai alcuna chiamata di sincronizzazione, ma non posso spiegare il rallentamento a meno che non lo faccia.

Non ho idea del motivo per cui copiare da una pila e chiamare pop richiede così tanto tempo. Un'altra cosa molto confusa è perché una chiamata a lasciare() richiede così tanto tempo.

Qualcuno può speculare sul perché funziona così lentamente?

Non avrei mai pensato che la differenza di processore avrebbe dato una differenza di prestazioni di 100 volte, ma potrebbe essere correlata a due CPU? (dovendo sincronizzare tra CPU separate rispetto ai core interni).

A proposito, sono a conoscenza di std :: thread ma voglio che il mio codice libreria funzioni con pre C++ 11.

modificare

//in a while(hasJobs) loop... 

EVENT qwe1 = {"lock", timeGetTime(), id}; 
events.push_back(qwe1); 

scene->jobMutex.lock(); 

EVENT qwe2 = {"getjob", timeGetTime(), id}; 
events.push_back(qwe2); 

hasJobs = !scene->jobs.empty(); 
if (hasJobs) 
{ 
    job = scene->jobs.front(); 
    scene->jobs.pop(); 
} 

EVENT qwe3 = {"gotjob", timeGetTime(), id}; 
events.push_back(qwe3); 

scene->jobMutex.unlock(); 

EVENT qwe4 = {"unlock", timeGetTime(), id}; 
events.push_back(qwe4); 

if (hasJobs) 
    scene->performJob(job); 

e la classe mutex, con roba linux #ifdef rimosso ...

CRITICAL_SECTION mutex; 

... 

Mutex::Mutex() 
{ 
    InitializeCriticalSection(&mutex); 
} 
Mutex::~Mutex() 
{ 
    DeleteCriticalSection(&mutex); 
} 
void Mutex::lock() 
{ 
    EnterCriticalSection(&mutex); 
} 
void Mutex::unlock() 
{ 
    LeaveCriticalSection(&mutex); 
} 
+0

Cosa stavi usando su Linux? Stai proteggendo solo l'accesso allo std :: stack con le sezioni critiche? – xanatos

+2

Dove stampate l'id del thread e l'ora corrente? – avakar

+0

Scegli una lingua. E questo sembra strano. Sicuramente * sembra * abbastanza semplice da consentire a [SSCCE] (http://www.sscce.org) di funzionare. Concordo che la mia esperienza su Windows è stata alquanto scialba rispetto a una distro Linux adeguatamente equipaggiata, ma questa sembra davvero una voragine piuttosto ampia. – WhozCraig

risposta

0

sembra che le vostre finestre le discussioni si trovano ad affrontare super-contesa. Sembrano totalmente serializzati. Hai circa 7ms di tempo di elaborazione totale nella sezione critica e 32 thread. Se tutti i thread sono in coda sul blocco, l'ultimo thread nella coda non verrà eseguito fino a dopo aver dormito per circa 217 ms. Questo non è troppo lontano dai 160 ms di tempo di attesa osservati.

Quindi, se i thread non hanno altro da fare che entrare nella sezione critica, non lavorare, quindi lasciare la sezione critica, questo è il comportamento che mi aspetterei.

Provare a caratterizzare il comportamento del profilo di linux e verificare se il comportamento del programma è reale e il confronto tra mele e mele.

+0

Sono d'accordo, sembra che stiano correndo in modo sequenziale (occasionalmente c'è una piccola differenza nell'ordine di esecuzione). Non esiste un codice per serializzarli e una copia e un pop di 80 byte dovrebbero essere WAY WAY più veloci del mio lavoro. Ciò che realmente non ha senso è il tempo impiegato per chiamare LeaveCriticalSection. Credo che la serializzazione sia sintomo di un problema di fondo con sezioni critiche. Verificherò comunque la contesa su linux. – jozxyqk

1

CRITICAL_SECTION di Window gira in un circuito chiuso quando lo inserisci per la prima volta. Non sospende il thread che ha chiamato EnterCriticalSection a meno che non sia trascorso un periodo sostanziale nel ciclo di spin. Quindi avere 32 discussioni contendenti per la stessa sezione critica brucerà e sprecherà un sacco di cicli della CPU. Prova invece un mutex (vedi CreateMutex).

+3

[Sospetto lo stesso] (http://stackoverflow.com/questions/18442574/porting-threads-to-windows-critical-sections-are-very-slow#comment27103010_18442574), ma non regolerei il numero di spin da più appropriato/più veloce di usare un mutex? – dyp

Problemi correlati