2013-04-10 6 views
7

Sto usando i nuovi generatori di numeri casuali in C++ 11. Sebbene ci siano opinioni diverse, da questo thread sembra che la maggioranza crede che non siano thread-safe. Di conseguenza, vorrei creare un programma, in cui ogni thread utilizza il proprio RNG.come rendere ogni thread utilizza il proprio RNG in C++ 11

Un esempio è dato nel relativo discussione su come ottenere questo risultato con OpenMP:

#include <random> 
#include <iostream> 
#include <time.h> 
#include "omp.h" 

using namespace std; 



int main() 
{ 
    unsigned long long app = 0; 
    { 
     //mt19937_64 engine((omp_get_thread_num() + 1)); //USE FOR MULTITHREADING 
     mt19937_64 engine; //USE FOR SINGLE THREAD 
     uniform_real_distribution<double> zeroToOne(0.0, 1.0); 

     //#pragma omp parallel for reduction(+:app) //USE FOR MULTITHREADING 
     for (unsigned long long i = 0; i < 2000000000; i++) 
     { 
      if(zeroToOne(engine) < 0.5) app++; 
     } 
    } 
    cout << app << endl; 
    return 0; 
} 

Quando eseguo la versione multi-threaded e single-threaded di questo programma e di tenere traccia del tempo, richiedere lo stesso tempo per terminare dopo l'esecuzione. Inoltre, app non ha la stessa dimensione nei due casi, ma sospetto che sia semplicemente a causa dei diversi semi.

Domanda: L'esempio fornito mostra correttamente come forzare ogni thread a utilizzare il proprio RNG? In caso contrario, posso vedere un esempio di come è fatto, o ottenere un riferimento ad un posto dove spiegano come ottenere ciò?

risposta

6

Non è necessario condividere istanze di motore casuale tra più thread. Dovresti bloccare un singolo motore o creare un motore per ogni filo (con seme diverso (si noti la risposta di e4e5f4 riguardante la creazione di motori MT paralleli)). Nel caso di OpenMP è possibile memorizzare facilmente un motore per thread in un vettore e recuperarlo in base al risultato di omp_get_thread_num() compreso tra 0 e omp_get_num_threads()–1.

class RNG 
{ 
public: 
    typedef std::mt19937 Engine; 
    typedef std::uniform_real_distribution<double> Distribution; 

    RNG() : engines(), distribution(0.0, 1.0) 
    { 
     int threads = std::max(1, omp_get_max_threads()); 
     for(int seed = 0; seed < threads; ++seed) 
     { 
      engines.push_back(Engine(seed)); 
     } 
    } 

    double operator()() 
    { 
     int id = omp_get_thread_num(); 
     return distribution(engines[id]); 
    } 

    std::vector<Engine> engines; 
    Distribution distribution; 
}; 

int main() 
{ 
    RNG rand; 
    unsigned long app = 0; 

    #pragma omp parallel for reduction(+:app) 
    for (unsigned long long i = 0; i < 2000000000; i++) 
    { 
     if(rand() < 0.5) app++; 
    } 
} 
+0

Grazie per questo esempio, che è molto utile. Ho due domande: (1) Posso chiederti perché hai scelto di includere ': engines()'? A rigor di termini, è necessario? .... (2) Posso usare l'oggetto 'rand' in un ciclo successivo nel mio programma, che non è parallelizzato? – BillyJean

+1

@BillyJean (1) Non richiesto ma il mio stile personale per chiamare ciascun elemento ctor nella lista di inizializzazione se almeno uno è chiamato. (2) Non sicuro al 100%, ma penso che 'omp_get_thread_num()' restituisce 0 per la regione non parallelizzata, quindi Sì. – hansmaad

+0

Un'ultima domanda: supponiamo di rendere globale 'RNG' e anche il suo oggetto' rand' globale. Invece del condizionale '(rand() <0.5)' Ora chiamo una funzione globale 'func', che esegue alcuni calcoli che dipendono da' rand'. L'uso di 'rand' in' func' sarà ancora sicuro? Direi di sì, ma mi piacerebbe sentire anche la tua opinione professionale. – BillyJean

2

Vorrei astenermi dall'utilizzare la semina casuale. Potrebbe finire con flussi sovrapposti. Questo alla fine influenzerà la statistica finale.

vorrei suggerire qualche soluzione collaudata come this

+2

La tua "soluzione collaudata come questa" avvisa: "Attenzione II: questo non è ancora testato, quindi potrebbe contenere molti bug". :-) – hansmaad

+0

Preferirei fidarmi dell'MTDC piuttosto che della semina casuale :) – Nishanth

+0

Ho appena sorriso quando ho letto questa nota sul sito web.Tuttavia il documento è molto interessante. Quando ho implementato il PRNG in parallelo, la prima volta mi sono chiesto come seminare i motori, ma non sono riuscito a trovare alcuna informazione. In effetti, non abbiamo mai avuto problemi (ovvi) nelle simulazioni Monte Carlo usando una semina casuale semplice. – hansmaad