我已經實現了CUDA版本的逆離散餘弦變換(IDCT),通過「翻譯」MATLAB內置函數idct.m
到CUDA:自我實現的cuIDFT.cu的遞歸使用導致每次重新運行代碼時改變輸出
- 我實現
cuIDCT.cu
,工作時米= N兩者米和ñ是偶數。
cuIDCT.cu
#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>
#include <cufft.h>
#include <cuComplex.h>
// round up n/m
inline int iDivUp(int n, int m)
{
return (n + m - 1)/m;
}
typedef cufftComplex complex;
#define PI 3.1415926535897932384626433832795028841971693993751
__global__
void idct_ComputeWeightsKernel(const int n, complex *ww)
{
const int pos = threadIdx.x + blockIdx.x * blockDim.x;
if (pos >= n) return;
ww[pos].x = sqrtf(2*n) * cosf(pos*PI/(2*n));
ww[pos].y = sqrtf(2*n) * sinf(pos*PI/(2*n));
}
__global__
void idct_ComputeEvenKernel(const float *b, const int n, const int m, complex *ww, complex *y)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
if (ix >= n || iy >= m) return;
const int pos = ix + iy*n;
// Compute precorrection factor
ww[0].x = ww[0].x/sqrtf(2);
ww[0].y = ww[0].y/sqrtf(2);
y[iy + ix*m].x = ww[iy].x * b[pos];
y[iy + ix*m].y = ww[iy].y * b[pos];
}
__global__
void Reordering_a0_Kernel(complex *y, const int n, const int m, complex *yy)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
if (ix >= n || iy >= m) return;
const int pos = ix + iy*n;
yy[iy + ix*n].x = y[pos].x/(float) n;
yy[iy + ix*n].y = y[pos].y/(float) n;
}
__global__
void Reordering_a_Kernel(complex *yy, const int n, const int m, float *a)
{
const int ix = threadIdx.x + blockIdx.x * blockDim.x;
const int iy = threadIdx.y + blockIdx.y * blockDim.y;
if (ix >= n || iy >= m) return;
const int pos = ix + iy*n;
// Re-order elements of each column according to equations (5.93) and (5.94) in Jain
if (iy < n/2)
{
a[ix + 2*iy*n] = yy[pos].x;
a[ix + (2*iy+1)*n] = yy[ix + (m-iy-1)*n].x;
}
}
/**
* a = idct(b), where a is of size [n m].
* @param b, input array
* @param n, first dimension of a
* @param m, second dimension of a
* @param a, output array
*/
void cuIDCT(float *h_in, int n, int m, float *h_out) // a is of size [n m]
{
const int data_size = n * m * sizeof(float);
// device memory allocation
float *d_in, *d_out;
cudaMalloc(&d_in, data_size);
cudaMalloc(&d_out, data_size);
// transfer data from host to device
cudaMemcpy(d_in, h_in, data_size, cudaMemcpyHostToDevice);
// compute IDCT using CUDA
// begin============================================
// Compute weights
complex *ww;
cudaMalloc(&ww, n*sizeof(complex));
dim3 threads(256);
dim3 blocks(iDivUp(n, threads.x));
idct_ComputeWeightsKernel<<<blocks, threads>>>(n, ww);
complex *y;
complex *yy;
cufftHandle plan;
dim3 threads1(32, 6);
dim3 blocks2(iDivUp(n, threads1.x), iDivUp(m, threads1.y)); // for even case
int Length[1] = {m}; // for each IFFT, the length is m
cudaMalloc(&y, n*m*sizeof(complex));
idct_ComputeEvenKernel<<<blocks2, threads1>>>(d_in, n, m, ww, y);
cufftPlanMany(&plan, 1, Length,
Length, 1, m,
Length, 1, m, CUFFT_C2C, n);
cufftExecC2C(plan, y, y, CUFFT_INVERSE); // y is of size [n m]
cudaMalloc(&yy, n*m*sizeof(complex));
Reordering_a0_Kernel<<<blocks2, threads1>>>(y, n, m, yy);
Reordering_a_Kernel<<<blocks2, threads1>>>(yy, n, m, d_out);
// end============================================
// transfer result from device to host
cudaMemcpy(h_out, d_out, data_size, cudaMemcpyDeviceToHost);
// cleanup
cufftDestroy(plan);
cudaFree(ww);
cudaFree(y);
cudaFree(yy);
cudaFree(d_in);
cudaFree(d_out);
}
然後,我比較我的CUDA IDCT(即cuIDCT.cu
)針對MATLAB idct.m
的使用下面的代碼的結果:
- 測試
main.cpp
功能,和 - 一個MATLAB的主函數
main.m
讀取CU的結果DA並將其與MATLAB進行比較。
的main.cpp
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <helper_functions.h>
#include <stdlib.h>
#include <stdio.h>
// N must equal to M, and both must be even numbers
#define N 256
#define M 256
void WriteDataFile(const char *name, int w, int h, const float *in, const float *out)
{
FILE *stream;
stream = fopen(name, "wb");
float data = 202021.25f;
fwrite(&data, sizeof(float), 1, stream);
fwrite(&w, sizeof(w), 1, stream);
fwrite(&h, sizeof(h), 1, stream);
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++)
{
const int pos = j + i * h;
fwrite(in + pos, sizeof(float), 1, stream);
fwrite(out + pos, sizeof(float), 1, stream);
}
fclose(stream);
}
void cuIDCT(float *b, int n, int m, float *a);
int main()
{
// host memory allocation
float *h_in = new float [N * M];
float *h_out = new float [N * M];
float *h_temp = new float [N * M];
// input data initialization
for (int i = 0; i < N * M; i++)
{
h_in[i] = (float)rand()/(float)RAND_MAX;
h_out[i] = h_in[i];
h_temp[i] = h_in[i];
}
// please comment either one case for testing
// test case 1: use cuIDCT.cu once
// cuIDCT(h_in, N, M, h_out);
// test case 2: iteratively use cuIDCT.cu
for (int i = 0; i < 4; i++)
{
if (i % 2 == 0)
cuIDCT(h_out, N, M, h_temp);
else
cuIDCT(h_temp, N, M, h_out);
}
// write data, for further visualization using MATLAB
WriteDataFile("test.flo", N, M, h_in, h_out);
// cleanup
delete [] h_in;
delete [] h_out;
delete [] h_temp;
cudaDeviceReset();
}
的main.m
clc;clear;
% read
[h_in, h_out] = read_data('test.flo');
% MATLAB result, for test case 1, comment the for-loop
matlab_out = h_in;
for i = 1:4
matlab_out = idct(matlab_out);
end
% compare
err = matlab_out - h_out;
% show
figure(1);
subplot(221); imshow(h_in, []); title('h\_in'); colorbar
subplot(222); imshow(h_out, []); title('h\_out'); colorbar
subplot(223); imshow(matlab_out, []); title('matlab\_out'); colorbar
subplot(224); imshow(err, []); title('error map'); colorbar
disp(['maximum error between CUDA and MATLAB is ' ...
num2str(max(max(abs(err))))])
我在Windows 7上運行的Visual Studio 11(即VS2012)的代碼與Nvidia的GPU的Tesla K20C ,使用CUDA工具包版本7.5,而我的MATLAB版本是R2015b。
我的測試步驟:
- 對於測試用例1。未評論測試案例1和評論測試案例2.
- 運行
main.cpp
。 - 在MATLAB中運行
main.m
。 - 重複步驟1和步驟2(沒有任何更改,只需重新運行代碼)。
- 運行
我重複了第3步20次。輸出結果是不變的,並且導致main.m
是:
的最大誤差爲7.7152e-07。
- 對於測試案例2。未評論測試案例2和評論測試案例1.
- 運行
main.cpp
。 - 在MATLAB中運行
main.m
。 - 重複步驟1和步驟2(沒有任何更改,只需重新運行代碼)。
- 運行
我重複了第3步20次。輸出結果發生改變,並導致main.m
是(沒有足夠的信譽來把所有的圖像,只有錯誤的情況如下所示):
one situation (the wrong one) of test case 2
的最大誤差爲0.45341(2次),0.44898 (1次),0.26186(1次),0.26301(1次)和9.5716e-07(15次)。
從測試結果,我的結論是:
- 從測試用例1:
cuIDCT.cu
在數值上是正確的(錯誤〜10^-7)到idct.m
。 - 從測試案例2:遞歸地使用的
cuIDCT.cu
導致不穩定的結果(即,輸出每變化時重新運行代碼和有時可能數值錯誤的時間,錯誤〜0.1)
我的問題:
從測試案例1我們知道cuIDCT.cu
在數值上正確到idct.m
。但是爲什麼遞歸使用cuIDCT.cu
會導致每次重新運行代碼時產生不同的輸出結果?
任何幫助或建議,高度讚賞。
您的要求之一是測試用例2中運行的可變性。我無法再現該問題。作爲一項測試,我添加了一個'double din = 0.0; double dout = 0.0;'在'WriteDataFile'函數的for循環之前。在循環體中,我使用'din + = in [pos]創建了校驗和; dout + = out [pos];'。在for循環之後,我將這些數量輸出。它們的運行速度保持不變。我認爲你的CUDA代碼不會顯示任何運行的可變性。 [Here](http://pastebin.com/VtYmcxux)是我的完整測試用例。作爲錯誤檢查,您可能想嘗試使用'cuda-memcheck'運行代碼。 –
如果您偶爾(四分之一中的一個)遇到WDDM TDR超時,可能會有變化,因爲您在Windows上。這對我來說似乎不太可能,但排除這一點很好。你應該添加[適當的cuda錯誤檢查](http://stackoverflow.com/questions/14038589/what-is-the-canonical-way-to-check-for-errors-using-the-cuda-runtime-api)任何時候您在使用CUDA代碼時遇到問題。 ('cuda-memcheck'會減慢執行速度)最後,你在VS中構建一個調試項目還是一個發佈項目?你在VS中構建一個win32項目還是一個x64項目? –
@RobertCrovella我根據你的建議修改了我的代碼[http://pastebin.com/3WznVxCa],添加了(a)'checkCudaErrors'和(b)'cufftSafeCall'和(c)你的'din'和'dout'。我使用「Debug - > Start Debugging(F5)」每次運行VS中的代碼。並且每次使用「Debug - > Start Without Debugging(Ctrl + F5)」獲得前一鏈接中的結果,則手動記錄輸出。第一次運行花費了很長時間,但在後續運行中速度更快。從「Property Pages」中顯示「Platform:Active(x64)」。所以我猜測我正在VS中構建一個調試x64項目。 – WDC