在OpenCL2.0中,增加了SVM(shared virtual memory)的特性。在开始讲解SVM之前,我们先用图片来看下OpenCL1.2中主机与设备端的地址空间:
图1 OpenCL1.2中主机与设备端地址空间
从图1可以看到,主机与设备具有不同的地址空间,各自需要对各自的内存进行管理。彼此之间不能直接访问对方的地址空间。所以,两者之间数据需要通信的话,只能把数据在主机与设备间来回拷贝,或者把设备端地址空间map /unmap到主机端。对于这样一种模式下,如果我们要想在设备端处理主机端的链表、树之类的数据。我们只能鞭长莫及!对于异构平台,我们就真的没办法愉快地处理链表之类的数据么?技术是发展的,有需求就必有技术来解决!
从CUDA6以后,GPU与CPU之间支持统一寻址(Unified Memory) ,GPU与CPU间可以直接访问彼此的地址空间,不需要我们人为的数据拷贝。这给异构计算又带入了一个新的高度,我们可以处理链表数据啦!既然CUDA都开始支持了,OpenCL也不能落后呀。在OpenCL2.0中,增加了共享虚拟内存(shared virtual memory),我们还是以一张图片来形象的描述:
图2 OpenCL2.0中主机与设备端地址空间
从图2可以看到,图1中原来两个彼此不相交的地址空间现在有个公共交集,这个公共交集就是SVM.。对于SVM的地址空间,主机和设备都可以直接访问,妈妈再也不用担心异构平台的数据访问方式了!
说完了SVM的意义,我们来聊聊SVM的具体细节。
对于SVM的创建,OpenCL2.0中有两种方式,一种缓冲分配(buffer allocation),另一种是系统分配(System allocation):
1、所谓缓冲分配,就是我们使用OpenCL API函数clSVMAlloc来分配,然后使用clSetKernelArgSVMPointer把分配的SVM作为内核参数传入
2、所谓系统分配,就是在主机端,我们可以使用malloc,new之类的系统分配内存函数来分配空间,然后使用clSetKernelArgSVMPointer把分配的SVM作为内核参数传入。
对于SVM的类型,OpenCL2.0也是有两种类型:一种是粗粒度;另一种是细粒度:
1、粗粒度SVM:共享发生在OpenCL缓冲内存对象区域粒度。在同步点强制内存一致性,使用map/unmap命令来更新主机与设备间的数据。粗粒度的SVM与OpenCL1.2中使用缓冲对象类似,不过唯一不同的是:我们不需要来回拷贝数据,设备与主机可以直接访问对方的数据,这才是重点!
2、所谓细粒度SVM:共享发生在OpenCL缓冲对象单个的加载/存储粒度。内存一致性在同步点得到保证。
好,结合SVM分配方式和SVM类型,可以把OpenCL2.0中的SVM分为:粗粒度缓冲SVM,细粒度缓冲SVM,细粒度系统SVM。(木有粗粒度系统SVM)。对于你的OpenCL设备(请确保你的设备支持OpenCL2.0),到底支持上述三种的哪三种呢?我们可以通过如下代码查询:
cl_device_svm_capabilities svm; clGetDeviceInfo(*device,CL_DEVICE_SVM_CAPABILITIES,sizeof(svm),&svm,NULL); if(svm&CL_DEVICE_SVM_FINE_GRAIN_SYSTEM) printf("CL_DEVICE_SVM_FINE_GRAIN_SYSTEM\n"); if(svm&CL_DEVICE_SVM_FINE_GRAIN_BUFFER) printf("CL_DEVICE_SVM_FINE_GRAIN_BUFFER\n"); if(svm&CL_DEVICE_SVM_COARSE_GRAIN_BUFFER) printf("CL_DEVICE_SVM_COARSE_GRAIN_BUFFER\n");在我的AMD A10-7400 Radeon R6平台上,当设备为CL_DEVICE_TYPE_GPU时,输出为:
CL_DEVICE_SVM_FINE_GRAIN_BUFFER CL_DEVICE_SVM_COARSE_GRAIN_BUFFER对于细粒度系统SVM,AMD当前是不支持的。
粗粒度缓冲SVM和细粒度缓冲SVM大致用法,如下表格所示
Coarse-grained SVM (Map/Unmap is requred) |
fine-grained SVM buffer |
---|---|
float* p = (float*)clSVMAlloc(…); clEnqueueSVMMap(…, CL_TRUE, // block until map is done p, …); // Initialize SVM buffer p[i] = …; clEnqueueSVMUnmap(…, p, …); clEnqueueNDRange(…); clEnqueueSVMMap(…, CL_TRUE, // block until map is done p, …); // Read the data produced by the kernel … = p[i]; clEnqueueSVMUnmap(…, p, …); |
float* p = (float*)clSVMAlloc(…); // Initialize SVM buffer p[i] = …; clEnqueueNDRange(…); clFinish(…); // Read the data produced by the kernel … = p[i]; |
OpenCL2.0 SVM的讲解,就到这吧!
ps:最后唠叨一句:对于SVM,方便了我们码农编程,从硬件上来说,OpenCL设备与主机不一定是共享物理内存的。
原文地址:http://blog.csdn.net/wcj0626/article/details/46360605