Quali sono le solite difficoltà nella programmazione e messa a punto simultanee di programmi concorrenti (modello a memoria condivisa)?

3

Posso elencare parte dei problemi che verranno visualizzati durante la scrittura di un'applicazione concorrente di dimensioni moderate con memoria condivisa:

  • Blocco della granularità
  • Scelta della primitiva di sincronizzazione
  • Numero di thread
  • Metodo per la scomposizione in thread (parallelismo dei dati, parallelismo dei task, organizzazione dei thread, ecc.)

Quali sono altri problemi simili?

    
posta Jay 20.06.2012 - 18:59
fonte

3 risposte

2

I due problemi più comuni nella mia esperienza di prendere una lezione sull'argomento sono stati il debug dei programmi e l'efficiente distribuzione delle risorse.

Il debug di un programma parallelo, in particolare su un sistema di thread gestito in modo indipendente, è sorprendentemente difficile. Non è deterministico, il che significa che il tuo programma può funzionare 999 volte su 1000 e che una volta fallisce solo perché qualcosa è arrivato nell'ordine sbagliato o il gestore thread non ha assegnato correttamente.

Anche gli errori di atomicità sono comuni, ecco perché i linguaggi funzionali stanno diventando popolari per gestire la concorrenza mentre stanno fuori dallo stato (non è che non abbiano uno stato, sia trasparente).

L'altro grande problema è la comunicazione. Questo rientra in due categorie: risorse e tempo. Resourcing fa riferimento a dimensioni della cache e allocazioni di memoria spesso limitate per singoli processi. I sistemi paralleli spesso vedono l'uso in applicazioni pesanti di dati, quindi il flusso e il confezionamento corretti di tali dati sono importanti. C'è anche il problema di affrontare il fatto che potrebbe non esserci un buon algoritmo che fa in modo efficiente quello che vuoi. Alcune attività non sono facilmente parallelizzabili e, anche se lo sono, la comunicazione in questione può rendere discutibile qualsiasi guadagno di velocità. Il tempo si riferisce ai tempi di comunicazione tra processori. Questo è meno un problema nei sistemi "multicore" rispetto a quelli distribuiti, ma è ancora un problema.

Potresti chiedere perché sto parlando di sistemi distribuiti. Bene, alcuni sistemi di "memoria condivisa" sono effettivamente distribuiti con particolari funzionalità che li fanno funzionare come memoria condivisa.

    
risposta data 20.06.2012 - 22:25
fonte
2

L'ultimo grande problema che ho riscontrato è stato lavorare attorno ai colli di bottiglia. Avevo molti campi a cui spesso facevano riferimento molti thread e, con il programma in esecuzione, avrei un utilizzo della CPU compreso tra l'8 e il 10 percento. Spero che i tuoi thread non interferiscano l'uno con l'altro in quel modo, ma ho passato molto tempo a mettere in pausa il mio debugger ea controllare chi era bloccato dove. Alla fine ho interrotto il blocco / i blocchi sincronizzati per osservare un numero minimo di campi e lavorare con le variabili locali quando possibile. (Ci sono molti motivi per altri per restare con le variabili locali. Questo era solo un altro.)

Ho anche finito per inserire blocchi di codice in thread separati. Questo potrebbe richiedere un po 'di CPU in sovraccarico, ma ho avuto 90-92 per cento disponibili. Il codice nel thread separato non doveva attendere che il thread padre fosse in attesa su un lock e non mantenesse il thread genitore mentre esso era in attesa su un lock.

E sono andato alla ricerca di strutture dati che fossero sia thread-safe che thread-efficient. È così che ho trovato la ConcurrentSkipListMap di Java.

Tutto il resto qui, mentre scrivo questo, è più importante, e forse non entrerai nel groviglio dei fili che ho fatto, ma, come ho detto, questo è il posto dove sono finito. In realtà è solo un ripasso dei problemi che hai elencato, ma ho affrontato dopo le decisioni che si supponeva fossero state fatte e implementate.

    
risposta data 20.06.2012 - 23:03
fonte
1

Tutto quanto sopra, oltre al fatto che si deve assumere una quantità arbitraria di tempo può passare per altri thread (remoti o meno) durante e tra l'esecuzione di una singola istruzione in un dato thread.

A meno che tu non possa lavorare con l'assembly, i linguaggi di livello superiore (specialmente Java e C #) rendono questo problema difficile da aggirare senza assembler in linea, "intrinseci" o un minimo di operazioni "atomizzate" esposte come funzioni.

Anche così, esistono istruzioni "atomiche" che falliscono spurie su alcune piattaforme, e quindi devono essere comunque utilizzate in un ciclo (che è la motivazione del comitato standard del C ++ dietro compare_exchange_strong e compare_exchange_weak ).

    
risposta data 24.07.2012 - 05:28
fonte

Leggi altre domande sui tag