C++ OpenCL gibt Memory Fehler aus

F

Furtano

Gast
Hi,

ich programmiere eine Ameisensimulation mit OpenCL und OpenGL (+SFML).

Jetzt komme ich nicht weiter, OpenCL gibt mir auf meiner Nvidia Karte den OpenCl Fehler -6 (CL_OUT_OF_HOST_MEMORY) und auf meinem Intel Laptop -4 (CL_MEM_OBJECT_ALLOCATION_FAILURE ).

Es liegt an OpenCL. Wenn ich die Ameisen ca. 3 Sekunden simuliere kommt nur noch je nach System die oben genannten Fehler.

Danke für eure Hilfe !

PHP:
bool CreateMemObjectsAnts(cl_context context, cl_mem memObjects[11],
						  float *world, float *antX, float *antY,  int *targetX, int *targetY, int *speed, int *direction, float * resultX, float * resultY, float *newAntX, float *newAntY)
{
	cl_int errorcode;
	memObjects[0] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, world, &errorcode);
	memObjects[1] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, antX, NULL);
	memObjects[2] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, antY, NULL);
	memObjects[3] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, targetX, NULL);
	memObjects[4] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, targetY, NULL);
	memObjects[5] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(int) * ARRAY_SIZE, speed, NULL);
	memObjects[6] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, direction, NULL);
	memObjects[7] = clCreateBuffer(context, CL_MEM_READ_WRITE,
		sizeof(float) * ARRAY_SIZE, NULL, NULL);
	memObjects[8] = clCreateBuffer(context, CL_MEM_READ_WRITE,
		sizeof(float) * ARRAY_SIZE, NULL, NULL);
	memObjects[9] = clCreateBuffer(context, CL_MEM_READ_WRITE,
		sizeof(float) * ARRAY_SIZE, NULL, NULL);
	memObjects[10] = clCreateBuffer(context, CL_MEM_READ_WRITE,
		sizeof(float) * ARRAY_SIZE, NULL, NULL);

	for (int i = 0 ; i < 11; i++){
		if(memObjects[i] == NULL){
		
			std::cerr << i << "Error creating memory objects!!!!!" << errorcode << std::endl; 
			return false;
		}
	}
		
	

	return true;
}
Ergänzung ()

PHP:
int  openCLinit::startOpenCLmain(){
	if (!CreateMemObjectsAnts(context, memObjects, world, antX, antY, targetX, targetY, speed, direction, resultX, resultY, newAntX, newAntY))
	{
		//Cleanup(context, commandQueue, program, kernel, memObjects);
		return 1;
	}
	 int errorNum = 0;
	// Set the kernel arguments (result, a, b)
	
	for (int i = 0; i < 11; i++){
		clSetKernelArg(kernel, i, sizeof(cl_mem), &memObjects[i]);
	}



	
	globalWorkSize[0] = ARRAY_SIZE ;
	localWorkSize[0] = 1;

	sf::Clock clocki;
	// Queue the kernel up for execution across the array
	clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL,
		globalWorkSize, localWorkSize,
		0, NULL, NULL);
	

	// Read the output buffer back to the Host
	clEnqueueReadBuffer(commandQueue, memObjects[9], CL_TRUE,
		0, ARRAY_SIZE * sizeof(float), newAntX,
		0, NULL, NULL);
	clEnqueueReadBuffer(commandQueue, memObjects[10], CL_TRUE,
		0, ARRAY_SIZE * sizeof(float), newAntY,
		0, NULL, NULL);
	//std::cout << clocki.getElapsedTime().asMilliseconds();
	//if (errNum != CL_SUCCESS)
	//{
		//print_cl_err(errNum);
		//Cleanup(context, commandQueue, program, kernel, memObjects);
		//return 1;
	//}

	
	clFinish(this->commandQueue);
	//std::cout << std::endl;
	//std::cout << "Executed program succesfully." << std::endl;

	// Output the result buffer
	/*for (int i = 0; i < ARRAY_SIZE; i++)
	{
		openclLogger << "ID : " << i << std::endl;
		openclLogger << "AntX: " << antX[i] << " AntY: " << antY[i] << std::endl;
		openclLogger << "TargetX: " << targetX[i] << " TargetY: " << targetY[i] << std::endl;
		openclLogger << "NewX: " << resultX[i] << " NewY: " << resultY[i] << std::endl << std::endl;
	}*/
	//Cleanup(context, commandQueue, program, kernel, memObjects);

	return errorNum;
}
 
Mal ne Frage sind NV Karten überhaupt OpenCL fähig?
 
OpenCL funktioniert nicht gut mit onBoard-Grafikkarten. Ich habe es jedenfalls nie auf dem Laptop zum Laufen bekommen. Es scheint, es hat ein Problem damit, dass die keinen dedizierten Grafikspeicher haben.

Wie schaut dein Shader aus? Wie groß sind die Arrays?


@Makso
OpenCL läuft auf AMD, Nvidia und Intel GPUs und x86-CPUs.
 
Ich habe es auf dem PC mit Nvidia Graka probiert und auf den Laptop mit CPU und Haswell.
Arraygröße muss ich mal schauen ob sich da was ändert, momentan bei 20000.
Wenn ichs auf 1 ändere, läuft er zwar ein paar Sekunden länger aber dann kommt der gleiche Fehler.

anbei noch der Kernel

PHP:
__kernel void stepSizeAnt(						__global  float *world,
						  __global  float *antX,
						  __global  float *antY,
						  __global  int *targetX,
						  __global  int *targetY,
						  __global  int *speed,
						  __global  int *direction,
						  __global  float *resultX,
						  __global  float *resultY,
						  __global  float *newAntX,
						  __global  float *newAntY
						  )
{
	int gid = get_global_id(0);




	newAntX[gid] = 0;
	newAntY[gid] = 0;
	resultX[gid] = 0;
	resultY[gid] = 0;


	// Neuen Richtungsvektor berechnen (in Welche Richtung soll die Ameise laufen)
	resultX[gid] = (targetX[gid]-antX[gid]);
	resultY[gid] = (targetY[gid]-antY[gid]);

	// Normvektor ausrechnen normalisieren
	float norm = sqrt((resultX[gid]*resultX[gid])+(resultY[gid]*resultY[gid]));


	// Richtungsvektor normalisieren
	resultX[gid] = (resultX[gid] / norm);
	resultY[gid] = (resultY[gid] / norm);


	// Ameisenschritt machen
	// neue Ameisenposition = Alte Position + (neue Schrittposition * Geschwindigkeit)
	newAntX[gid] +=  antX[gid] + (resultX[gid]*(speed[gid]));
	newAntY[gid] +=  antY[gid] + (resultY[gid]*(speed[gid]));

	// Auf 2 Stellen runden
	newAntX[gid] += 0.5;
	newAntX[gid] = (float)((int)(newAntX[gid]*1000))/1000;
	newAntY[gid] += 0.5;
	newAntY[gid] = (float)((int)(newAntY[gid]*1000))/1000;


	// Wenn die Ameise den Rand betritt

	if (newAntX[gid] <= 0 || newAntY[gid] <= 0){
		newAntX[gid] = 450;
		newAntY[gid] = 450;

	}
	if (newAntX[gid] >= 900 || newAntY[gid] >= 900){
		newAntX[gid] = 450;
		newAntY[gid] = 450;

	}








}
 
Zuletzt bearbeitet von einem Moderator:
Der Shader schau i. O. aus. Mich wundert nur, dass du in der Kernel-Signatur die Parameter dereferenzierst (*). Das war in meinen Shadern bisher nicht nötig (oder der Compiler hat das korrigiert).

Der Fehlermeldung (CL_OUT_OF_HOST_MEMORY) nach, tippe ich darauf, dass dir in deinem Programm (nicht im Shader) der allozierte Speicher aus dem Rahmen läuft. Da solltest du mal nachharken. Vielleicht erstellst du die Arrays immer wieder neu, ohne die alten wieder freizugeben.
 
Im Hauptprogramm läuft alles super.
Es muss irgendwas mit OpenCL zu tun haben.
 
Passiert das auch wenn der Kernel leer ist? Wenn ja, dann machst Du was beim allokieren der Buffer falsch. Wenn nicht, dann mach mal alle Buffer nur 1 Element groß und versuch es mit printf() Debugging OpenCL Kernel. Einfach auf der CPU ausführen, das findet dann hoffentlich schnell den Fehler. Alternativ einfach eine Hälfte auskommentieren, schauen ob es wieder auftritt, usw.

Was Du auch versuchen kannst: Anstatt dem enqueueRead den Buffer einfach Mappen und dann selber daraus lesen.
 
Rufst du int openCLinit::startOpenCLmain() für jeden Zeitschritt auf? Wenn ja allozierst du dir immer die Buffer neu, da ists kein Wunder dass es dir nach 3 s Simulation abstürzt. . . . .

Mehr Code wäre allerdings für weitere Tipps wirklich hilfreich . . . .
 
Zuletzt bearbeitet:
Ja ich rufe startOpenCLmain bei jeden Simulationsschritt auf. Wei mache ich das denn richtig ohne jedes mal neu zu allozieren?

Welchen Code brauchst du?

Zur Erklärung für den Kernel (_kernel void stepSizeAnt()).
Rein in den Kernel geht:
antX
antY

== alte Position der Ant
targetX
targetY

== neue Zielposition wo die Ant hinwandern sollen

Raus kommt:
newAntY
newAntX

== neue Ant Positionen
 
Zuletzt bearbeitet von einem Moderator:
Jemineh, etwas Eigenarbeit wäre angebracht. Als Code wäre vor allem der Mainloop und evtl die globalen Variablen interessant.

An welcher Stelle in startOpenCLmain könntest du denn die Buffer in OpenCL allozieren?
Lösung: clCreateBuffer
Wie könntest du wohl vermeiden, dass sie bei jedem Aufruf neu alloziert werden?
Lösung: Buffer einmalig vor dem Start des Mainloops allozieren.
 
Zuletzt bearbeitet:
Bei jedem Durchgang ruft du CreateMemObjectsAnts() auf. Das bedeutet, dass er bei jedem Durchgang die ganzen Buffer neu erstellt, statt die alten wiederzuverwenden. Nach 3 Sekunden dann ist kein Speicher mehr für neue Buffer verfügbar.

Lösung: Verwende die alten Buffer wieder, recycel sie.


Btw, der Shader lässt sich noch etwas optimieren. zB kannst du native_sqrt statt sqrt verwenden. Statt einfach x*x zu verwenden, kann man auch die pown(basis, exponent)-Funktion verwenden.
In der Kernel-Signatur übergibst du einzelne Variablen, statt Vektoren zu verwenden. float antX und float antY kann man als den Vector float2 ant zusammenfassen. Damit muss man nur einmal hin und her rechnen und x und y nicht getrennt dasselbe machen lassen. Die Grafikkarte freut das, ist doch Vektor- und Matritzenrechnung ihr Haupteinsatzfeld.
 
@ E-Laurin: Moderne GPUs sind "Skalararchitekturen", da ists egal ob man für Rechenoperationen Vektordatentypen oder Skalardatentypen verwendet. Des Weiteren ist es zumindest bei NVIDIA GPUs sofern man sequentiell bzw. coalesced aus dem Speicher liest oder schreibt egal ob man eine 4 Byte oder eine 8 Byte Ladeinstruktion verwendet, da die 8 Byte Ladeoperationen von der Hardware in zwei "Teil"-Ladeoperationen aufgeteilt und dann nacheinander abgearbeitet werden. (zuerst für die ersten 16 Threads eines Warps, dann für die 2. 16 Threads). Wie es sich bei AMD-GPUs mit den Ladeoperationen verhält, weiß ich leider nicht. Ebenso ist mir unbekannt, in wie weit Vektordatentypen auf CPUs etwas bringen, da die OpenCL CPU-Compiler auf "obskure Art und weise" nicht nur innerhalb eines Workitems, sondern über mehrere Workitems innerhalb einer Workgroup hinweg vektorisieren. Insbesondere ist mir unbekannt, wie man in OpenCL Speicherzugriffe auf der CPU optimiert; ich würde mich allerdings sehr freuen, falls jemand dafür Literatur für mich hätte.

Des Weiteren ist das Kernel extrem Speicherbandbreiten limitiert, da macht es keinen Unterschied ob man die schnellere native und ungenauere Mathematik der GPU verwendet. Auch ist es mehr oder weniger Standard x^2 mit x*x zu berechnen, da es billiger ist.

Wenn man das Kernel optimieren will, sollte man ersteinmal die Workgroupgröße ( localWorkSize[0] = 1; ) auf einen Wert wie 128 oder 256 setzen, damit sowohl GPU als auch CPU ihre SIMD Natur ausnutzen können und man keine Occupancy Probleme auf der GPU bekommt. Anschließend würde ich nicht darauf wetten, dass der Compiler annimmt (In CUDA darf er das nicht; in wie weit er das in OpenCL darf bin ich mir unsicher) , dass die einzelnen Zeiger der Kernelargumente jeweils restricted (http://en.wikipedia.org/wiki/Restrict) sind. Denn sind sie das nicht, so führt das ständige Dereferenzieren des Zeigers zu einer Vielzahl von Load/Store Operationen, welche zu großen Latenzen führen und die Caches unnötig belasten.


Deshalb würde ich das Kernel in etwa so aufbauen:

Code:
float VarA = PointerInA[LoadIndex];
float VarB = PointerInB[LoadIndex];
float VarC = PointerInC[LoadIndex];

float ResultA = BerechneEtwas(VarA ,VarB ,VarC );
float ResultB = BerechneEtwas(VarA ,VarB ,VarC );
float ResultC = BerechneEtwas(VarA ,VarB ,VarC );

PointerOutA[ResultIndex] = ResultA;
PointerOutB[ResultIndex] = ResultB;
PointerOutC[ResultIndex] = ResultC;
 
Zuletzt bearbeitet:
Hi danke für eure Tipps, werde ich bei der nächsten Version mal überlegen.
Momentan habe ich noch folgendes problem.

es soll ja immer wieder aufgerufen werden (pro Simulationsschritt)
{
clEnqueueNDRangeKernel (...)
clEnqueueReadBuffer (...) <- Lese die neuen Positionen aus

}

Doch wie gebe ich die neuen Positionen wieder in den Kernel hinein?
Ich habs mit löschen der alten Buffer und neues Erstellen versucht.
Allerdings bekomme ich immer noch Positionen mit dem Wert 0 zurück gelifert.

PHP:
int  openCLinit::startOpenCLmain(){
	

	 // Create memory objects that will be used as arguments to
	// kernel.  First create host memory arrays that will be
	// used to store the arguments to the kernel

	
	for (int i = 1; i <= 4; i++){
		clReleaseMemObject(memObjects[i]);
	}
	memObjects[1] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, antX, NULL);
	memObjects[2] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, antY, NULL);
	memObjects[3] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, targetX, NULL);
	memObjects[4] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
		sizeof(float) * ARRAY_SIZE, targetY, NULL);

	for (int i = 0; i < 11; i++){
		////////////////////////////////////// & bei memObjects
		clSetKernelArg(kernel, i, sizeof(cl_mem), memObjects[i]);
	}

	globalWorkSize[0] = ARRAY_SIZE ;
	localWorkSize[0] = 1;

	// Queue the kernel up for execution across the array
	clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL,
		globalWorkSize, localWorkSize,
		0, NULL, NULL);
	

	// Read the output buffer back to the Host
	clEnqueueReadBuffer(commandQueue, memObjects[9], CL_TRUE,
		0, ARRAY_SIZE * sizeof(float), newAntX,
		0, NULL, NULL);
	clEnqueueReadBuffer(commandQueue, memObjects[10], CL_TRUE,
		0, ARRAY_SIZE * sizeof(float), newAntY,
		0, NULL, NULL);
	//std::cout << clocki.getElapsedTime().asMilliseconds();
	//if (errNum != CL_SUCCESS)
	//{
		//print_cl_err(errNum);
		//Cleanup(context, commandQueue, program, kernel, memObjects);
		//return 1;
	//}
	
	std::cout << newAntX[0] << std::endl;
	std::cout << newAntY[0] << std::endl;

	clFinish(this->commandQueue);
	//std::cout << std::endl;
	//std::cout << "Executed program succesfully." << std::endl;

for (int f = 0; f < ARRAY_SIZE; f++){
					antX[f] = newAntX[f];
					antY[f] = newAntY[f];
	}

	// Output the result buffer
	/*for (int i = 0; i < ARRAY_SIZE; i++)
	{
		openclLogger << "ID : " << i << std::endl;
		openclLogger << "AntX: " << antX[i] << " AntY: " << antY[i] << std::endl;
		openclLogger << "TargetX: " << targetX[i] << " TargetY: " << targetY[i] << std::endl;
		openclLogger << "NewX: " << resultX[i] << " NewY: " << resultY[i] << std::endl << std::endl;
	}*/
	//Cleanup(context, commandQueue, program, kernel, memObjects);

	return 1;
}
 
Zuletzt bearbeitet von einem Moderator:
Das bringt bei mir leider nicht viel, wenn ich es davor einfüge.
Die Parameter müssten eig. richtig sein.

PHP:
clEnqueueWriteBuffer(commandQueue, memObjects[1], CL_FALSE, 0, sizeof(float) * ARRAY_SIZE, antX, 0, NULL, NULL); 
	clEnqueueWriteBuffer(commandQueue, memObjects[2], CL_FALSE, 0, sizeof(float) * ARRAY_SIZE, antY, 0, NULL, NULL); 
	clEnqueueWriteBuffer(commandQueue, memObjects[3], CL_FALSE, 0, sizeof(int) * ARRAY_SIZE, targetX, 0, NULL, NULL); 
	clEnqueueWriteBuffer(commandQueue, memObjects[4], CL_FALSE, 0, sizeof(int) * ARRAY_SIZE, targetY, 0, NULL, NULL);
Ergänzung ()

Er gibt auch CL_SUCCESS bei den hier obigen Kommandos aus.
 
hmmm wieso bringt dir das nichts ? Hast du auch dafür gesorgt clCreateBuffer Teil nur einmal in deinem Programm aufgerufen wird? Außerdem:

Code:
	for (int i = 1; i <= 4; i++){
clReleaseMemObject(memObjects[i]);
}

An der Stelle sollte es dich beim ersten Aufruf von openCLinit::startOpenCLmain wohl zerhauen, da memObjects noch nicht von OpenCL allokiert worden ist.
 
Ich dachte, dass kann ich indem ich zuerst clCreateBuffer aufrufe (immer wieder, denn irgendwie muss ich an die Werte ja ran um sie mit writeBuffer wieder in den Speicher zu schreiben) und nach Übergabe an den Kernel wieder mit clReleaseMemObject lösche .

Es kommen immer Null-Positionen raus (x == 0, y == 0)
 
Tut mir leid, ich habe wieder Null übersicht, was du gerade Tust. Poste doch einmal bitte deinen gesamten aktuellen relevanten QuellCode. (OpenCL Initialisierung, Mainloop, globale Variablen, Kernelaufruf und Datentransfer, Kernelquelltext)
 
Habs mal hier hochgeladen https://github.com/Furtano/ants :)
Der OpenCL Part findet hier statt https://github.com/Furtano/ants/blob/master/clInitFunctions.cpp
Ergänzung ()

So hab ein bissel den Ablauf geändert in OpenCL aber komme trotzdem nicht weiter :(.

Es muss an der Funktion int openCLinit::startOpenCLmain() in https://github.com/Furtano/ants/blob/master/clInitFunctions.cpp#L520 liegen.
Ergänzung ()

Hab noch mal ein paar Fixes auf github gemacht, funktioniert aber immer noch nicht :(
Ergänzung ()

Warum bekomme ich als X und Y Position immer 9.33265e-043 geliefert?
https://github.com/Furtano/ants
 
Zuletzt bearbeitet von einem Moderator:

Ähnliche Themen

F
Antworten
7
Aufrufe
1.648
Furtano
F
F
Antworten
6
Aufrufe
1.323
Zurück
Oben