OpenCL - 为什么使用READ_ONLY或WRITE_ONLY缓冲区

时间:2013-07-27 19:03:48

标签: kernel buffer opencl flags pyopencl

OpenCL中,将缓冲区标记为READ_ONLYWRITE_ONLY是否有任何性能优势?

kernel是我经常看到的(a是READ_ONLY,b是WRITE_ONLY):

__kernel void two_buffer_double(__global float* a, __global float* b)
{
    int i = get_global_id(0);
    b[i] = a[i] * 2;
}

这个kernel似乎更好,因为它使用较少的全局内存(a READ_WRITE):

__kernel void one_buffer_double(__global float* a)
{
    int i = get_global_id(0);
    a[i] = a[i] * 2;
}

是否存在READ_ONLYWRITE_ONLY标志以帮助调试和捕获错误?

3 个答案:

答案 0 :(得分:5)

请注意,实际上有两种。在分配缓冲区时,您有CL_MEM_READ_ONLYCL_MEM_WRITE_ONLYCL_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说明符可以很容易地避免写入只读缓冲区。在纯&#34; C&#34;。

中不允许从只写缓冲区读取

将这些缓冲区映射到主机内存时会发生另一种可能的优化。映射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 /秒的额外速率。 Haven在sm_30上看到明显的加速(不确定它是否意味着在那里得到支持 - 尽管指令没有从ptx中删除)。请注意,您需要自己定义NVIDIA(或参见Detect OpenCL device vendor in kernel code)。

答案 1 :(得分:4)

取决于,

READ_ONLY __global内存位置存储在“全局/常量内存数据缓存”中,它比GPU上的普通缓存或RAM快 (参见here) ,在CPU上并不重要。

我不知道WRITE_ONLY的任何优点,也许它也有帮助,因为GPU知道它可以流出数据而不需要缓存。

如果您不确定,请去测量它......

答案 2 :(得分:4)

直接回答你的问题,我会说:不,这些标志不仅仅存在以帮助调试和捕获错误。但是,很难对任何实现如何使用这些标志以及它们如何影响性能提供任何参考。

我的理解(遗憾的是没有任何文档支持)是在使用这些标志时你对缓冲区的使用方式施加了更多限制,因此你可以帮助运行时/驱动程序/编译器做一些可能会改善表现的假设。例如我想当内核使用它时,不应该担心内存与只读缓冲区的一致性,因为工作项不应该写入其中。因此可以跳过一些检查......虽然在Opencl中你应该自己使用障碍等来处理这个问题。

另请注意,自Ope​​ncl 1.2以来,已经引入了一些与主机需要访问缓冲区相关的其他标志。有:

CL_MEM_HOST_NO_ACCESS,
CL_MEM_HOST_{READ, WRITE}_ONLY,
CL_MEM_{USE, ALLOC, COPY}_HOST_PTR

我猜测这又一次帮助人们实现opencl以提高性能,但我想我们需要一些AMD或NVIDIA专家的输入。

请注意,到目前为止,我所说的只是只是我的想法,而不是基于任何严肃的文档(我没有找到任何文件)。

另一方面,我可以肯定地告诉你,标准不会像强制一个只读缓冲区位于常量空间中。可能是某些实现为小缓冲区执行此操作。我们不要忘记,常量空间内存很小,因此您可以使只读缓冲区太大而无法容纳。确保缓冲区位于常量空间内存中的唯一方法是使用内核代码中的常量关键字解释here。当然在主机端,如果你想使用常量缓冲区,你必须使用只读标志。