我是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]));
}
答案 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
是区域数。
<强>第一。通过强>
为每个组渲染一行,以便我们获得K
行大小K
。使用片段着色器计算每个区域的整体图像,仅输出到某些纹理。这是M
整个事情可以在覆盖屏幕的单个QUAD呈现的片段中完成......
<强>第二。通过强>
将区域积分转换为完整图像积分。因此,再次渲染T(0.5*K*M^2*N)
行并在片段中添加每个前一组的所有最后像素。这是K
整个事情也可以在覆盖屏幕的单个QUAD呈现的片段中完成......
对另一个轴方向的结果执行#1,#2
这整件事转换为
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次通过。
看看这个类似的质量保证:
我认为这是一个很好的起点。