Punkte aus einer OpenMP-Schleife zeichnen

Alles rund um die Programmierung mit Qt
Antworten
Trash
Beiträge: 4
Registriert: 3. September 2009 13:51

Punkte aus einer OpenMP-Schleife zeichnen

Beitrag von Trash »

Hallo Qt-Gemeinde :)
ich habe ein Programm geschrieben, das über das Monte-Carlo-Prinzip Pi approximiert und dieses Programm mit OpenMP parallelisiert:
Es funktioniert, indem man auf das Quadrat mit den Eckpunkten (0,0), (0,1), (1,0) und (1,1) gleichverteilt Punkte "regnen" lässt und anschließend prüft, wie viele davon im Einheitskreis liegen. Über das Verhältnis kommt man dann an die Zahl Pi.

Code: Alles auswählen

#include <iostream>
#include <omp.h>
#include <cmath>
#include <ctime>
#include <climits>

using namespace std ;

// **********************************************************

double randomNumGen(double low, double high)
{
	double range = high - low ;
	double num = static_cast<double>( rand() ) * range / static_cast<double>( RAND_MAX ) + low ; 
	return num ;
}

// **********************************************************

int main()
{
//	const unsigned long long int precision(ULLONG_MAX-1) ;	// 18446744073709551614
	const unsigned long long precision(200000000) ;
														
	const double pi(3.1415926535897932) ;
	
	srand(time( NULL ));
	
	unsigned long long int inside(0) ;
	
	cout << precision << endl ;
// ----------------------------------------------------------
#pragma omp parallel 
{
	double length( 1.0 / omp_get_num_threads() ) ;
	int no( omp_get_thread_num() ) ;
	double left( no * length ) ;
	double right( (no+1) * length ) ;
	double x(0.0), y(0.0) ;
	
#pragma omp for reduction( + : inside )
		for( unsigned long long int k = 0 ; k < precision ; k++ )
		{		
			x = randomNumGen(left, right) ;
			y = randomNumGen(0.0, 1.0) ;

			if( y*y <= 1.0 - x*x )
				inside++ ;
		}	
}
// ----------------------------------------------------------
	cout.setf(ios::fixed, ios::floatfield) ;
	cout.precision(6) ;
	
	cout << "Inside: " << inside << " (of " << precision << ")" << endl ;
	cout << "Pi:     " << pi << endl ;
	long double tmp( (long double)inside / (long double)precision ) ;
 	cout << "Ratio:  " << 4.0 * tmp << endl ;
	
	return 0 ;
	
}

/* output :

Inside:    157076482 (of 200000000)
Pi:        3.141593
Pi(Ratio): 3.141730

*/
Nun möchte ich gerne den "Regen" graphisch darstellen. Dazu habe ich folgendes geschrieben:

Code: Alles auswählen

#include <QApplication>

#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

#include <omp.h>
#include <cmath>
#include <ctime>

// **********************************************************

double randomNumGen(double low, double high)
{
	double range = high - low ;
	double num = static_cast<double>( rand() ) * range / static_cast<double>( RAND_MAX ) + low ; 
	return num ;
}

// **********************************************************


int main( int argc, char **argv )
{
  const int breiteScene(600) ;
  const int breiteItems(500);

  QApplication app( argc, argv );
  
  QGraphicsScene scene( QRect( -breiteScene/2, -breiteScene/2, breiteScene, breiteScene ) );

  QGraphicsRectItem *rectItem = new QGraphicsRectItem(
                                QRect( -breiteItems/2, -breiteItems/2, breiteItems, breiteItems ), 0, &scene );
  rectItem->setPen( QPen( Qt::red, 3 ) );

  QGraphicsEllipseItem *ellipseItem = new QGraphicsEllipseItem(
                                      QRect( -breiteItems/2, -breiteItems/2, breiteItems, breiteItems ), 0, &scene );
  ellipseItem->setPen( QPen( Qt::red, 3 ) );

  QGraphicsView view;
  view.setScene( &scene );
  view.show();
  
//------------------- OpenMP part -----------------------------
 
	const unsigned long long int precision(100000) ;
	
//	const double pi(3.1415926535897932) ;
	
	srand(time( NULL ));
	
	unsigned long long int inside(0) ;

#pragma omp parallel 
{
	double length( 1.0 / omp_get_num_threads() ) ;
	int no( omp_get_thread_num() ) ;
	double left( no * length ) ;
	double right( (no+1) * length ) ;
	double x(0.0), y(0.0) ;
	
#pragma omp for reduction( + : inside )
	for( unsigned long long int k = 0 ; k < precision ; k++ )
	{		
		x = randomNumGen(left, right) ;
		y = randomNumGen(0.0, 1.0) ;

		if( y*y <= 1.0 - x*x )
		{
			inside++ ;
#pragma omp critical
{			
		  	 scene.addRect( QRectF( (x*breiteItems/2), (y*breiteItems/2), 1, 1), QPen( Qt::green ) );
  			 scene.update( 0, 0 , breiteItems, breiteItems ) ;
}
		}
		else
#pragma omp critical
{			
			scene.addRect( QRectF( (x*breiteItems/2), (y*breiteItems/2), 1, 1), QPen( Qt::darkBlue ) );
  	 		scene.update( 0, 0 , breiteItems, breiteItems ) ;
}
	}	
}

  return app.exec();
}
Leider zeigt das Programm nicht das Verhalten, das ich mir wünsche...
Anstatt das beregnete Quadrat sukzessive zu füllen, werden die Punkte zuerst alle berechnet und nachdem der parallele Abschnitt absolviert wurde, wird die Graphik gefüllt.

Wie kann ich das von mir gewünschte Verhalten erreichen ?
Ist das Design soweit OK oder sollte ich da was ändern ?

Abschließend sei noch angemerkt, dass ich relativ neu bei Qt und für jeden Support dankbar bin :D.

Viele Grüße,
Trash
.
Curtis Newton
Beiträge: 122
Registriert: 11. Juni 2008 18:39

Beitrag von Curtis Newton »

Erstmal solltest Du Dich mit der Nachrichtenverarbeitung bei grafischen Systemen vertraut machen. FÜr Windows ist es hier erklärt: http://msdn.microsoft.com/en-us/library ... 85%29.aspx So ähnlich ist es dann immer.

Und die Doku von QThread könnte auch interessant sein.

Kurz: die Berechnung in einen Thread, die GUI nebst Darstellung in den Hauptthread.

C.
Trash
Beiträge: 4
Registriert: 3. September 2009 13:51

Beitrag von Trash »

Erstmal solltest Du Dich mit der Nachrichtenverarbeitung bei grafischen Systemen vertraut machen. Für Windows ist es hier erklärt: http://msdn.microsoft.com/en-us/library ... 85%29.aspx So ähnlich ist es dann immer.
Besten Dank. Ich benutzte OS X, werde mir das aber trotzdem durchlesen.
Und die Doku von QThread könnte auch interessant sein...
Mit QThread hab ich bereits gearbeitet. Ist tatsächlich interessant.
Kurz: die Berechnung in einen Thread, die GUI nebst Darstellung in den Hauptthread.
Ich setze mich dran und lass von mir hören.

Sollte jemand noch etwas Konkreteres beisteuern können und wollen : you´re welcome :wink:

Viele Grüße,
Trash
.
Trash
Beiträge: 4
Registriert: 3. September 2009 13:51

Beitrag von Trash »

Hallo,

ich habe den gemachten Vorschlag umgesetzt und folgende Modifikationen vorgenommen:

Code: Alles auswählen

#include <QApplication>

#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QMessageBox>
#include <QThread>

#include <omp.h>
#include <cmath>
#include <ctime>

// **********************************************************

double randomNumGen(double low, double high)
{
	double range = high - low ;
	double num = static_cast<double>( rand() ) * range / static_cast<double>( RAND_MAX ) + low ; 
	return num ;
}

// **********************************************************

class MonteCarloPi : public QThread
{
private:
	int precision ;
	int inside ;
	double pi ;
	QGraphicsScene* scene ;
	int breiteScene ;
	int breiteItems ;
	
public:
	// constructor
	MonteCarloPi(const int &p, QGraphicsScene* s) ;
	
	// run
	void run() ;
};

// **********************************************************

MonteCarloPi::MonteCarloPi(const int &p, QGraphicsScene* s ) : QThread()
{
	precision = p ;
	scene = s ;
	inside = 0 ;
	pi = 3.1415926535897932 ;
	breiteScene = 600 ;
	breiteItems = 500 ;
}

void MonteCarloPi::run()
{
	srand( time( NULL ) ) ;
	
	int tmp(0) ;
	
	#pragma omp parallel 
	{
		double length( 1.0 / ( omp_get_num_threads() - 1 ) ) ;
		int no( omp_get_thread_num() ) ;
		double left( no * length ) ;
		double right( (no+1) * length ) ;
		double x(0.0), y(0.0) ;

		#pragma omp for reduction( + : tmp )
		for( int k = 0 ; k < precision ; k++ )
		{		
			x = randomNumGen(left, right) ;
			y = randomNumGen(0.0, 1.0) ;	

			if( y*y <= 1.0 - x*x )
			{
				tmp++ ;
				#pragma omp critical
				{			
					scene->addRect( QRectF( (x*breiteItems/2), (y*breiteItems/2), 1, 1), QPen( Qt::green ) );
					scene->update( 0, 0 , breiteItems, breiteItems ) ;
				}
			}
			else
			{
				#pragma omp critical
				{			
					scene->addRect( QRectF( (x*breiteItems/2), (y*breiteItems/2), 1, 1), QPen( Qt::darkBlue ) );
 					scene->update( 0, 0 , breiteItems, breiteItems ) ;
				}
			}
		}	
	}
	
	inside = tmp ;
} ;

// **********************************************************

int main( int argc, char **argv )
{
  const int breiteScene(600) ;
  const int breiteItems(500);

  QApplication app( argc, argv );
  
  QGraphicsScene scene( QRect( -breiteScene/2, -breiteScene/2, breiteScene, breiteScene ) );

  QGraphicsRectItem *rectItem = new QGraphicsRectItem(
                                QRect( -breiteItems/2, -breiteItems/2, breiteItems, breiteItems ), 0, &scene );
  rectItem->setPen( QPen( Qt::red, 3 ) );

  QGraphicsEllipseItem *ellipseItem = new QGraphicsEllipseItem(
                                      QRect( -breiteItems/2, -breiteItems/2, breiteItems, breiteItems ), 0, &scene );
  ellipseItem->setPen( QPen( Qt::red, 3 ) );

  QGraphicsView view;
  view.setScene( &scene );
  view.show();
  
  MonteCarloPi calculation(1000, &scene) ;
  
  calculation.start();
  
  calculation.wait();
  
  return app.exec();
}
Der Code lässt sich ohne Warnungen kompilieren, aber leider stürzt das Programm ab, nachdem Quadrat und Kreis gezeichnet wurden.

Was läuft da falsch ?

Viele Grüße,

Trash
.

PS:
Ich habe mal probehalber alle OpenMP Anweisungen auskommentiert und siehe da: das Programm läuft ( QThreads und OpenMP mögen sich anscheinen leider nicht besonders... )
Allerdings ist das Verhalten immer noch das gleiche: zuerst werden Quadrat und Kreis gezeichnet, dann die Punkte berechnet und anschließend werden alle Punkte in einem Rutsch eingezeichnet.
Curtis Newton
Beiträge: 122
Registriert: 11. Juni 2008 18:39

Beitrag von Curtis Newton »

Du darfst nur in einem Thread auf die GUI-Elemente zugreifen.

Erstelle Dir eine eigene Klasse für das Fenster! Vom MonteCarlo-Thread schickst Du dann einem Objekt dieser Klasse Nachrichten, die die Werte für den Punkt enthalten. Das Objekt Zeichnet dann mit addRect das Rechteck.

Schau doch mal durch die examples von qt, speziell zu den Threads sowie speziell auch http://doc.trolltech.org/4.5/threads.ht ... ss-threads

C.
Curtis Newton
Beiträge: 122
Registriert: 11. Juni 2008 18:39

Beitrag von Curtis Newton »

Code: Alles auswählen

#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H

#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>

class Window : public QGraphicsView
{
    Q_OBJECT

public:
    Window();

public slots:
    void slot_addrect(QRectF, QPen);

private:
     QGraphicsScene *scene;
    
};


#include <QThread>

class MonteCarloPi : public QThread
{
	Q_OBJECT
signals:
	void do_addrect(QRectF, QPen);

public:
	void run() ;
}; 

double randomNumGen(double low, double high);

#endif
und

Code: Alles auswählen


#include "imageviewer.h"

Window::Window()
{
	scene = new QGraphicsScene;
	
	this->setScene(scene);

	show();

	MonteCarloPi *thread=new MonteCarloPi;

	connect(thread, SIGNAL(do_addrect(QRectF, QPen)), this, SLOT(slot_addrect(QRectF, QPen)));

	setMinimumSize(200,200);
	thread->start();
}


void Window::slot_addrect(QRectF rect, QPen pen)
{
	scene->addRect(rect, pen);
	scene->update();
}

void MonteCarloPi::run()
{
	for( int k = 0 ; k < 1000 ; k++ )
	{      
		double x = randomNumGen(0, 100) ;
		double y = randomNumGen(0, 100) ;   
		emit do_addrect ( QRectF( x,y, 1, 1), QPen( Qt::green ) );

		msleep(250);
	}

} ; 

double randomNumGen(double low, double high)
{
	double range = high - low ;
	double num = static_cast<double>( rand() ) * range / static_cast<double>( RAND_MAX ) + low ;
	return num ;
}
C.
Curtis Newton
Beiträge: 122
Registriert: 11. Juni 2008 18:39

Beitrag von Curtis Newton »

Trash hat geschrieben: Ich habe mal probehalber alle OpenMP Anweisungen auskommentiert und siehe da: das Programm läuft ( QThreads und OpenMP mögen sich anscheinen leider nicht besonders... )
Allerdings ist das Verhalten immer noch das gleiche: zuerst werden Quadrat und Kreis gezeichnet, dann die Punkte berechnet und anschließend werden alle Punkte in einem Rutsch eingezeichnet.
Bei mir lief das mit OpenMP, allerdings liefert omp_get_num_threads immer eins. Keine Ahnung, kenne ich nicht. Du wolltest Dich aber mit der Nachrichtenbehandlung beschäftigen!!!1! ;-)

C.
Trash
Beiträge: 4
Registriert: 3. September 2009 13:51

Beitrag von Trash »

Erst einmal vielen Dank für die Hilfe :D Habe als Qt-newbee gerade schwer mit der Doku gekämpft um das hier zum laufen zu bekommen...
Sobald ich die OpenMP-Angelegenheit geklärt habe, werde ich das hier mitteilen. Noch einmal ein dickes TNX :D.
Noch was zu OpenMP:
omp_get_num_threads() liefert die Gruppenstärke des Thread-Teams in der parallelen Region, die mit dem macro
#pragma omp parallel
aufgemacht wurde. Um das macro tatsächlich zu aktivieren, muss man unter Unix & Co mir dem Schalter -fopenmp kompilieren, ansonsten werden die macros einfach überlesen (in der Regel mit einer Warnung). Wie es in der Windows-Welt aussieht weiß ich nicht, http://www.openmp.org kann aber bestimmt helfen.

Viele Grüße,

Trash
.
Antworten