使用float4时加速,opencl

时间:2013-04-28 02:28:32

标签: opencl

我有以下opencl内核函数来获取图像的列总和。

__kernel void columnSum(__global float* src,__global float* dst,int srcCols,
                            int srcRows,int srcStep,int dstStep)   
{

    const int x = get_global_id(0);
    srcStep >>= 2;
    dstStep >>= 2;

    if (x < srcCols)
    {
        int srcIdx = x ;
        int dstIdx = x ;

        float sum = 0;

        for (int y = 0; y < srcRows; ++y)
        {
            sum += src[srcIdx];
            dst[dstIdx] = sum;
            srcIdx += srcStep;
            dstIdx += dstStep;
        }
    }
}

我指定每个线程在这里处理一个列,以便很多线程可以并行获取每列的column_sum。

我还使用float4重写上面的内核,这样每个线程一次可以从源图像中读取一行中的4个元素,如下所示。

__kernel void columnSum(__global float* src,__global float* dst,int srcCols,
                            int srcRows,int srcStep,int dstStep)
{

    const int x = get_global_id(0);

    srcStep >>= 2;
    dstStep >>= 2;
    if (x < srcCols/4)
    {
        int srcIdx = x ;
        int dstIdx = x ;

        float4 sum = (float4)(0.0f, 0.0f, 0.0f, 0.0f);

        for (int y = 0; y < srcRows; ++y)
        {
            float4 temp2;
            temp2 = vload4(0, &src[4 * srcIdx]);
            sum = sum + temp2;

            vstore4(sum, 0, &dst[4 * dstIdx]);

            srcIdx += (srcStep/4);
            dstIdx += (dstStep/4);
        }
    }
}

在这种情况下,从理论上讲,我认为第二个内核处理图像所用的时间应该是第一个内核函数消耗时间的1/4。但是,无论图像有多大,两个内核几乎消耗相同的时间。我不知道为什么。你们能给我一些想法吗? Ť

5 个答案:

答案 0 :(得分:6)

float4这样的OpenCL矢量数据类型更适合较旧的GPU架构,尤其是AMD的GPU。现代GPU不具备可用于单个工作项的SIMD寄存器,在这方面它们是标量的。对于NVIDIA Kepler GPU和Intel HD集成显卡上的OpenCL驱动程序,CL_DEVICE_PREFERRED_VECTOR_WIDTH_*等于1。因此,在现代GPU上添加float4向量应该需要4个操作。另一方面,Intel Core CPU上的OpenCL驱动程序CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT等于4,因此可以在一个步骤中添加这些向量。

答案 1 :(得分:4)

您正在直接读取“src”数组(全局内存)中的值。这通常比私人内存慢400倍。你的瓶颈是内存访问的定义,而不是“添加”操作本身。

当你从float移动到float4时,由于GPU能够使用向量进行操作,因此向量操作(add / multiply / ...)更有效。但是,对全局内存的读/写保持不变。 由于这是主要的瓶颈,你根本不会看到任何加速。

如果要加速算法,则应移至本地内存。但是,您必须手动解决内存管理和正确的块大小。

答案 2 :(得分:2)

你使用哪种架构?

使用float4具有更高的指令级并行性(然后需要4倍的线程)因此理论上应该更快(参见http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf

但是我在你的内核中正确地理解你正在做前缀sum(你在y的每次迭代中存储部分和)?如果是这样,由于存储瓶颈在于内存写入。

答案 3 :(得分:0)

我认为在GPU上,float4不是OpenCL中的SIMD操作。换句话说,如果添加两个float4值,则总和分四步完成,而不是一次完成。 Floatn真的是为CPU设计的。在GPU上,floatn仅作为方便的语法,至少在Nvidia卡上。 GPU上的每个线程就好像它是没有SIMD的标量处理器。但是warp中的线程并不像它们在CPU上那样独立。考虑GPGPU模型的正确方法是单指令多线程(SIMT)。 http://www.yosefk.com/blog/simd-simt-smt-parallelism-in-nvidia-gpus.html

您是否尝试在CPU上运行代码?我认为使用float4的代码应该比CPU上的标量代码运行得更快(可能快四倍)。此外,如果您有一个带AVX的CPU,那么您应该尝试使用float8。如果在具有AVX的CPU上,浮动4代码在CPU上比浮动代码更快,则应该更快。

答案 4 :(得分:0)

尝试将__ attribute __定义为内核并查看运行时间的变化 例如,尝试定义:

__ kernel void __ attribute __((vec_type_hint(int)))

__ kernel void __ attribute __((vec_type_hint(int4)))

或你想要的一些floatN

了解更多: https://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/functionQualifiers.html