SMP in eigenem C-Programm nutzen

Stimmt natürlich ... Ich hab das Copy on Write völlig vergessen ... :o

Und OpenBSD's malloc cacht ebenfalls, von daher habe ich einfach mal ins Blaue geraten, was Unterschiede angeht, und lag daneben.

Habe mal testweise ein Programm geschrieben, das mehr Speicher alloziert als ich habe. In dem Fall liefert malloc NULL zurück. Alloziere ich leicht drunter, ist es noch in Ordnung und ich kann in paar Bereiche schreiben, ohne dass mir top oder irgendwas anderes anzeigt, dass jetzt fast kein Speicher mehr verfügbar wäre.

Starte ich das Programm parallel ein zweites Mal, das gleiche Verhalten. Also irgendwann wird das System dann wohl ausfallen, wenn beide ihren Speicher individuell befüllen.
 
Für mein genutztes Linux habe ich die Lösung gefunden:

Code:
mallopt( M_MXFAST, 0 );

Aus der Manpage von mallopt(3):

mallopt() allows tuning of the parameters affecting allocation. param
is one of the constants listed below; value should be specified in
bytes.

M_MXFAST
Free chunks not larger than the given value are not joined to
adjacent free chunks; larger ones are joined. This is intended
to optimize future requests for small chunks of the same size as
previously freed chunks. Allowed values are in the range
[0-80]; a value of 0 causes all free chunks to be joined.
Default: 64.

Füge ich also die genannte Zeile am Anfang des Codes ein, zeigt free(1) mir auch sofort nach dem free(3) im Programm den freien Speicher an.

Interessant...

Wie ist es mit mallopt bei *BSD? Muss es dort ebenfalls Einsatz finden, damit das Programm funktioniert wie erwünscht?

@Paldium: Bei dem, was Du beobachtest und dann vermutest, dass "irgendwas wohl das System" ausfallen wird, möchte ich meine Bedenken äußern, ohne sie fundieren zu können. Ich kann mir nicht vorstellen, dass ein derart ausgereiftes Betriebssystem abschmiert, weil min. zwei Userspace-Programme zuviel Speicher allozieren. Da sollte doch Abfangmechanismen für Schutz sorgen...!? :confused:

Herakles
 
@Paldium: Bei dem, was Du beobachtest und dann vermutest, dass "irgendwas wohl das System" ausfallen wird, [...]

Dann entschuldige meine Wortwahl. Ich versuchs nochmal:

Irgendwann wird das System die Speicheranfragen dieser Programme nicht mehr bedienen können. Wie sich welches Programm dann verhält (irgendeines wird wohl abgeschossen?), kann ich aber nicht sagen.
 
Es gibt unterschiedliche Strategien, das abzuschießende Programm auszuwählen:
- Linux wählt zufällig. Wenn du Pech hast, ist es init oder was man da gerade so nutzt.
- FreeBSD schießt den ab, der die Anfrage stellte.
 
Was in 99% der Fälle auf's Abschießen hinaus läuft. :) Mir wurde auch gestern noch tadelnd gesagt, dass Linux sich inzwischen auch etwas intelligenter verhält und nicht mehr blind irgendwas abschießt.
 
Mir wurde auch gestern noch tadelnd gesagt, dass Linux sich inzwischen auch etwas intelligenter verhält und nicht mehr blind irgendwas abschießt.

Das kann ich so für Debian nicht sagen. Hier hat ein DB-Server Anfang des Jahres noch init abgeschossen. Ich dachte auch, dass das mittlerweile nicht mehr auftritt.
 
Moin!

Mal kurz zurück zum eigentlichen Thema. Ich versuche seit geraumer Zeit, wirklich mit OpenMP klarzukommen, habe aber noch grundlegende Probleme.

Das Augenscheinlichste ist, dass eine parallele Berechnung andere Ergebnisse liefert, als eine in nur einem Thread ausgeführte.

Hier mal ein simpel gehaltener Beispielcode:

Code:
#include "stdio.h"
#include <omp.h>
#include <math.h>
#include <limits.h>
#include <stdint.h>

int main(int argc, char *argv[]) {
	int k,inf=100000000;
	
	double sum, revsum;

	sum = 0.0;
	revsum = 0.0;
	
#pragma omp parallel for
	for( k=1 ; k<=inf ; k++) {
		sum += 1.0/pow(k,(double)2);
	}
#pragma omp parallel for
	for( k=inf ; k>=1 ; k--) {
		revsum += 1.0/pow(k,(double)2);
	}
	printf("%.52f\n%.52f\n",sum,revsum);
        return 0;
}

Hier das Ergebnis:

Code:
herakles@schlepptop ~ $ gcc test.c -Wall 
test.c: In Funktion »main«:
test.c:15:0: Warnung: #pragma omp parallel wird ignoriert [-Wunknown-pragmas]
test.c:19:0: Warnung: #pragma omp parallel wird ignoriert [-Wunknown-pragmas]
herakles@schlepptop ~ $ ./a.out 
1.6449340578345750252253765211207792162895202636718750
1.6449340568482264668404013718827627599239349365234375

Dass bei der umgedrehten Berechnung ein anderes Ergebnis herauskommt, habe ich inzwischen gelernt (siehe diesen Thread) - Rundungsfehler offenbar. Darum geht es auch nicht. Interessanter ist folgendes:

Wenn ich das nun mit openmp kompilieren, wird das Ergebnis beider Berechnungen anders:

Code:
herakles@schlepptop ~ $ gcc test.c -Wall -fopenmp
herakles@schlepptop ~ $ ./a.out 
1.6422920408720118601308968209195882081985473632812500
1.644934052758408649097532361338380724191665649414062

Bei der ersten Berechnung ist die Differenz schon recht groß, ich kopiere die beiden zum Vergleich mal in die nächsten zwei Zeilen:

1.6449340578345750252253765211207792162895202636718750 OHNE OpenMP
1.6422920408720118601308968209195882081985473632812500 MIT OpenMP

Da ist also bei der Verwendung von OpenMP schon eine Differenz in der dritten Nachkommastelle zu erkennen - nur weil ich auf 2 Prozessoren arbeite?

Bis jetzt habe ich das noch nicht vollkommen nachvollziehen können.

Zwei Fragen dazu:

1. Entsteht das unterschiedliche Ergebnis (mit und ohne OpenMP), weil ohne OpenMP die Berechnungen immer in der gleichen Reihenfolge durchgeführt werden und deshalb immer exakt DERSELBE Rundungsfehler auftritt, wenn man aber in willkürlicher Reihenfolge (also mit OpenMP) die Berechnungen macht, dann treten die Rundungsfehler in unterschiedlicher Reihenfolge auf, was zu einem abweichenden Ergebnis führt?

Zitat dazu von linux-mag.com
Very briefly, the way the round-off error accumulates in these calculations does have an impact upon the final results. Running this sum in the other direction, which most people would normally do, accumulates error in a different order, leading to a different result. That is, when you change the order of your floating point computations, you will change the round-off error accumulation. It it possible to observe a catastrophic loss of accuracy, impacting first and second digits of a summation like this, by not being aware of these issues. But that’s a topic for another time.

2. Wie macht man den obigen Beispielcode "besser", "richtiger" oder minimiert, ja bestenfalls löscht den Fehler? Geht das überhaupt?

Grübelnde Grüße
Herakles
 
x86 Prozessoren können double mit 80 Bit Registern rechnen. Ob das passiert hängt von Plattform und Compiler ab.

Man kommt von Außen aber nur an 64 Bit dran, immer wenn eine Zahl in den Speicher muss, zum Beispiel zum weiterreichen an einen anderen Core, geht diese Extragenauigkeit also verloren.
 
Ok, trial and error! Machen wir's mit float :)

Zugrunde liegt nun derselbe Code wie oben, nur überall, wo "double" steht, ist nunmehr ein "float".

Hier ohne OpenMP:

Code:
herakles@schlepptop ~ $ gcc test.c -Wall
test.c: In Funktion »main«:
test.c:15:0: Warnung: #pragma omp parallel wird ignoriert [-Wunknown-pragmas]
test.c:19:0: Warnung: #pragma omp parallel wird ignoriert [-Wunknown-pragmas]
herakles@schlepptop ~ $ ./a.out 
1.6447253227233886718750000000000000000000000000000000
1.6449340581893920898437500000000000000000000000000000

Und hier mit OpenMP:

Code:
herakles@schlepptop ~ $ gcc test.c -Wall -fopenmp
herakles@schlepptop ~ $ ./a.out 
1.6446857452392578125000000000000000000000000000000000
0.0002112551592290401458740234375000000000000000000000

Es wird also besser, ohne dass ich weiß, wie ein x86 float nun intern darstellt. Ein Unterschied findet sich nun in der vierten Nachkommastelle.

Ich seh's schon kommen. Die wirklich schönste Lösung geht nur, indem ich die Fließkommazahlen nach Integer wandle.

Nichtsdestotrotz frage ich mich immer noch, wieso das eigentlich so ist - sprich: wieso sind die Ergebnisse mit OpenMP anders als ohne?

Herakles
 
Ich denke das Problem ist eher, dass du Race-Conditions mit dem Code hast.

+= ist nicht atomar. Das ist ein Lesen und eine Zuweisung (also zwei Befehle).

Wenn zwei Threads nun die Variable gleichzeitig lesen, addieren sie global auf den alten Wert etwas drauf. Was dann rauskommt hängt dann davon ab, wer zuletzt den Wert gespeichert hat.

Dir fehlen schlicht Berechnungen. Darum ist der Wert mit OpenMP kleiner.
 
Als Lösungsvorschlag:

Teile die Schleife in N-Schleifen auf, wo jeder einen Teil berechnet und addieren dann die N-Ergebnisse. Der Performance-Gewinn dürfte aber nicht allzu groß sein, damit, wegen Overhead.

D.h.:
Bevor der Kern alle Threads gestartet hat, wäre ein einzelner Kern schon fertig.
 
Mit Rundungsfehler hat das nichts zu tun, das ist wie Nuke bereits sagte eine race condition. OpenMP hilft dir zwar bei der Parallelisierung, aber algorithmische Probleme wird das nicht loesen. Du koenntest in deiner "critical section", in dem Fall "sum += 1.0/pow(k,(double)2);" ein
"#pragma omp atomic", oder "#pragma omp critical" drueber schreiben, wobei sich atomic in dem Fall besser eignet (und schneller ist). Dann darf halt nur ein Thread auf die Variable gleichzeitig schreiben. Damit waere dein ganzer Geschwindigkeitsgewinn aber verpufft, sogar im Gegenteil, es wird vermutlich viel langsamer sein, weil du den overhead des lockings drauf rechnen musst.
In der Regel benutzt man sowas eher fuer Algorithmen, die einen Grossteil der Berechnung unabhaengig ausfuehren koennen und am Ende hat man dann mal eine kritische Sektion, die einem das Gesamtergebnis liefern.
 
Oder es machen, wie es im oben verlinkten Tutorial steht...
Code:
#pragma omp parallel for reduction(+: sum)
 
Zurück
Oben