在 GPU 上计算积分图像真的比在 CPU 上更快吗?
Is computing integral image on GPU really faster than on CPU?
我是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]));
}
你说的integral image
是什么意思?
我的假设是将相同分辨率K
图像MxN
相加。 在这种情况下,它在展位 CPU 和 GPU 上O(K.M.N)
,但在GPU上恒定时间可以更好,因为 gfx 内存访问比在 CPU端快得多。通常还有比CPU内核更多的GPU内核支持这一点。
如果K
太大而无法一次U
放入GPU纹理单元,则需要使用多个通道,因此O(K.M.N.log(K)/log(U)) K>U
......在某些情况下,CPU可能会更快。但正如之前的评论所建议的那样,没有测试,你只能猜测。您还需要考虑到存在无绑定纹理和纹理数组之类的东西,它们允许在单次传递中执行此操作(但我不确定这是否有任何性能成本)。
[编辑1] 清除您真正想要做的事情后
首先假设为简单起见,我们得到了NxN
像素的方形输入图像。因此,我们可以将任务分别分为H线和V线(类似于2D FFT)以简化此过程。最重要的是,我们可以将每行细分为M
像素组。所以:
N = M.K
其中N
是分辨率,M
是区域分辨率,K
是区域数。
第一通行证
每个组的渲染线,所以我们得到了
K
行大小M
。使用片段着色器,仅输出到某些纹理,计算每个区域的积分图像。这T(0.5*K*M^2*N)
整个事情可以通过覆盖屏幕的单个 QUAD 以片段渲染的方式完成......第二、通行证
将区域积分转换为完整图像积分。因此,再次渲染
K
行,并在片段中添加每个前一组的所有最后像素。这T(0.5*K^3*N)
整个事情也可以通过覆盖屏幕的单个 QUAD 渲染片段来完成......在另一个轴方向上对结果执行 #1,#2
这整个事情转化为
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)
哪个比天真的O(N^4)
更快 但你是对的,CPU为此要执行的操作较少,O(N^2)
并且不需要复制数据或多次传递,因此您应该为您的任务找出特定硬件的阈值分辨率,并根据测量值进行选择。PS 希望我没有在计算中的某个地方犯一个愚蠢的错误。此外,如果您在 CPU 上分别执行 H 和 V 线,则CPU端的复杂性将O(N^3)
并且使用这种方法甚至O(N^2.5)
不需要每个轴 2 次传递。
看看这个类似的 QA:
- 如何在GLSL中实现2D光线投射光效果
我认为这是一个很好的起点。
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 将"打开的CV图像"中的"颜色"转换为整数格式
- 平均图像时图像损坏
- 在C++中使用GDAL可以将图像的像素坐标转换为lat,long吗
- 如何将图像传输到c++(dll)中的缓冲区,然后在c#的缓冲区中读/写
- Vulkan验证层不断在VkQueuePresentKHR()上抛出图像布局错误
- 使用FFMPEG将RGB图像序列保存到.mp4时出现问题
- 将RGB图像保存为PPM格式
- 将图像添加到资源文件夹UWP C++
- 彩色图像的卤化物处理平均值
- C++射线示踪剂ppm表示没有足够的数据来显示图像
- 重新定位图像时如何前进到下一个内存块
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 编写一个函数以使用 n 百分比的 CPU 使用率
- 在 GPU 上计算积分图像真的比在 CPU 上更快吗?
- 在QML中旋转图像时降低CPU使用情况
- 使用 CUDA 计算积分图像比 CPU 代码慢