OpenMP und C++11 Zufallszahlengenerator



  • Hallo Skym0sh0,

    Skym0sh0 schrieb:

    Wenn du Zufallszahlen willst, wieso erzeugst du dir nicht in jedem eigenen Thread einen Generator? Wieso willst du die Zusammenfassen?

    Dann: In deinem Code hättest du eigentlich eine schöne Abgrenzung, mehrere Generatoren für mehrere Threads. Aber dann nutzt du im double operator() eine OMP-Funktion, die dir die schöne Abgrenzung kaputt macht und die Kopplung erhöht.

    Okay,
    ich gebe jedem Thread einen eigenen (neu entworfenen) Zufallszahlengenerator. Jetzt habe ich aber nach wie vor das Problem, dass die erste und zweite Sequenz dieselben Zufallszahlen liefern.
    Diese nacheinander aufgerufenen parallelen Blöcke stehen symbolisch für eine immer wieder aufgerufene Funktion, die einen Datensatz mit Zufallszahlen bearbeiten.

    Irgendwie muss ich doch aber dafür sorgen, dass wiederholte Aufrufe derselben Funktion mit neuen Sequenzen von Zufallszahlen versorgt werden. 😕

    Gruß,
    -- Klaus.

    #ifndef RNG_H
    #define RNG_H
    
    #include <iostream>
    #include <random>
    
    #include "omp.h"
    
    using namespace std;
    
    class RNG
    {
    	public:
    		RNG(int seed):
    			_engine( seed ), _distribution( 0.0, 1.0 ){};
    
    		double operator()(){ return _distribution( _engine ); }
    
    	private:
    		mt19937 _engine;
    		uniform_real_distribution<double> _distribution;
    
    };
    #endif
    
    #include <iostream>
    
    #include "rng.h"
    
    int main()
    {
    	int const threads = 8;
    
    	omp_set_num_threads( threads );
    
    	#pragma omp parallel
    	{
    		RNG rng( omp_get_thread_num() );
    
    		#pragma omp critical
    		{
    			cout	<< "random number from thread "
    						<< omp_get_thread_num()	<<	": "	<< rng()
    						<< endl;
    		}
    	}
    
    	cout << "\nNext sequence!\n" << endl;
    
    	#pragma omp parallel
    	{
    		RNG rng( omp_get_thread_num() );
    
    		#pragma omp critical
    		{
    			cout	<< "random number from thread "
    						<< omp_get_thread_num()	<<	": "	<< rng()
    						<< endl;
    		}
    	}
    
    	return 0;
    }
    


  • Klaus82 schrieb:

    ich gebe jedem Thread einen eigenen (neu entworfenen) Zufallszahlengenerator. Jetzt habe ich aber nach wie vor das Problem, dass die erste und zweite Sequenz dieselben Zufallszahlen liefern.

    Verwende unterschiedliche Seeds, z.B. von std::random_device .



  • Nexus schrieb:

    Klaus82 schrieb:

    ich gebe jedem Thread einen eigenen (neu entworfenen) Zufallszahlengenerator. Jetzt habe ich aber nach wie vor das Problem, dass die erste und zweite Sequenz dieselben Zufallszahlen liefern.

    Verwende unterschiedliche Seeds, z.B. von std::random_device .

    Was es nicht alles gibt.

    Na ja, am Ende würde ich damit genau das tun, was ich oben als quick and dirty bezeichnet habe: Der seed wird jedes Mal zufällig bestimmt. Was natürlich super für die Reproduzierbarkeit ist. 😉

    #ifndef RNG_H
    #define RNG_H
    
    #include <iostream>
    #include <random>
    
    #include "omp.h"
    
    using namespace std;
    
    class RNG
    {
    	public:
    		RNG():
    			_engine( _rd() ), _distribution( 0.0, 1.0 ){};
    
    		double operator()(){ return _distribution( _engine ); }
    
    	private:
    		random_device _rd;
    		mt19937 _engine;
    		uniform_real_distribution<double> _distribution;
    
    };
    #endif
    

    Gruß,
    -- Klaus.



  • Du erzeugst deine RNGs in jedem parallel-Block aufs neue, und das mit den selben Seeds. Klar, daß immer wieder dieselben Sequenzen herauskommen. Außerdem ist der Mersenne-Twister m.W. nicht gerade billig zu konstruieren.

    Wie wär's mit einem übergeordneten vector<RNG> mit threads RNGs, auf die Du dann mit rng_vec[omp_get_thread_num()]() zugreifst?



  • Caligulaminus schrieb:

    Wie wär's mit einem übergeordneten vector<RNG> mit threads RNGs, Auf die Du dann mit rng_vec[omp_get_thread_num()]() zugreifst?

    Du meinst so wie in meinem Startpost wofür ich schon abgewatscht wurde? 😕

    Gruß,
    -- Klaus.



  • In deinem Startpost war RNG von OMP abhängig.



  • Caligulaminus schrieb:

    In deinem Startpost war RNG von OMP abhängig.

    Ja.

    Aber ist das jetzt nicht eine konzeptionelle Frage? Ob ich einen Vektor von RNGs habe oder einen wrapper (genannt RNG) um einen Vektor von Generatoren lege?

    Das macht doch nicht viel Unterschied?

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    Was natürlich super für die Reproduzierbarkeit ist. 😉

    Wenn du reproduzierbare Zufallszahlen willst, dann nimm eben zuvor festgelegte Seeds. Naheliegend, oder? 🙂

    P.S. Ich würde nicht jedes Mal auf den Vektor zugreifen. Übergib jedem Thread seinen eigenen, unabhängigen RNG, nachdem er entsprechend geseedet wurde. Gerade in Multithreaded-Szenarien würde alles so modular wie möglich halten und nur über sehr kleine Schnittstellen miteinander kommunizieren.



  • Ja, ja ... danke für die ganzen Vorschläge.

    [piensen]
    Ich befürchte nur, dass es am Ende sowieso niemanden interessiert, wie penibel ich darauf achte. D.h. ich kann eigentlich auch direkt jedes Mal mit std::random_device seeden. 😞
    [/piensen]



  • So,

    ich habe jetzt eine große #pragma omp parallel Umgebung, auf dass jeder Thread seinen eigenen Kram macht und jeder vorkommende PRNG wird mittels std::random_device initialisiert.

    Ist die mathematisch schändlichste Lösung, aber dafür die praktikabelste. 🙄

    Gruß,
    -- Klaus.


  • Mod

    Klaus82 schrieb:

    Ist die mathematisch schändlichste Lösung, aber dafür die praktikabelste. 🙄

    Schändlich ist das nur, wenn es nicht das ist, was du möchtest. Wenn es nicht das ist, was du möchtest, warum machst du es dann so?

    Wenn du auf die Reproduzierbarkeit anspielst: Erstens ist es ja wohl einfach, sich dafür eine Lösung zu denken, zweitens hat Nexus diese bereits ausdrücklich erklärt.



  • Sorry für die späte Rückmeldung.

    Ich habe tatsächlich nochmal den Eintrag bei Stackoverflow gefunden, wo die Generatoren im Vektor gespeichert werden und jeder Thread auf einen Eintrag zugreift - so wie ich es im Startpost geschrieben hatte.

    Also scheint das doch 'okay' zu sein, oder nicht?

    Gruß,
    -- Klaus.


Anmelden zum Antworten