在GPU上计算整体图像真的比在CPU上快吗?

时间:2017-05-11 03:21:56

标签: c++ performance opengl image-processing gpgpu

我是GPU计算的新手,所以这可能是一个非常天真的问题 我做了一些查看,看起来在GPU上计算整体图像是个不错的主意 然而,当我真正深入研究它时,我想知道它可能不比CPU快,特别是对于大图像。所以我只想知道你对它的想法,以及GPU是否真的更快的一些解释。

因此,假设我们有一个MxN图像,积分图像的CPU计算需要大约3xMxN的加法,即O(MxN)。
在GPU上,按照“OpenGL超级圣经”第6版提供的代码,它需要一些KxMxNxlog2(N)+ KxMxNxlog2(M)操作,其中K是很多位移,乘法的操作次数,此外...
GPU可以并行工作,例如,每次32像素,取决于设备,但它仍然是O(MxNxlog2(M))。 我认为即使在640x480的通用分辨率下,CPU仍然更快。

我错了吗? [编辑]这是直接从书中着色器代码,想法是使用2遍:计算行的积分,然后计算通过1的结果列的积分。这个着色器代码是1遍。

#version 430 core
layout (local_size_x = 1024) in;
shared float shared_data[gl_WorkGroupSize.x * 2];
layout (binding = 0, r32f) readonly uniform image2D input_image;
layout (binding = 1, r32f) writeonly uniform image2D output_image;
void main(void)
{
    uint id = gl_LocalInvocationID.x;
    uint rd_id;
    uint wr_id;
    uint mask;
    ivec2 P = ivec2(id * 2, gl_WorkGroupID.x);
    const uint steps = uint(log2(gl_WorkGroupSize.x)) + 1;
    uint step = 0;
    shared_data[id * 2] = imageLoad(input_image, P).r;
    shared_data[id * 2 + 1] = imageLoad(input_image,
    P + ivec2(1, 0)).r;
    barrier();
    memoryBarrierShared();
    for (step = 0; step < steps; step++)
    {
        mask = (1 << step) - 1;
        rd_id = ((id >> step) << (step + 1)) + mask;
        wr_id = rd_id + 1 + (id & mask);
        shared_data[wr_id] += shared_data[rd_id];
        barrier();
        memoryBarrierShared();
    }
    imageStore(output_image, P.yx, vec4(shared_data[id * 2]));
    imageStore(output_image, P.yx + ivec2(0, 1),
    vec4(shared_data[id * 2 + 1]));
}

1 个答案:

答案 0 :(得分:2)

integral image是什么意思?

我的假设是将同一分辨率K的{​​{1}}张图像加在一起。在这种情况下,它在{strong> CPU 和 GPU 上是MxN,但是 GPU 上的常量时间可以更好,因为gfx内存访问是比 CPU 方面快得多。对于 GPU 核心,通常还有更多 GPU 核心支持 GPU

如果O(K.M.N)太大而不能同时适应 GPU 纹理单位K,那么您需要使用多次传递U ...其中在某些情况下, CPU 可能会更快。但正如先前的评论建议没有测试你只能猜测。你还需要考虑到像无绑定纹理和纹理数组这样的东西允许单次传递(但我不确定是否有任何性能成本)。

清除实际想要的内容后

[Edit1]

首先让我们假设为了简单,我们得到了方形输入图像O(K.M.N.log(K)/log(U)) K>U像素。因此,我们可以将任务分为H线和V线(类似于 2D FFT )以简化此过程。最重要的是,我们可以使用每行细分为NxN像素组。所以:

M

N = M.K 是分辨率,N是区域分辨率,M是区域数。

  1. <强>第一。通过

    为每个组渲染一行,以便我们获得K行大小K。使用片段着色器计算每个区域的整体图像,仅输出到某些纹理。这是M整个事情可以在覆盖屏幕的单个QUAD呈现的片段中完成......

  2. <强>第二。通过

    将区域积分转换为完整图像积分。因此,再次渲染T(0.5*K*M^2*N)行并在片段中添加每个前一组的所有最后像素。这是K整个事情也可以在覆盖屏幕的单个QUAD呈现的片段中完成......

  3. 对另一个轴方向的结果执行#1,#2

  4. 这整件事转换为

    T(0.5*K^3*N)

    现在,您可以在设置中调整T(2*N*(0.5*K*M^2+0.5*K^3)) T(N*(K*M^2+K^3)) O(N*(K*M^2+K^3)) 到最高性能...如果我将整个内容重写为M,那么:

    M,N

    所以你应该最小化,所以我会尝试使用

    周围的值
    T(N*((N/M)*M^2+(N/M)^3))
    T(N*(N*M+(N/M)^3))
    

    所以整件事转换为:

    N*M = (N/M)^3
    N*M = N^3/M^3
    M^4 = N^2
    M^2 = N
    M = sqrt(N) = N^0.5
    

    哪个比天真更快T(N*(N*M+(N/M)^3)) T(N*(N*N^0.5+(N/N^0.5)^3)) T(N^2.5+N^1.5) O(N^2.5) 但你是对的 CPU 为此做O(N^4)的操作较少,并且不需要复制数据或多次通过,所以你应找出针对您的任务的特定 HW 的阈值分辨率,并根据测量值进行选择。 PS希望我在计算中的某个地方没有做过愚蠢的错误。此外,如果您在 CPU 上分别执行H和V线,而不是 CPU ,那么复杂性将为O(N^2),并且在不需要的情况下使用此方法甚至O(N^3)每轴2次通过。

    看看这个类似的质量保证:

    我认为这是一个很好的起点。