请注意,实际上有两种。您有CL_MEM_READ_ONLY
,CL_MEM_WRITE_ONLY
并且CL_MEM_READ_WRITE
在分配缓冲区时,但您也有__read_only
,__write_only
并__read_write
在内核代码中装饰您的指针。
这些可用于优化和错误检查。让我们先看看性能。如果遇到只写缓冲区,则无需缓存写入(如通过缓存写入),为读取节省更多缓存。这在很大程度上取决于 GPU 硬件,至少 NVIDIA 硬件确实具有实际实现它所需的指令(.cs
和.lu
修饰符)。你可以参考他们的PTX ISA。我还没有看到编译器实际执行此优化的任何证据,例如:
__kernel void Memset4(__global __write_only unsigned int *p_dest,
const unsigned int n_dword_num)
{
unsigned int i = get_global_id(0);
if(i < n_dword_num)
p_dest[i] = 0; // this
}
编译为:
st.global.u32 [%r10], %r11; // no cache operation specified
这是有道理的,因为 CUDA 没有这些限定符的等价物,因此编译器很可能会默默地忽略它们。但是把它们放在那里并没有什么坏处,我们将来可能会变得更幸运。在 CUDA 中,使用该__ldg
函数和使用编译器标志来选择加入/退出缓存 L1 ( -Xptxas -dlcm=cg
) 中的全局内存传输,从而公开了其中的一些功能。asm
如果您发现绕过缓存会产生主要优势,您也可以随时使用。
const
至于错误检查,使用内核声明中的说明符很容易避免写入只读缓冲区。在纯“C”中不可能禁止从只写缓冲区读取。
将这些缓冲区映射到主机内存时,会发生另一种可能的优化。映射CL_MEM_READ_ONLY
缓冲区时,映射区域可能未初始化,因为主机只会写入该内存,而设备只能读取它。类似地,当取消映射CL_MEM_WRITE_ONLY
缓冲区时,驱动程序不需要将(可能被主机修改的)内容从主机内存复制到设备内存。我没有测量这个。
作为旁注,我尝试使用:
inline unsigned int n_StreamingLoad(__global __read_only const unsigned int *p_src)
{
#ifdef NVIDIA
unsigned int n_result;
asm("ld.global.cs.u32 %r0, [%r1];" : "=r" (n_result) : "r" (p_src));
return n_result;
#else // NVIDIA
return *p_src; // generic
#endif // NVIDIA
}
inline void StreamingWrite(__global __write_only unsigned int *p_dest, const unsigned int n_value)
{
#ifdef NVIDIA
asm("st.global.cs.u32 [%r0], %r1;" : : "r" (p_dest), "r" (n_value) : "memory");
#else // NVIDIA
*p_dest = n_value; // generic
#endif // NVIDIA
}
即使在带有sm_35
设备的简单 memcpy 内核上(在 GTX 780 和 K40 上测试),它也可以为您提供大约 15 GB/秒的额外速度。还没有看到明显的加速sm_30
(不确定它是否甚至意味着在那里得到支持——尽管指令没有从 ptx 中删除)。请注意,您需要NVIDIA
自己定义(或参见在内核代码中检测 OpenCL 设备供应商)。