2017-09-23 2 views
1

Ich bin ziemlich neu in der Embedded und OpenCL, ich versuche derzeit, einen Beispielcode zu einer i.MX6q-Platine, die OpenCL 1.1 EP unterstützt ausführen zu entwickeln.Versuchen Sie, in OpenCL 1.1 mit CL_MEM_USE_HOST_PTR einfach Werte zu kopieren/einfügen, warum funktioniert das nicht?

Ich musste von vorne anfangen, also folgte ich these tutorials, the OpenCL 1.1 Reference pages und auch this OpenCL example, um meine erste OpenCL-Implementierung/Anwendung zu machen.

Grundsätzlich möchte ich einen "Performance Test" entwickeln, der auf der Platine läuft. Es besteht darin, zwei int-Arrays (Eingabe und Ausgabe) zu haben, die erste mit zufälligen Werten zu füllen und sie mit OpenCL-Workitems in das Ausgabe-Array einzufügen.

Ich war ziemlich verwirrt zwischen clEnqueue (Lesen/Schreiben) Pufferfunktionen und den clCreateBuffer Flags (besonders CL_MEM_USE_HOST_PTR), also beschloss ich, einen Blick darauf zu werfen und damit zu üben.

Mein Code kompiliert korrekt und läuft jedoch richtig, wenn ich die Ausgabe-Array Werte gerade lese, sie bleiben immer noch auf 0

Hier ist mein Code (das ist C++):

void buffer_copy(char* kernelfile) 
{ 
    cl_platform_id  platform_id; 
    cl_device_id  device_id; 
    cl_context   context; 
    cl_command_queue cmd_queue; 
    cl_program   program; 

    // Retrieving all the OpenCL data needed 
    // to start the performance test 
    platform_id = get_platform(); 
    device_id = get_device(platform_id); 
    context = get_context(platform_id, device_id); 
    cmd_queue = get_command_queue(context, device_id); 
    program = get_program(context, kernelfile); 

    cl_mem  buffer_input, buffer_output; 
    size_t  buffer_width = 640, buffer_height = 480; 
    size_t  buffer_size = buffer_width * buffer_height; 
    cl_kernel kernel; 
    cl_int  err = 0; 
    char*  options = "-Werror -cl-std=CL1.1"; 

    int   data_input[buffer_size]; 
    int   data_output[buffer_size]; 

    // Assigning random values in the data_input array and 
    // initializing the data_output array to zero-values 
    srand(time(NULL)); 
    for (size_t index = 0; index < buffer_size; ++index) 
    { 
     data_input[index] = rand(); 
     data_output[index] = 0; 
    } 

    // Creating OpenCL buffers 
    buffer_input = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_input, &err); 
    assert(err == CL_SUCCESS); 
    buffer_output = clCreateBuffer(context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_output, &err); 
    assert(err == CL_SUCCESS); 

    err = clBuildProgram(program, 1, &device_id, options, NULL, NULL); 
    assert(err == CL_SUCCESS); 
    kernel = clCreateKernel(program, "buffer_copy", &err); 
    assert(err == CL_SUCCESS); 

    clSetKernelArg(kernel, 0, sizeof(cl_mem), &buffer_input); 
    clSetKernelArg(kernel, 1, sizeof(cl_mem), &buffer_output); 

    size_t device_max_work_group_size; 
    size_t global_work_size, local_work_size; 
    size_t preferred_work_group_size_multiple; 

    cl_ulong global_mem_size, max_mem_alloc_size; 
    clGetDeviceInfo(device_id, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &global_mem_size, NULL); 
    clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_mem_alloc_size, NULL); 
    clGetDeviceInfo(device_id, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &device_max_work_group_size, NULL); 
    std::cout << "Global device memory size: " << global_mem_size << " bytes" << std::endl; 
    std::cout << "Device max memory allocation size: " << max_mem_alloc_size << " bytes" << std::endl; 
    std::cout << "Device max work group size: " << device_max_work_group_size << std::endl; 

    clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &global_work_size, NULL); 
    std::cout << "global_work_size value: " << global_work_size << std::endl; 

    clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(size_t), &preferred_work_group_size_multiple, NULL); 
    local_work_size = global_work_size/preferred_work_group_size_multiple; 
    std::cout << "local_work_size value: " << local_work_size << std::endl; 

    cl_event events[2]; 
    err = clEnqueueNDRangeKernel(cmd_queue, kernel, 1, NULL, &global_work_size, &local_work_size, 0, 0, &events[0]); 
    assert (err == CL_SUCCESS); 
    err = clEnqueueReadBuffer(cmd_queue, buffer_output, CL_TRUE, 0, buffer_size * sizeof(int), data_output, 0, NULL, &events[1]); 
    assert (err == CL_SUCCESS); 
    err = clWaitForEvents(2, events); 
    assert (err == CL_SUCCESS); 

    for (size_t index = 0; index < buffer_size; ++index) 
    { 
     if (data_input[index] != data_output[index]) 
     { 
      std::cerr << "Error, values differ (at index " << index << ")." << std::endl; 
      break; 
     } 
     else 
     { 
      //std::cout << "data_input[index] =\t" << data_input[index] << std::endl; 
      //std::cout << "data_output[index] =\t" << data_output[index] << std::endl; 
     } 
    } 

    cl_ulong time_start, time_end; 
    double  total_time; 
    clGetEventProfilingInfo(events[0], CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL); 
    clGetEventProfilingInfo(events[1], CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL); 
    total_time = time_end - time_start; 
    std::cout << "Execution time in milliseconds: " << (total_time/1000000.0) << " ms" << std::endl; 

    clReleaseKernel(kernel); 
    clReleaseProgram(program); 
    clReleaseMemObject(buffer_input); 
    clReleaseMemObject(buffer_output); 
    clReleaseCommandQueue(cmd_queue); 
    clReleaseContext(context); 
} 

Und hier ist mein OpenCL-Kernel:

__kernel void buffer_copy(__global int* input, __global int* output) 
{ 
    int id = get_global_id(0); 

    output[id] = input[id]; 
} 

Im Moment bin ich nur zu machen versucht, es funktioniert, nicht es zu optimieren. Und ich denke, ich vermisse hier und da gute Punkte, aber ich kann sie nicht fangen. Meiner Meinung nach verwechsle ich die clCreateBuffer Flags.

Könnt ihr mich aufklären und mir dabei helfen?


EDIT: aktualisierten Code + neue Infos!

Es scheint, dass Werte gut eingefügt werden, aber nur entsprechend der Größe der Kernel-Arbeitsgruppe: CL_DEVICE_MAX_WORK_GROUP_SIZE gibt 1024 zurück und CL_KERNEL_WORK_GROUP_SIZE gibt auch 1024 zurück (was auch merkwürdig ist). Also die ersten 1024 ganzen Zahlen meines Arrays werden gut kopiert/eingefügt, aber danach funktioniert es nicht mehr. Um dies zu überprüfen, setze ich global_work_group_size manuell auf 32, führe mein Programm erneut aus und dann werden die ersten 32 ganzen Zahlen korrekt eingefügt. Ich verstehe wirklich nicht, was hier vor sich geht.

Antwort

0

Ich denke, ich konnte es für meinen Laptop und das i.MX6q-Board arbeiten lassen. Hier

ist der Code, der funktioniert:

void buffer_copy(char* kernelfile) 
{ 
    cl_platform_id  platform_id; 
    cl_device_id  device_id; 
    cl_context   context; 
    cl_command_queue cmd_queue; 
    cl_program   program; 

    // Retrieving all the OpenCL data needed 
    // to start the performance test 
    platform_id = get_platform(); 
    device_id = get_device(platform_id); 
    context = get_context(platform_id, device_id); 
    cmd_queue = get_command_queue(context, device_id); 
    program = get_program(context, kernelfile); 

    cl_mem  buffer_input, buffer_output; 
    size_t  buffer_width = 640, buffer_height = 480; 
    size_t  buffer_size = buffer_width * buffer_height; 
    cl_kernel kernel; 
    cl_int  err = 0; 
    char*  options = "-Werror -cl-std=CL1.1"; 

    int   data_input[buffer_size]; 
    int   data_output[buffer_size]; 

    // Assigning random values in the data_input array and 
    // initializing the data_output array to zero-values 
    srand(time(NULL)); 
    for (size_t index = 0; index < buffer_size; ++index) 
    { 
     data_input[index] = rand(); 
     data_output[index] = 0; 
    } 

    // Creating OpenCL buffers 
    buffer_input = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_input, &err); 
    assert(err == CL_SUCCESS); 
    buffer_output = clCreateBuffer(context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, buffer_size * sizeof(int), data_output, &err); 
    assert(err == CL_SUCCESS); 

    err = clBuildProgram(program, 1, &device_id, options, NULL, NULL); 
    assert(err == CL_SUCCESS); 
    kernel = clCreateKernel(program, "buffer_copy", &err); 
    assert(err == CL_SUCCESS); 

    clSetKernelArg(kernel, 0, sizeof(cl_mem), &buffer_input); 
    clSetKernelArg(kernel, 1, sizeof(cl_mem), &buffer_output); 

    cl_ulong global_mem_size = 0, max_mem_alloc_size = 0; 
    size_t  device_max_work_group_size = 0; 
    size_t  kernel_work_group_size = 0; 
    size_t  preferred_work_group_size_multiple = 0; 
    clGetDeviceInfo(device_id, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &global_mem_size, NULL); 
    clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_mem_alloc_size, NULL); 
    clGetDeviceInfo(device_id, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &device_max_work_group_size, NULL); 
    clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &kernel_work_group_size, NULL); 
    clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(size_t), &preferred_work_group_size_multiple, NULL); 
    std::cout << "CL_DEVICE_GLOBAL_MEM_SIZE : " << global_mem_size << " bytes" << std::endl; 
    std::cout << "CL_DEVICE_MAX_MEM_ALLOC_SIZE : " << max_mem_alloc_size << " bytes" << std::endl; 
    std::cout << "CL_DEVICE_MAX_WORK_GROUP_SIZE : " << device_max_work_group_size << std::endl; 
    std::cout << "CL_KERNEL_WORK_GROUP_SIZE : " << kernel_work_group_size << std::endl; 
    std::cout << "CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE : " << preferred_work_group_size_multiple << std::endl; 

    cl_event events[2]; 
    err = clEnqueueNDRangeKernel(cmd_queue, kernel, 1, NULL, &buffer_size, &kernel_work_group_size, 0, NULL, &events[0]); 
    assert (err == CL_SUCCESS); 
    err = clEnqueueReadBuffer(cmd_queue, buffer_output, CL_TRUE, 0, buffer_size * sizeof(int), data_output, 1, &events[0], &events[1]); 
    assert (err == CL_SUCCESS); 
    err = clWaitForEvents(2, events); 
    assert (err == CL_SUCCESS); 

    for (size_t index = 0; index < buffer_size; ++index) 
    { 
     if (data_input[index] != data_output[index]) 
     { 
      std::cerr << "Error, values differ (at index " << index << ")." << std::endl; 
      break; 
     } 
    } 

    cl_ulong time_start, time_end; 
    double  total_time; 

    clGetEventProfilingInfo(events[0], CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL); 
    clGetEventProfilingInfo(events[0], CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL); 
    total_time = time_end - time_start; 
    std::cout << "clEnqueueNDRangeKernel execution time in milliseconds: " << (total_time/1000000.0) << " ms" << std::endl; 
    clGetEventProfilingInfo(events[1], CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL); 
    clGetEventProfilingInfo(events[1], CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL); 
    total_time = time_end - time_start; 
    std::cout << "clEnqueueReadBuffer execution time in milliseconds: " << (total_time/1000000.0) << " ms" << std::endl; 

    clReleaseKernel(kernel); 
    clReleaseProgram(program); 
    clReleaseMemObject(buffer_input); 
    clReleaseMemObject(buffer_output); 
    clReleaseCommandQueue(cmd_queue); 
    clReleaseContext(context); 
} 

So wie Sie sehen können, bin ich das Kopieren nur 640 * 480 (307200) ganze Zahlen von einem Array zu einem anderen 1.1 EP mit OpenCL.

Ich habe beide Speicherpuffer von der Host-Seite zugeteilt und sagte OpenCL, sie über die Host-Zeiger zu verwenden (was bedeutet, dass kein memcpy, wenn ich richtig bin).

Hier die Ausgabe von meinem Laptop ist (arbeitet an GeForce GTX 765m):

CL_DEVICE_GLOBAL_MEM_SIZE : 2094923776 bytes 
CL_DEVICE_MAX_MEM_ALLOC_SIZE : 523730944 bytes 
CL_DEVICE_MAX_WORK_GROUP_SIZE : 1024 
CL_KERNEL_WORK_GROUP_SIZE : 1024 
CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE : 32 

clEnqueueNDRangeKernel execution time in milliseconds: 0.061856 ms 
clEnqueueReadBuffer execution time in milliseconds: 0.100544 ms 

Hier ist die Ausgabe vom i.MX6q SoM (Arbeit an Vivante GC2000 GPU):

CL_DEVICE_GLOBAL_MEM_SIZE : 67108864 bytes 
CL_DEVICE_MAX_MEM_ALLOC_SIZE : 33554432 bytes 
CL_DEVICE_MAX_WORK_GROUP_SIZE : 1024 
CL_KERNEL_WORK_GROUP_SIZE : 176 
CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE : 16 

clEnqueueNDRangeKernel execution time in milliseconds: 4.463 ms 
clEnqueueReadBuffer execution time in milliseconds: 7.199 ms 

Was war falsch? Ich
denken, dass ich was falsch global_work_size und local_work_size Werte an die clEnqueueNDRangeKernel Funktion. Dennoch verstehe ich immer noch nicht wirklich, wie sie funktionieren und wie sie berechnet werden. Ich verstehe immer noch nicht den Unterschied zwischen diesen Werten und CL_KERNEL_WORK_GROUP_SIZE und wie wird die Größe der Kernel-Arbeitsgruppe von OpenCL-Compiler berechnet. Warum unterscheidet sich CL_KERNEL_WORK_GROUP_SIZE zwischen dem SoM und meinem Laptop? Ich benutze jedoch den gleichen Kernel.

Irgendwelche Optimierungen zu empfehlen?
Wenn Sie irgendwelche Optimierungen zu mir empfehlen, wäre ich dankbar! In diesem Kontext lernen Sie, wie Sie Bildverarbeitungsfunktionen ausführen und Algorithmen entwickeln, um sie mit OpenCL arbeiten zu lassen (da ich OpenCV auf diesem SoM nicht verwenden kann).

+0

Ja, ich stimme zu, dass Sie falsche globale Größe und lokale Arbeitsgruppengrößen verwendet haben. Ihre globale Größe gibt an, wie viele Threads insgesamt ausgeführt werden sollen. Die Größe Ihrer lokalen Arbeitsgruppe wird verwendet, wenn Sie gemeinsam genutzten lokalen Speicher oder andere Arbeitsgruppenkonzepte verwenden. Wenn Sie ein Anfänger sind, können Sie es ignorieren und NULL übergeben, und lassen Sie die Laufzeit eine für Sie wählen (es wird besser, wenn Sie globale Nicht-Prime-Größe verwenden, wie Potenzen von zwei oder mindestens ein Vielfaches von CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE). – Dithermaster

Verwandte Themen