2015-04-21 15 views
5

ecco un esempio minimo di quello che mi preoccupaC++ OpenMP con shared_pointer

#include <iostream> 
#include <memory> 
#include"omp.h" 

class A{ 
    public: 
     A(){std::cout<<this<<std::endl;} 
}; 

int main(){ 
#pragma omp parallel for 
    for(unsigned int i=0;i<4;i++){ 
     std::shared_ptr<A> sim(std::make_shared<A>()); 
    } 
    for(unsigned int i=0;i<4;i++){ 
     std::shared_ptr<A> sim(std::make_shared<A>()); 
    } 
} 

Se corro quel codice un paio di volte io possa ottenere questo tipo di risultato:

0xea3308 
0xea32d8 
0xea3338 
0x7f39f80008c8 
0xea3338 
0xea3338 
0xea3338 
0xea3338 

Quello che ho capito è che il 4 ultimo output abbia sempre lo stesso numero di caratteri (8). Ma per qualche motivo accade (non sempre) che uno o più dei i primi quattro output contiene più (14) caratteri. Sembra che l'uso di open space cambi la "natura" del puntatore (questa è la mia comprensione ingenua). Ma questo comportamento è normale? Dovrei aspettarmi qualche strano comportamento?

EDIT

here è un test dal vivo che presenta lo stesso problema in una versione leggermente più complicata del codice

+5

un downvote di 5 secondi dopo la pubblicazione? perché ? – PinkFloyd

risposta

3

Questo comportamento è del tutto ragionevole, vediamo cosa succede.

loop seriale

In ogni iterazione che stai ricevendo uno A che viene creato sul mucchio, e uno è sempre distrutta. Queste operazioni sono ordinate in questo modo:

  1. costruzione
  2. distruzione
  3. costruzione
  4. distruzione
  5. ... (e così via) sono stati creati

Dal momento che i A s sull'heap, passano attraverso l'allocatore di memoria. Quando l'allocatore di memoria riceve una richiesta di nuova memoria come nel passaggio 3, in molti casi (in molti casi) guarderà prima alla memoria liberata di recente. Vede che l'ultima operazione era una memoria priva della giusta misura (passo 2), e quindi prenderà di nuovo quel pezzo di memoria. Questa procedura verrà ripetuta in ogni iterazione. Quindi il ciclo seriale (comunemente ma non necessariamente) fornirà lo stesso indirizzo più e più volte.

loop parallelo

Ora pensiamo al ciclo parallelo. Poiché non c'è sincronizzazione, l'ordinamento delle allocazioni e delle allocazioni di memoria non è determinato. Pertanto è possibile che siano interfogliati in qualunque modo tu possa immaginare. Quindi, in generale, l'allocatore di memoria non sarà in grado di utilizzare lo stesso trucco dell'ultima volta per distribuire sempre lo stesso pezzo di memoria.Un esempio ordinamento può essere per esempio che tutti e quattro A s vengono costruiti prima che tutti distrutti - qualcosa di simile:

  1. costruzione
  2. costruzione
  3. costruzione
  4. costruzione
  5. distruzione
  6. distruzione
  7. distruzione
  8. distruzione

L'allocatore di memoria dovrà pertanto servire fino 4 nuovi pezzi della memoria prima di poter ottenere un certo indietro e ricominciare il riciclaggio.

Il comportamento della versione basata su stack è leggermente più deterministico, ma può dipendere dalle ottimizzazioni del compilatore. Per la versione seriale ogni volta che l'oggetto viene creato/distrutto viene regolato il puntatore dello stack. Dato che non c'è nulla in mezzo, continuerà a essere creato nella stessa posizione.

Per la versione parallela, ogni thread ha il proprio stack in un sistema di memoria condivisa. Pertanto ogni thread creerà i suoi oggetti in una diversa posizione di memoria, e non è possibile alcun "riciclo".

Il comportamento che stai vedendo non è in alcun modo strano o comunque garantito. Dipende dalla quantità di core fisici che hai, da quanti thread vengono eseguiti, da quante iterazioni usi - generalmente le condizioni di runtime.

Riga inferiore: tutto va bene, non dovresti leggerlo troppo.

+1

davvero bella risposta, grazie. Ora capisco perché nella versione seriale, stampa sempre la stessa cosa e perché nel parallelo non lo fa. ma questo non risponde perché il quarto cout contiene 14 caratteri e il primo di albero solo 8 (tratto dal mio esempio). Qual è il significato dei "nomi" brevi o lunghi per il puntatore? – PinkFloyd

+1

Questa sarebbe solo la posizione in memoria. Lo stack si trova nella parte superiore dello spazio di memoria e cresce verso il basso. Un indirizzo lungo significherebbe che la posizione dell'heap è stata trovata da qualche parte vicino allo stack. – jepio

1

Credo che questo dipende al vostro ambiente non è Revelant e non deve essere considerato come strano comportamento. Utilizzando anteprima MS VS 2015, il codice mi dà il seguente (con OMP abilitato):

0082C3DC 
0082C41C 
0082C49C          
0082C45C          
0082C41C          
0082C41C          
0082C41C     
0082C41C 
+0

il più delle volte ottengo un risultato simile, ma a volte ho il tipo che ho mostrato nella mia domanda ... quante volte hai provato a eseguire il mio codice? – PinkFloyd

+0

Ho appena fatto 10 volte non ho mai incontrato il tuo comportamento. Cosa usi ? Lo proverò usando GCC. – coincoin

+0

gcc 4.7.2 su linux – PinkFloyd