Opencl 较新版本和“0”版本上的内核速度较慢;“更好”;英伟达GPU
我在OpenCL中创建了一个实时光线跟踪器。这是在GTX 580上开发的。我停止了几年的工作,最近又重新开始了。我预计,随着更新和“更好”的Nvidia GPU,它将运行得更快。然而,它仍然在GTX 580上运行最快 下面是一个时间表,我在三台不同的计算机和图形卡上使用了一个基准场景Opencl 较新版本和“0”版本上的内核速度较慢;“更好”;英伟达GPU,opencl,gpgpu,nvidia,raytracing,Opencl,Gpgpu,Nvidia,Raytracing,我在OpenCL中创建了一个实时光线跟踪器。这是在GTX 580上开发的。我停止了几年的工作,最近又重新开始了。我预计,随着更新和“更好”的Nvidia GPU,它将运行得更快。然而,它仍然在GTX 580上运行最快 下面是一个时间表,我在三台不同的计算机和图形卡上使用了一个基准场景 GPU Kernel time CPU OS System Mem GTX 580 11 ms E
GPU Kernel time CPU OS System Mem
GTX 580 11 ms E5-1670 Windows 7 32 GB
GTX Titan 15 ms W5580 (two processors) Windows 7 48 GB
GTX 980M 15 ms i7-4710HQ (laptop) Windows 10 16 GB
每台计算机都在2016年1月10日安装了Nvidia驱动程序361.43,主机代码采用Visual Studio 2013 64位发布模式编译
我还发现GTX 580的帧速率更快
我曾经
time_end = clevent.getProfilingInfo<CL_PROFILING_COMMAND_END>();
time_start = clevent.getProfilingInfo<CL_PROFILING_COMMAND_START>();
您是否启用了无序处理,我在进行基本图像处理时,在Nvidia GPU中遇到了类似的问题 <> >当按顺序创建命令队列时,代码运行速度较慢,但是如果在OpenCL中创建的命令队列在英伟达GPU中可能出现故障,则执行速度将指数快速。 请参阅API CLU命令队列clCreateCommandQueue(CLU上下文上下文, cl_设备\u id设备, cl_命令_队列_属性, cl_int*错误代码(返回) $cl_命令\u队列\u属性应设置为$cl_队列\u输出\u顺序\u执行\u模式\u启用 但请确保内核中没有数据依赖项,因为在这种情况下,您不能使用此选项 此外,请确保查询计算单元的数量,并相应地给出全局工作大小和本地工作大小
例如,我的Nvidia GPU有4个计算单元,因此为了获得最佳性能,我的全局工作大小应该可以被4整除,而本地工作大小应该是4的整数倍。您是否启用了无序处理,我在进行基本图像处理时在Nvidia GPU中遇到了类似的问题 <> >当按顺序创建命令队列时,代码运行速度较慢,但是如果在OpenCL中创建的命令队列在英伟达GPU中可能出现故障,则执行速度将指数快速。 请参阅API CLU命令队列clCreateCommandQueue(CLU上下文上下文, cl_设备\u id设备, cl_命令_队列_属性, cl_int*错误代码(返回) $cl_命令\u队列\u属性应设置为$cl_队列\u输出\u顺序\u执行\u模式\u启用 但请确保内核中没有数据依赖项,因为在这种情况下,您不能使用此选项 此外,请确保查询计算单元的数量,并相应地给出全局工作大小和本地工作大小 例如,我的Nvidia GPU有4个计算单元,因此为了获得最佳性能,我的全局工作大小应该可以被4整除,而本地工作大小应该是4的整数倍(从我的头顶算起) CUDA/PTX可以生成32位或64位 OpenCL编译器默认生成:
- 计算能力2.0->32位ptx
- 计算能力3.0及更高版本->64位ptx
- GTX 580->计算能力2.0
- GTX泰坦->计算能力3.5
- GTX 980M->计算能力5.2
- 计算能力2.0->32位ptx
- 计算能力3.0及更高版本->64位ptx
- GTX 580->计算能力2.0
- GTX泰坦->计算能力3.5
- GTX 980M->计算能力5.2
由于切换到64位ptx,您可能会遇到更高的寄存器使用率-请查看CUDA占用率计算器,以检查是否预期会减速。如果确认了这一点,那么您需要对内核进行微调。我无法提供具体的答案-GTX 580和GTX 980之间的流式多处理器设计已经发生了重大变化。至少,您可能需要找到新的最佳本地和全局工作组规模
我建议使用NVIDIA的评测工具,因为它们仍然适用于OpenCL。请看@jrprice提供的详细说明。记录分析数据后,您可以将其导入Visual Profiler,并检查内核的寄存器和本地内存使用情况和占用情况。我无法提供具体答案-GTX 580和GTX 980之间的流式多处理器设计已发生重大变化。至少,您可能需要找到新的最佳本地和全局工作组规模
我建议使用NVIDIA的评测工具,因为它们仍然适用于OpenCL。请看@jrprice提供的详细说明。记录分析数据后,您可以将其导入Visual Profiler,并检查内核的寄存器和本地内存使用情况和占用情况。您希望我们盲目猜测吗?硬件功能越来越强大,但我们不知道你的跟踪器是什么样子,也不知道它是如何工作的。你的基准时间看起来也很小,似乎在毫秒范围内,它会受到太多的随机波动,无法对实际表现做出准确判断。我意识到我可能没有提供足够的信息来回答这个问题。但这可能是OpenCL和Nvidia的一个已知问题?我可能会很快发布我的代码,但它是q
void Contexts::init(string sourceCode) {
run_time = -1;
context = createCLContext(type, vendor);
cl_uint uiNumSupportedFormats = 0;
devices = context.getInfo<CL_CONTEXT_DEVICES>();
int err = 0;
try{
//queues.push_back(cl::CommandQueue(context, devices[i], 0, &err));
//queue = cl::CommandQueue(context, devices[device], CL_QUEUE_PROFILING_ENABLE, &err);
queue = cl::CommandQueue(context, devices[device], CL_QUEUE_PROFILING_ENABLE|CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &err);
//printf("\t\tDevice: %s\n", devices[device].getInfo<CL_DEVICE_NAME>().c_str());
}
catch (cl::Error er) {
printf("ERROR: %s(%d)\n", er.what(), er.err());
}
//ndevices = devices.size();
//if(ndevices>max_devices) ndevices = max_devices;
program = buildProgramFromSource(context, sourceCode);
try{
kernel1 = cl::Kernel(program, "trace", &err);
kernel2 = cl::Kernel(program, "transform_primitives", &err);
kernel_postprocess = cl::Kernel(program, "post_process", &err);
}
catch (cl::Error er) {
printf("ERROR: %s(%d)\n", er.what(), er.err());
}
}
cl::Buffer Contexts::copy_buffer(int size, const void* ptr, int flags = CL_MEM_READ_ONLY) {
cl::Buffer out;
if(size>0) {
out = cl::Buffer(context, flags| CL_MEM_COPY_HOST_PTR, size, (void*)ptr);
}
else {
//NULL pointers to kernel do not seem to work on INTEL so use this hack
out = cl::Buffer(context, flags, 1, NULL);
}
return out;
}
void Contexts::copy_buffers() {
//int cubemap_size = para->cubemap->sizeX * para->cubemap->sizeY * 6 * para->cubemap->ncubemap;
//if(para->cubemap->sizeX== -1) cubemap_size = 0;
int nobj = para->kernel1_parameters.nobj;
int nprim = para->kernel1_parameters.nprim;
int nmat= para->kernel1_parameters.nmat;
int nlight = para->kernel1_parameters.nlight;
int nnode = para->kernel1_parameters.nnode;
int nmap = para->nmaps;
int err = 0;
int npixels = para->kernel1_parameters.height*para->kernel1_parameters.width;
int exposure_samples = para->kernel1_parameters.exposure_samples;
int mask_size = para->kernel1_parameters.mask_size;
int nmask = (2*mask_size+1)*(2*mask_size+1);
cl_objects_mem = copy_buffer(sizeof(CSG_object)*nobj, para->objects);
cl_node_mem = copy_buffer(sizeof(Node)*nnode, para->nodes);
cl_prim_mem = copy_buffer(sizeof(Primitive)*nprim, para->prims, CL_MEM_READ_WRITE);
cl_light_mem = copy_buffer(sizeof(Light)*nlight, para->lights);
cl_mat_mem = copy_buffer(sizeof(Material)*nmat, para->mats);
cubemap_info = copy_buffer(sizeof(Cubemap_info)*nmap, para->maps);
cubemap_images = copy_buffer(sizeof(cl_uchar4)*para->envmap_npixels, para->envmap_images);
cl_mask_mem = copy_buffer(sizeof(cl_float)*nmask, para->mask);
cl_image_mem = cl::Buffer(context, CL_MEM_WRITE_ONLY, sizeof(cl_uchar4)*npixels, NULL, &err);
cl_results_mem = cl::Buffer(context, CL_MEM_READ_WRITE, sizeof(cl_float4)*npixels, NULL, &err);
cl_luminance = cl::Buffer(context, CL_MEM_WRITE_ONLY, sizeof(cl_float)*exposure_samples, NULL, &err);
if(para->surfacecpy_sw) {
cmPinnedBufOut1 = cl::Buffer(context, CL_MEM_WRITE_ONLY |CL_MEM_ALLOC_HOST_PTR, sizeof(cl_uchar4)*npixels, NULL, NULL);
image = (int*)queue.enqueueMapBuffer(cmPinnedBufOut1, CL_TRUE, CL_MAP_READ, 0, sizeof(cl_uchar4)*npixels, 0, NULL, NULL);
//queue.enqueueUnmapMemObject(cmPinnedBufOut1, image);
//int pageSize = 4096;
//image = (int*) _aligned_malloc(sizeof(cl_uchar4)*npixels, pageSize);
//CL_MEM_USE_PERSISTENT_MEM_AMD
}
cmPinnedBufOut2 = cl::Buffer(context, CL_MEM_WRITE_ONLY |CL_MEM_ALLOC_HOST_PTR, sizeof(cl_float)*exposure_samples, NULL, NULL);
luminance = (float*)queue.enqueueMapBuffer(cmPinnedBufOut2, CL_TRUE, CL_MAP_READ, 0, sizeof(cl_float)*exposure_samples, 0, NULL, NULL);
queue.finish();
//int kindex = 0;
kernel1.setArg(0, cl_objects_mem);
kernel1.setArg(1, cl_node_mem);
kernel1.setArg(2, cl_prim_mem);
kernel1.setArg(3, cl_mat_mem);
kernel1.setArg(4, cl_light_mem);
kernel1.setArg(5, cubemap_info);
kernel1.setArg(6, cubemap_images);
kernel1.setArg(7, cl_results_mem);
kernel_postprocess.setArg(0, cl_results_mem);
kernel_postprocess.setArg(1, cl_luminance);
kernel_postprocess.setArg(2, cl_image_mem);
kernel_postprocess.setArg(3, cl_mask_mem);
kernel2.setArg(0, cl_prim_mem);
}
void Contexts::run() {
int nprim = para->kernel2_parameters.nprim;
cl_float speed = para->kernel2_parameters.speed;
cl_float4 speed_obj = para->kernel2_parameters.speed_obj;
cl_float16 cl_viewTransform;
for(int i=0; i<16; i++)
cl_viewTransform.s[i] = para->viewTransform[i];
//para->kernel1_parameters.offset = offset;
//para->kernel1_parameters.offset2 = offset2;
kernel1.setArg(8, cl_viewTransform);
kernel1.setArg(9, para->kernel1_parameters);
kernel1.setArg(10, offset);
kernel_postprocess.setArg(4, para->kernel1_parameters);
kernel_postprocess.setArg(5, offset);
kernel_postprocess.setArg(6, offset2);
//kernel1.setArg(11, offset2);
cl::NDRange local_size = cl::NDRange(local_work_size);
if(local_work_size == 0) {
local_size = cl::NullRange;
}
queue.enqueueNDRangeKernel(kernel1, cl::NullRange, cl::NDRange(size), local_size, NULL, &clevent);
queue.finish();
cl_ulong time_start, time_end;
time_end = clevent.getProfilingInfo<CL_PROFILING_COMMAND_END>();
time_start = clevent.getProfilingInfo<CL_PROFILING_COMMAND_START>();
run_time = (float)(time_end - time_start);
//post_process
queue.enqueueNDRangeKernel(kernel_postprocess, cl::NullRange, cl::NDRange(size), local_size, NULL, &clevent);
queue.finish();
time_end = clevent.getProfilingInfo<CL_PROFILING_COMMAND_END>();
time_start = clevent.getProfilingInfo<CL_PROFILING_COMMAND_START>();
run_time += (float)(time_end - time_start);
//printf("run time %f, run time2 %f\n", run_time, run_time2);
//kernel2
kernel2.setArg(1, speed);
kernel2.setArg(2, speed_obj);
queue.enqueueNDRangeKernel(kernel2, cl::NullRange, cl::NDRange(nprim), cl::NullRange, NULL, &clevent);
queue.finish();
time_end = clevent.getProfilingInfo<CL_PROFILING_COMMAND_END>();
time_start = clevent.getProfilingInfo<CL_PROFILING_COMMAND_START>();
run_time += (float)(time_end - time_start);
if(para->getoutput_sw) {
if(!para->surfacecpy_sw) {
if(SDL_MUSTLOCK(para->surface)) {
if(SDL_LockSurface(para->surface) < 0) return;
}
queue.enqueueReadBuffer(cl_image_mem, CL_TRUE, 0, sizeof(cl_uchar4)*size, (int*)para->surface->pixels + offset, NULL, &clevent);
queue.finish();
if(SDL_MUSTLOCK(para->surface))
SDL_UnlockSurface(para->surface);
}
else {
queue.enqueueReadBuffer(cl_image_mem, CL_TRUE, 0, sizeof(cl_uchar4)*size, (int*)image, NULL, &clevent);
queue.finish();
}
queue.enqueueReadBuffer(cl_luminance, CL_TRUE, 0, sizeof(cl_float)*size2, luminance, NULL, &clevent);
queue.finish();
}
}