2010-12-01 5 views
0

Ich bin ziemlich neu zu OpenCL und ich betreibe OS X 10.6, die die Nvidia 330 Grafikkarte. Ich arbeite an einer Stoffsimulation in C++, die es mir ermöglicht hat, einen Kernel zu schreiben, der kompiliert und ausgeführt wird. Das Problem ist, dass es langsamer läuft als auf der CPU ohne OpenCL. Ich glaube der Grund dafür ist, dass jedes Mal, wenn ich die update() -Methode aufruft, um einige Berechnungen durchzuführen, ich den Kontext und das Gerät setze und dann den Kernel aus der Quelle neu kompiliere.Problem mit OpenCL Kernel rekompilieren Verlangsamung Programm und mögliche Speicherprobleme wegen der

Um dies zu lösen, habe ich versucht, die verschiedenen OpenCL-Typen, die ich brauchte, in die Stoffsimulationsklasse zu kapseln und zu versuchen, sie dort zu speichern, und dann ein initCL() erstellt, um diese Werte einzurichten. Ich habe dann eine runCL() erstellt, um den Kernel auszuführen. Seltsamerweise gibt mir das nur ein Speicherproblem, wenn ich die OpenCL-Sachen in zwei Methoden unterteile. Es funktioniert gut, wenn die initCL() und runCL() beide in einer Methode kombiniert werden, weshalb ich ein wenig fest bin.

Das Programm kompiliert und läuft, aber ich bekomme dann eine SIGABRT oder EXC BAD ACCESS an der Stelle im RunCL() - Code markiert. Wenn ich ein SIGABRT bekomme, bekomme ich den Fehler CL_INVALID_COMMAND_QUEUE, aber ich kann nicht für das Leben von mir herausfinden, warum dies nur passiert, wenn ich die zwei Methoden aufspalte. Ich bekomme manchmal ein SIGABRT, wenn die Assertion fehlschlägt, was zu erwarten ist, aber manchmal bekomme ich nur den schlechten Speicherzugriffsfehler, wenn ich versuche, in den Puffer zu schreiben.

Auch wenn mir jemand einen besseren Weg/das Recht dazu sagen kann oder wenn das JIT-Re-Compilieren nicht das ist, was meinen Code verlangsamt, dann wäre ich sehr dankbar, weil ich das auch schon lange angestarrt habe lange!

Danke,

Jon

Die Initialisierung von OpenCL Variablen Code:

int VPESimulationCloth::initCL(){ 
    // Find the CPU CL device, as a fallback 
    err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_CPU, 1, &device, NULL); 
    assert(err == CL_SUCCESS); 

    // Find the GPU CL device, this is what we really want 
// If there is no GPU device is CL capable, fall back to CPU 
    err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, 1, &device, NULL); 
if (err != CL_SUCCESS) err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_CPU, 1, &device, NULL); 
assert(device); 

// Get some information about the returned device 
cl_char vendor_name[1024] = {0}; 
cl_char device_name[1024] = {0}; 
err = clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(vendor_name), 
       vendor_name, &returned_size); 
err |= clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(device_name), 
       device_name, &returned_size); 
assert(err == CL_SUCCESS); 
//printf("Connecting to %s %s...\n", vendor_name, device_name); 

// Now create a context to perform our calculation with the 
// specified device 
context = clCreateContext(0, 1, &device, NULL, NULL, &err); 
assert(err == CL_SUCCESS); 

// And also a command queue for the context 
cmd_queue = clCreateCommandQueue(context, device, 0, NULL); 

// Load the program source from disk 
// The kernel/program should be in the resource directory 
const char * filename = "clothSimKernel.cl"; 
char *program_source = load_program_source(filename); 


program[0] = clCreateProgramWithSource(context, 1, (const char**)&program_source, 
          NULL, &err); 
if (!program[0]) 
{ 
    printf("Error: Failed to create compute program!\n"); 
    return EXIT_FAILURE; 
} 
assert(err == CL_SUCCESS); 

err = clBuildProgram(program[0], 0, NULL, NULL, NULL, NULL); 
if (err != CL_SUCCESS) 
{ 
    char build[2048]; 
    clGetProgramBuildInfo(program[0], device, CL_PROGRAM_BUILD_LOG, 2048, build, NULL); 
    printf("Build Log:\n%s\n",build); 
    if (err == CL_BUILD_PROGRAM_FAILURE) { 
     printf("CL_BUILD_PROGRAM_FAILURE\n"); 
    } 
} 
if (err != CL_SUCCESS) { 
    cout<<getErrorDesc(err)<<endl; 
} 
assert(err == CL_SUCCESS); 
//writeBinaries(); 
// Now create the kernel "objects" that we want to use in the example file 
kernel[0] = clCreateKernel(program[0], "clothSimulation", &err); 

} 

Verfahren den Kernel Code auszuführen:

int VPESimulationCloth::runCL(){ 

// Find the GPU CL device, this is what we really want 
// If there is no GPU device is CL capable, fall back to CPU 
err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, 1, &device, NULL); 
if (err != CL_SUCCESS) err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_CPU, 1, &device, NULL); 
assert(device); 

// Get some information about the returned device 
cl_char vendor_name[1024] = {0}; 
cl_char device_name[1024] = {0}; 
err = clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(vendor_name), 
       vendor_name, &returned_size); 
err |= clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(device_name), 
       device_name, &returned_size); 
assert(err == CL_SUCCESS); 
//printf("Connecting to %s %s...\n", vendor_name, device_name); 

// Now create a context to perform our calculation with the 
// specified device 

//cmd_queue = clCreateCommandQueue(context, device, 0, NULL); 
//memory allocation 
cl_mem nowPos_mem, prevPos_mem, rForce_mem, mass_mem, passive_mem, canMove_mem,numPart_mem, theForces_mem, numForces_mem, drag_mem, answerPos_mem; 

// Allocate memory on the device to hold our data and store the results into 
buffer_size = sizeof(float4) * numParts; 

// Input arrays 
//------------------------------------ 
// This is where the error occurs 
nowPos_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, nowPos_mem, CL_TRUE, 0, buffer_size, 
        (void*)nowPos, 0, NULL, NULL); 
if (err != CL_SUCCESS) { 
    cout<<getErrorDesc(err)<<endl; 
} 
assert(err == CL_SUCCESS); 
//------------------------------------ 
prevPos_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, prevPos_mem, CL_TRUE, 0, buffer_size, 
        (void*)prevPos, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 
rForce_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, rForce_mem, CL_TRUE, 0, buffer_size, 
        (void*)rForce, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 
mass_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, mass_mem, CL_TRUE, 0, buffer_size, 
        (void*)mass, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 
answerPos_mem = clCreateBuffer(context, CL_MEM_READ_WRITE, buffer_size, NULL, NULL); 
//uint buffer 
buffer_size = sizeof(uint) * numParts; 
passive_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, passive_mem, CL_TRUE, 0, buffer_size, 
        (void*)passive, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 
canMove_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, canMove_mem, CL_TRUE, 0, buffer_size, 
        (void*)canMove, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 

buffer_size = sizeof(float4) * numForces; 
theForces_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err = clEnqueueWriteBuffer(cmd_queue, theForces_mem, CL_TRUE, 0, buffer_size, 
        (void*)theForces, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 

//drag float 
buffer_size = sizeof(float); 
drag_mem = clCreateBuffer(context, CL_MEM_READ_ONLY, buffer_size, NULL, NULL); 
err |= clEnqueueWriteBuffer(cmd_queue, drag_mem, CL_TRUE, 0, buffer_size, 
        (void*)drag, 0, NULL, NULL); 
assert(err == CL_SUCCESS); 

// Now setup the arguments to our kernel 
err = clSetKernelArg(kernel[0], 0, sizeof(cl_mem), &nowPos_mem); 
err |= clSetKernelArg(kernel[0], 1, sizeof(cl_mem), &prevPos_mem); 
err |= clSetKernelArg(kernel[0], 2, sizeof(cl_mem), &rForce_mem); 
err |= clSetKernelArg(kernel[0], 3, sizeof(cl_mem), &mass_mem); 
err |= clSetKernelArg(kernel[0], 4, sizeof(cl_mem), &passive_mem); 
err |= clSetKernelArg(kernel[0], 5, sizeof(cl_mem), &canMove_mem); 
err |= clSetKernelArg(kernel[0], 6, sizeof(cl_mem), &numParts); 
err |= clSetKernelArg(kernel[0], 7, sizeof(cl_mem), &theForces_mem); 
err |= clSetKernelArg(kernel[0], 8, sizeof(cl_mem), &numForces); 
err |= clSetKernelArg(kernel[0], 9, sizeof(cl_mem), &drag_mem); 
err |= clSetKernelArg(kernel[0], 10, sizeof(cl_mem), &answerPos_mem); 
if (err != CL_SUCCESS) { 
    cout<<getErrorDesc(err)<<endl; 
} 
assert(err == CL_SUCCESS); 
// Run the calculation by enqueuing it and forcing the 
// command queue to complete the task 
size_t global_work_size = numParts; 
size_t local_work_size = global_work_size/8; 
err = clEnqueueNDRangeKernel(cmd_queue, kernel[0], 1, NULL, 
        &global_work_size, &local_work_size, 0, NULL, NULL); 
if (err != CL_SUCCESS) { 
    cout<<getErrorDesc(err)<<endl; 
} 

assert(err == CL_SUCCESS); 
//clFinish(cmd_queue); 

// Once finished read back the results from the answer 
// array into the results array 
//reset the buffer first 
buffer_size = sizeof(float4) * numParts; 
err = clEnqueueReadBuffer(cmd_queue, answerPos_mem, CL_TRUE, 0, buffer_size, 
        answerPos, 0, NULL, NULL); 
if (err != CL_SUCCESS) { 
    cout<<getErrorDesc(err)<<endl; 
} 


//cl mem 
clReleaseMemObject(nowPos_mem); 
clReleaseMemObject(prevPos_mem); 
clReleaseMemObject(rForce_mem); 
clReleaseMemObject(mass_mem); 
clReleaseMemObject(passive_mem); 
clReleaseMemObject(canMove_mem); 
clReleaseMemObject(theForces_mem); 
clReleaseMemObject(drag_mem); 
clReleaseMemObject(answerPos_mem); 
clReleaseCommandQueue(cmd_queue); 
clReleaseContext(context); 
assert(err == CL_SUCCESS); 
return err; 

} 
+0

Frage auch hier gepostet http://www.khronos.org/message_boards/viewtopic.php?f=37&t=3296 –

Antwort

1

Problem gelöst! Am Ende der runCL() - Methode habe ich alle meine cl-Typen "befreit", obwohl ich gerade etwas cl_mem freigegeben habe, aber bei näherer Betrachtung habe ich den Kontext usw. freigegeben. Ein offensichtlicher und nerviger Fehler wie immer :).

Dank andrew.brownsword auf den Khronos-Foren für die Aufdeckung dieser.

+1

Danke, dass Sie sich die Zeit genommen haben, zurückzukommen und uns zu erzählen, wie Sie den Absturz gelöst haben. – Tom

0

Gut gemacht, um das Hauptproblem zu beheben.

In Bezug auf die Leistung, ist numParts eine große Zahl? Die globale Arbeitsgröße sollte groß sein, um sicherzustellen, dass Sie das Gerät mit Arbeit sättigen, z. Zehntausende. Idealerweise ist die lokale Arbeitsgröße (wenn sie linearisiert wird) ein Vielfaches von 32, der beste Wert hängt von Ihrem Kernel ab.

Es ist üblich, die lokale Arbeitsgröße auf eine Konstante oder einen Wert abhängig von Ihrem Kernel zu setzen (Sie können nach Informationen wie der maximalen lokalen Arbeitsgröße suchen), da dies zu einem Fehler führen kann auf dem spezifischen Kernel und dem spezifischen Gerät).

+0

Vielen Dank, dass Sie darauf hingewiesen haben. Ich hatte das mit einem ziemlich kleinen 32x32-Tuch getestet, als ich es vorher auf der CPU lief.Ich fand dieses Problem mit der Arbeitsgröße, nachdem ich das Hauptproblem behoben und alles auf die GPU verschoben hatte, um die Stoffgröße zu erhöhen. Ich werde versuchen, eine Konstante zu verwenden, wie Sie vorgeschlagen haben, und ich werde einige Abfragen durchführen, um den besten Wert zu finden. –