11.7 OpenCL 2.0提出共享虚拟内存的原因

OpenCL 2.0中SVM共享虚拟内存特性的添加尤为重要。这让主机端和设备端可以访问同一上下文中的地址空间。其也支持在主机端和内核间传递自定义结构体的指针。这个特性可以理解成将设备端的全局内存扩张到了主机端内存空间中,因此OpenCL中的工作项就能够访问主机端地址空间的数据。

根据OpenCL 2.0标准,SVM分为三种:

  1. 粗粒度SVM:数据以OpenCL内存对象的形式共享。其具有一些同步点——内核执行、映射和逆映射。
  2. 细粒度SVM:数据以OpenCL内存对象的形式共享。不需要进行显式同步,不过在主机端和设备端对相应内存进行原子操作时,内存数据需要进行同步。
  3. 系统细粒度SVM:设备端和主机端使用的内存可以认为是完全一样的,内存对象使用的方式和C/C++一样。

表11.3展示了2.0之前、粗粒度SVM和细粒度SVM间的不同。

表11.3 OpenCL共享数据的行为对比

操作 2.0之前 粗粒度SVM 细粒度SVM
数据拷贝到设备端 clEnqueueWriteBuffer 不需要 不需要
设备端执行原子操作对主机端可见 不适用 不可见 可见
设备端对数据进行修改对主机端可见 需要进行拷贝之后可见 内核执行完成时 内核执行完成后,或在设备端执行原子操作之后
数据从设备端拷贝到主机端 clEnqueueReadBuffer 不需要 不需要

细粒度SVM具有一个特殊能力,就是支持主机端和设备同时对同一块内存地址进行原子操作。直方图统计的例子中,使用细粒度SVM就可以和主机或其他设备合作完成该工作;他们都共享同一块内存,并且都能看到相应的数据,在原子操作完成后,每个设备上都会看到最新的数据。

OpenCL 2.0中,粗粒度SVM是强制要求支持,其他两种类型可以选择支持。本节中,我们就来聊一下如何在C++ AMP中使用粗粒度SVM。注意,粗粒度SVM有点类似于OpenCL 1.x中的内存对象,不过其不需要显式的通过clEnqueueWriteBuffer()进行写入。因为相似,CLamp将组粒度SVM认为是一种性能有所提升的concurrency::array_view实现。

为了利用粗粒度SVM,主机端需要调用clSVMAlloc()分配出SVM内存,这段内存可以在主机端和设备端共享。cl_mem对象是通过clCreateBuffer()通过传入CL_MEM_USE_HOST_PTR参数和主机端内存指针进行创建。这里clSVMAlloc()分配出的指针就类似于传入的主机端指针。

同样,内存中的内容设备端和主机端也是能够自动共享的。这里就不需要再去调用clEnqueueWriteBuffer()clEnqueueReadBuffer(),来完成设备端和主机端数据进行共享了。

当不在需要SVM内存,可以通过clReleaseMemObject()API对粗粒度SVM对象进行释放。之后,还需要通过clSVMFree()API销毁SVM内存所开辟的空间。