簡體   English   中英

在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是什么意思?

我的假設是將具有相同分辨率MxN K張圖像相加。 在這種情況下,它是O(KMN)展台CPUGPU但所述恆定時間可以在GPU作為GFX存儲器訪問更好上比在CPU側快得多。 還有平時多GPU核心比CPU內核有利於這種情況的GPU。

如果K太大而無法一次放入GPU紋理單元U ,則您需要使用多次O(KMNlog(K)/log(U)) K>U因此O(KMNlog(K)/log(U)) K>U ...在某些情況下, CPU可能會更快。 但是,正如之前的評論所建議的那樣,未經測試,您只能猜測。 您還需要考慮到諸如無綁定紋理和紋理數組之類的東西可以單次執行(但我不確定這樣做是否會產生任何性能成本)。

[Edit1]清除您實際要執行的操作后

首先,為簡單起見,我們假設輸入圖像為NxN像素。 因此,我們可以將任務分為H線和V線(類似於2D FFT )以簡化此過程。 最重要的是,我們可以將每行細分為M像素。 所以:

N = M.K

其中N是分辨率, M是區域分辨率, K是區域數。

  1. 1。 通過

    為每個組渲染線,因此我們得到了K條尺寸為M線。 使用片段着色器僅計算輸出到某些紋理的每個區域的積分圖像。 這是T(0.5*K*M^2*N)這整個事情可以由單個QUAD覆蓋屏幕的片段完成...

  2. 第2位。 通過

    將區域積分轉換為完整圖像積分。 因此,再次渲染K線,並在片段中添加每個先前組的所有最后像素。 這是T(0.5*K^3*N)這整個事情也可以由單個QUAD覆蓋屏幕的片段完成...

  3. 在另一軸方向上對結果執行#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遍。

看看以下類似的質量檢查:

我認為這是一個很好的起點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM