2017-09-23 24 views
1

저는 OpenCL 1.1 EP를 지원하는 i.MX6q 보드로 실행하기위한 샘플 코드를 개발하기 위해 현재 임베디드 및 OpenCL에 대한 새로운 지식을 보유하고 있습니다.OpenCL 1.1에서 CL_MEM_USE_HOST_PTR을 사용하여 간단히 복사/붙여 넣기 값을 만들려고하는데 왜 작동하지 않습니까?

처음부터 시작해야하므로 these tutorials, the OpenCL 1.1 Reference pagesthis OpenCL example을 수행하여 첫 번째 OpenCL 구현/응용 프로그램을 만들었습니다.

기본적으로 내가하고 싶은 것은 보드에서 실행되는 "성능 테스트"를 개발하는 것입니다. 이것은 두 개의 int 배열 (입출력)을 가지며 첫 번째 배열을 임의의 값으로 채우고 OpenCL 작업 항목을 사용하여 출력 배열에 붙여 넣습니다.

clEnqueue (읽기/쓰기) 버퍼 함수와 clCreateBuffer 플래그 (특히 CL_MEM_USE_HOST_PTR) 사이에 혼란스러워 보였으므로 살펴보고 연습 해보기로했습니다.

내 코드가 제대로 컴파일 내가 출력 배열 값을 읽고있을 때, 그들은 여전히 ​​여기에 0

숙박 그러나 제대로 실행

내 코드 (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); 
} 

그리고 여기에 내 OpenCL 커널이 있습니다 :

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

    output[id] = input[id]; 
} 

지금 당장은 그것을 최적화하지 않고 작동 시키려고합니다. 그리고 나는 여기 저기에 좋은 점을 놓치고 있다고 생각하지만 나는 그들을 잡을 수 없다. 제 생각에는 clCreateBuffer 플래그를 혼동하고 있습니다.

여러분이 저에게 계몽하고 도와 주시겠습니까?


편집 : 업데이트 된 코드 + 새로운 정보를 정기적으로!

이 값이 잘 붙여 넣은 것 같다,하지만 커널 작업 그룹 크기에있어서, 상기 CL_DEVICE_MAX_WORK_GROUP_SIZE 1024 반환하고 CL_KERNEL_WORK_GROUP_SIZE는 1024을 반환 (도 이상이다). 따라서 내 배열의 처음 1024 개의 정수는 잘 복사/붙여 넣기는되지만 더 이상 작동하지 않습니다. 이를 확인하기 위해 global_work_group_size를 수동으로 32로 설정하고 프로그램을 다시 실행 한 다음 처음 32 개의 정수 만 올바르게 붙여 넣습니다. 나는 정말로 여기서 무슨 일이 일어나고 있는지 이해하지 못한다.

답변

0

내 랩톱과 i.MX6q 보드 모두에서 사용할 수 있다고 생각합니다.

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); 
} 

을 그래서 당신이 볼 수 있듯이, 난 그냥 오픈 CL 1.1 EP를 사용하여 다른 하나에 하나 개의 배열에서 640 * 480 (307200) 정수를 복사하고 : 여기

작동 코드입니다.

호스트 측에서 두 메모리 버퍼를 모두 할당하고 호스트 포인터를 통해 OpenCL에 사용하도록 명령했습니다 (올바른 경우 memcpy가 없음을 의미). 여기

내 노트북의 출력 (지포스 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 

는 I에서 출력됩니다.MX6q 솜 (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 

잘못 무엇입니까?
나는 내가 잘못 global_work_sizelocal_work_size clEnqueueNDRangeKernel 기능 값을주고 있었다 생각합니다. 그러나, 나는 아직도 그들이 어떻게 작동하고 어떻게 계산하는지 이해하지 못합니다. 나는 여전히 그 값들과 CL_KERNEL_WORK_GROUP_SIZE의 차이점을 이해하지 못하고 OpenCL 컴파일러에 의해 계산 된 커널 작업 그룹 크기가 어떻게되는지를 이해하지 못합니다. SoM과 내 랩톱간에 CL_KERNEL_WORK_GROUP_SIZE가 다른 이유는 무엇입니까? 나는 같은 커널을 사용한다.

추천할만한 최적화가 있습니까?
나에게 추천할만한 최적화가 있다면 감사드립니다! 이 모든 컨텍스트는 이미지 처리를 수행하고 OpenCL에서 작동하도록 알고리즘을 개발하는 방법을 배웁니다. (이 SoM에서 OpenCV를 사용할 수 없으므로)

+0

예, 잘못된 전체 크기 및 로컬 작업 그룹 크기를 사용하고있는 것에 동의합니다. 전역 크기는 실행할 총 스레드 수입니다. 로컬 작업 그룹 크기는 공유 로컬 메모리 또는 기타 작업 그룹 개념을 사용하는 경우를위한 것입니다. 초보자라면 무시하고 NULL을 전달하고 런타임에서 하나를 선택하도록 할 수 있습니다 (2의 거듭 제곱 또는 적어도 CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE의 배수와 같은 비 프라임 전역 크기를 사용하면 더 좋을 것입니다). – Dithermaster