Рассказы о математике | страница 41




Инициализировать подготовку вычислений:

int gpu = 1;

clGetDeviceIDs(NULL, gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU, 1, &device_id, NULL);

      cl_context context = clCreateContext(0, 1, &device_id, NULL, NULL, &err);

      cl_command_queue commands = clCreateCommandQueue(context, device_id, 0, &err);


На этом этапе можно выбрать где будут производиться вычисления, на основном процессоре или на GPU. Для отладки удобнее основной процессор, окончательные расчеты быстрее на GPU.


Подготовить данные

#define DATA_SIZE 1024

      cl_uint *data = (cl_uint*)malloc(sizeof(cl_uint) * DATA_SIZE);

      cl_uint *results = (cl_uint*)malloc(sizeof(cl_uint) * DATA_SIZE);


Загрузить данные и программу из основной памяти в GPU

      cl_program program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err);

      clBuildProgram(program, 0, NULL, NULL, NULL, NULL);

      cl_kernel kernel = clCreateKernel(program, "primes", &err);

      cl_mem output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(cl_uint) * count, NULL, NULL);


      clEnqueueWriteBuffer(commands, input, CL_TRUE, 0, sizeof(cl_uint) * count, data, 0, NULL, NULL);

      clSetKernelArg(kernel, 0, sizeof(cl_mem), &output);


      clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(local), &local, NULL);


Запустить вычисления на GPU и дождаться их завершения

      global = DATA_SIZE;

      clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0, NULL, NULL);

      clFinish(commands);


Загрузить результаты обратно из GPU в основную память

      clEnqueueReadBuffer( commands, output, CL_TRUE, 0, sizeof(cl_uint) * count, results, 0, NULL, NULL );


Освободить данные

      free(data);

      free(results);

clReleaseMemObject(input);

clReleaseMemObject(output);

clReleaseProgram(program);

clReleaseKernel(kernel);

clReleaseCommandQueue(commands);

clReleaseContext(context);


Как можно видеть, процесс довольно-таки громоздкий, но оно того стоит. Для примера, проверка простоты 250000 чисел заняла на процессоре Core i5 около 6 секунд. И всего лишь 0.5 секунд заняло выполнение вышеприведенного кода на встроенной видеокарте. Для дешевого нетбука с процессором Intel Atom этот же код выполнялся 34 секунды на основном процессоре, и 6 секунд на GPU. Т.е. разница весьма прилична.


Разумеется, еще раз стоит повторить, что “игра стоит свеч” лишь в том случае, если задача хорошо распараллеливается на небольшие блоки, в таком случае выигрыш будет заметен.