C与OpenCL,如何比较时间测量的结果?
所以,在另一篇文章中,我询问了关于C时间测量的问题。现在,我想知道如何比较C“函数”和OpenCL“函数”的结果 这是主机OpenCL和C的代码C与OpenCL,如何比较时间测量的结果?,c,performance,algorithm,opencl,measurement,C,Performance,Algorithm,Opencl,Measurement,所以,在另一篇文章中,我询问了关于C时间测量的问题。现在,我想知道如何比较C“函数”和OpenCL“函数”的结果 这是主机OpenCL和C的代码 #define PROGRAM_FILE "sum.cl" #define KERNEL_FUNC "float_sum" #define ARRAY_SIZE 1000000 #include <stdio.h> #include <stdlib.h> #include <time.h> #include &
#define PROGRAM_FILE "sum.cl"
#define KERNEL_FUNC "float_sum"
#define ARRAY_SIZE 1000000
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <CL/cl.h>
int main()
{
/* OpenCL Data structures */
cl_platform_id platform;
cl_device_id device;
cl_context context;
cl_program program;
cl_kernel kernel;
cl_command_queue queue;
cl_mem vec_buffer, result_buffer;
cl_event prof_event;;
/* ********************* */
/* C Data Structures / Data types */
FILE *program_handle; //Kernel file handle
char *program_buffer; //Kernel buffer
float *vec, *non_parallel;
float result[ARRAY_SIZE];
size_t program_size; //Kernel file size
cl_ulong time_start, time_end, total_time;
int i;
/* ****************************** */
/* Errors */
cl_int err;
/* ****** */
non_parallel = (float*)malloc(ARRAY_SIZE * sizeof(float));
vec = (float*)malloc(ARRAY_SIZE * sizeof(float));
//Initialize the vector of floats
for(i = 0; i < ARRAY_SIZE; i++)
vec[i] = i + 1;
/************************* C Function **************************************/
clock_t start, end;
start = clock();
for( i = 0; i < ARRAY_SIZE; i++)
{
non_parallel[i] = vec[i] * vec[i];
}
end = clock();
printf( "Number of seconds: %f\n", (clock()-start)/(double)CLOCKS_PER_SEC );
free(non_parallel);
/***************************************************************************/
clGetPlatformIDs(1, &platform, NULL);//Just want NVIDIA platform
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);
// Context error?
if(err)
{
perror("Cannot create context");
return 1;
}
//Read the kernel file
program_handle = fopen(PROGRAM_FILE,"r");
fseek(program_handle, 0, SEEK_END);
program_size = ftell(program_handle);
rewind(program_handle);
program_buffer = (char*)malloc(program_size + 1);
program_buffer[program_size] = '\0';
fread(program_buffer, sizeof(char), program_size, program_handle);
fclose(program_handle);
//Create the program
program = clCreateProgramWithSource(context, 1, (const char**)&program_buffer,
&program_size, &err);
if(err)
{
perror("Cannot create program");
return 1;
}
free(program_buffer);
clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
kernel = clCreateKernel(program, KERNEL_FUNC, &err);
if(err)
{
perror("Cannot create kernel");
return 1;
}
queue = clCreateCommandQueue(context, device, CL_QUEU_PROFILING_ENABLE, &err);
if(err)
{
perror("Cannot create command queue");
return 1;
}
vec_buffer = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(float) * ARRAY_SIZE, vec, &err);
result_buffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float)*ARRAY_SIZE, NULL, &err);
if(err)
{
perror("Cannot create the vector buffer");
return 1;
}
clSetKernelArg(kernel, 0, sizeof(cl_mem), &vec_buffer);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &result_buffer);
size_t global_size = ARRAY_SIZE;
size_t local_size = 0;
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, NULL, 0, NULL, &prof_event);
clEnqueueReadBuffer(queue, result_buffer, CL_TRUE, 0, sizeof(float)*ARRAY_SIZE, &result, 0, NULL, NULL);
clFinish(queue);
clGetEventProfilingInfo(prof_event, CL_PROFILING_COMMAND_START,
sizeof(time_start), &time_start, NULL);
clGetEventProfilingInfo(prof_event, CL_PROFILING_COMMAND_END,
sizeof(time_end), &time_end, NULL);
total_time += time_end - time_start;
printf("\nAverage time in nanoseconds = %lu\n", total_time/ARRAY_SIZE);
clReleaseMemObject(vec_buffer);
clReleaseMemObject(result_buffer);
clReleaseKernel(kernel);
clReleaseCommandQueue(queue);
clReleaseProgram(program);
clReleaseContext(context);
free(vec);
return 0;
}
现在,结果是:
秒数:0.010000在GPU上执行并行代码并不一定比在CPU上执行快。考虑到除了计算之外,您还必须将数据传输到GPU内存或从GPU内存传输数据
在您的示例中,您正在传输2*N项并并行执行O(N)操作,这是对GPU的一种非常低效的使用。因此,对于这个特定的计算来说,CPU很可能确实更快。只为其他向她寻求帮助的人:使用OpenCL评测内核运行时的简短介绍 启用分析模式:
cmdQueue = clCreateCommandQueue(context, *devices, CL_QUEUE_PROFILING_ENABLE, &err);
分析内核:
cl_event prof_event;
clEnqueueNDRangeKernel(cmdQueue, kernel, 1 , 0, globalWorkSize, NULL, 0, NULL, &prof_event);
在以下位置读取分析数据:
cl_ulong ev_start_time=(cl_ulong)0;
cl_ulong ev_end_time=(cl_ulong)0;
clFinish(cmdQueue);
err = clWaitForEvents(1, &prof_event);
err |= clGetEventProfilingInfo(prof_event, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &ev_start_time, NULL);
err |= clGetEventProfilingInfo(prof_event, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &ev_end_time, NULL);
计算内核执行时间:
float run_time_gpu = (float)(ev_end_time - ev_start_time)/1000; // in usec
你的方法
total_time/ARRAY_SIZE
这不是你想要的。它将为您提供每个工作项的运行时间
以纳秒为单位的操作/时间将为您提供GFLOPS(每秒千兆浮点操作)。这是应用程序的一个大问题:
size_t global_size = ARRAY_SIZE;
size_t local_size = 0;
您正在创建单项目工作组,这将使大多数gpu处于空闲状态。在许多情况下,使用单个项目工作组只能使用gpu的1/15
相反,请尝试以下方法:
size_t global_size = ARRAY_SIZE / 250; //important: local_size*global_size must equal ARRAY_SIZE
size_t local_size = 250; //a power of 2 works well. 250 is close enough, yes divisible by your 1M array size
现在,您正在创建大型组,以便更好地饱和图形硬件的ALU。内核将以您现在拥有的方式运行良好,但也有一些方法可以从内核部分获得更多
内核优化:将ARRAY_SIZE作为一个附加参数传递到内核中,并使用更少的组来获得更优化的组大小。您还将消除本地大小*全局大小与数组大小完全相等的需要。此内核中从未使用工作项的全局id,也不需要它,因为传入了总大小
__kernel void float_sum(__global float* vec,__global float* result,__global int count){
int lId = get_local_id(0);
int lSize = get_local_size(0);
int grId = get_group_id(0);
int totalOps = count/get_num_groups(0);
int startIndex = grId * totalOps;
int maxIndex = startIndex+totalOps;
if(grId == get_num_groups(0)-1){
endIndex = count;
}
for(int i=startIndex+lId;i<endIndex;i+=lSize){
result[i] = vec[i] * vec[i];
}
}
有没有办法让内核更高效?我也在考虑将数据并行化…@facundo:不太可能。恐怕您唯一的选择是在GPU上运行更重要的计算。例如,你可以尝试实现一个矩阵乘法,这没有任何意义。为此,只测量了内核执行时间。它不考虑数据传输。在这项任务中,一个普通的gpu通常会击败一个高端cpu。没有测量传输时间,但它只向上/向下4米。这个例子一开始有点做作。前N个平方和=(N*(N+1)*(2N+1))/6I对这些结果感到非常惊讶,特别是因为您将OpenCL速度除以数组的大小。你确定你对代码的计时正确吗?您使用的是Windows还是Linux?你在用什么GPU?你在用这个具体的例子做实验吗?如果使用float4类型,则可以在一次操作中对4个值进行点积和求和。我已经回答了下面的问题,假设您不是在寻找这样的优化,而是opencl指针。还有一个计算平方和的公式,你可以使用。
__kernel void float_sum(__global float* vec,__global float* result,__global int count){
int lId = get_local_id(0);
int lSize = get_local_size(0);
int grId = get_group_id(0);
int totalOps = count/get_num_groups(0);
int startIndex = grId * totalOps;
int maxIndex = startIndex+totalOps;
if(grId == get_num_groups(0)-1){
endIndex = count;
}
for(int i=startIndex+lId;i<endIndex;i+=lSize){
result[i] = vec[i] * vec[i];
}
}
size_t global_size = ARRAY_SIZE / numComputeUnits;
size_t local_size = 64; //also try other multiples of 16 or 64 for gpu; or multiples of your core-count for a cpu kernel