2010-12-01 36 views
0

Je suis assez nouveau à OpenCL et je cours sous OS X 10.6 que la carte graphique Nvidia 330. Je travaille sur une simulation de tissu en C++ que j'ai réussi à écrire un noyau pour compiler et exécuter. Le problème est qu'il fonctionne plus lentement que sur le CPU sans OpenCL. Je crois que la raison en est que chaque fois que j'appelle la méthode update() pour faire des calculs, je suis en train de configurer le contexte et le périphérique, puis de recompiler le noyau à partir de la source. Pour résoudre ce problème, j'ai essayé d'encapsuler les différents types d'OpenCL dont j'avais besoin dans la classe de simulation de chiffon pour essayer de les stocker, puis j'ai créé un initCL() pour configurer ces valeurs. J'ai alors créé un runCL() pour exécuter le noyau. Etrangement, cela ne me pose un problème de mémoire que lorsque je sépare le contenu OpenCL en deux méthodes. Cela fonctionne bien si les initCL() et runCL() sont tous deux combinés en une seule méthode, ce qui explique pourquoi je suis un peu coincé.Problème avec la recompilation du noyau OpenCL ralentissant le programme et les problèmes de mémoire possibles à cause de cela

Le programme compile et s'exécute mais je reçois alors un SIGABRT ou EXC BAD ACCESS au point marqué dans le code runCL(). Quand je reçois un SIGABRT, j'obtiens l'erreur CL_INVALID_COMMAND_QUEUE mais je ne peux pas m'expliquer pour la vie de moi pourquoi cela ne se produit que lorsque je divise les deux méthodes. Je reçois parfois un SIGABRT quand l'assertion échoue ce qui est à prévoir mais d'autres fois je reçois juste l'erreur d'accès de mémoire mauvaise en essayant d'écrire dans le tampon.

Aussi si quelqu'un peut me dire un meilleur moyen/le droit de le faire ou si la recompilation JIT n'est pas ce qui ralentit mon code, alors je serais très reconnaissant parce que je regarde cela depuis trop longtemps longue!

Merci,

Jon

L'initialisation des variables OpenCL 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); 

} 

La méthode pour exécuter le noyau code:

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

Question également publié ici http://www.khronos.org/message_boards/viewtopic.php?f=37&t=3296 –

Répondre

1

Problème résolu! Au bas de la méthode runCL(), je "libérais" tous mes cl types, je pensais juste libérer quelques cl_mem mais en y regardant de plus près je libérais le contexte etc. Une erreur évidente et ennuyante comme toujours :).

Merci à andrew.brownsword sur les forums de Khronos pour repérer celui-ci.

+1

Merci d'avoir pris le temps de revenir et de nous dire comment vous avez résolu le problème. – Tom

0

Bravo pour la résolution du problème principal. En ce qui concerne les performances, numParts est-il un grand nombre? La taille de travail globale doit être grande pour garantir que vous saturer le périphérique avec du travail, par ex. des dizaines de milliers. Idéalement, la taille du travail local (lorsqu'il est linéarisé) serait un multiple de 32, la meilleure valeur dépendra de votre noyau.

Il est courant de définir une taille de travail locale à une constante ou à une valeur dépendant de votre noyau (vous pouvez demander des informations comme la taille maximale du travail local) car numParts/8 peut provoquer des échecs de lancement s'il devient trop important. sur le noyau spécifique et le périphérique spécifique).

+0

Merci d'avoir signalé cela. Je l'avais testé avec une assez petite pièce de tissu 32x32 car je l'exécutais sur le cpu auparavant.J'ai trouvé ce problème de taille de travail après avoir corrigé le problème principal et déplacé tout sur le GPU et ainsi pourrait augmenter la taille du tissu. Je vais essayer d'utiliser une constante comme vous l'avez suggéré et je ferai quelques recherches pour trouver la meilleure valeur. –