串行代码片段如下所示:
int i, j;
for(j=0; j<ny; j++)
{
for(i=0; i<nx; i++)
{
x[i + j*nx] *= y[i];
}
}
我使用这个内核将其转换为CUDA:
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int i,j;
for(tid = 0; tid <nx*ny; tid++)
{
j = tid/nx;
i = tid - j*nx;
x[tid] *= y[i];
}
然而GPU内核没有提供任何加速改进?关于更好解决方案的任何建议?提前致谢
答案 0 :(得分:6)
如果这是序列号:
int i, j;
for(j=0; j<ny; j++)
{
for(i=0; i<nx; i++)
{
x[i + j*nx] *= y[i];
}
}
那么你应该这样做:
__global__ void fn(float *x, int nx)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int j = tid/nx, i = tid - j * nx;
x[tid] *= y[i];
}
fn<<<nx*ny/B, B>>>(x, nx); // with B = 256, 512, etc.
你正在做的事情是相当奇怪的:你指示CUDA内核的每个线程迭代 0和nx * ny之间的tid的所有值,并计算与你的相同的函数CPU版!而且,不是只是遍历索引,而是实际上比使用CPU版本更有效地执行循环 less ;换句话说,你在每个线程中做同样的事情,效率低于在CPU上的1个线程中做的事情。难怪这会慢一点;它应该更快,更慢。你的CUDA内核是:
int **tid** = blockIdx.x * blockDim.x + threadIdx.x;
int i,j;
for(**tid** = 0; **tid** <nx*ny; **tid**++)
{
j = tid/nx;
i = tid - j*nx;
x[tid] *= y[i];
}
这为每个线程执行nx * ny迭代,与主机代码相同;你失去了并行性的所有好处,因为每个线程都在做同样的事情;你可以使用GPU上的一个线程获得相同的性能,并获得相同的结果!
如果这是您的CUDA源文件中的逐字代码,则需要更改它并重做比较;如果这是您编写的代码,以帮助解释您的代码正在为非CUDA观众做些什么,那么您需要提供您的实际CUDA代码,以便我们可以看到正在发生的事情......实际情况,性能分析我已经完成了 - 这是微不足道的 - 是你所能想到的。
答案 1 :(得分:2)
鉴于您对this answer的评论:
nx * ny = 2205;所以我没用过。块= (nx * ny +(threads-1))/ threads and threads = 64。
意味着你打算每次计算启动一个线程,正确的CUDA实现就是:
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int j = tid/nx;
int i = tid - j*nx;
if (tid < (nx*ny))
x[tid] *= y[i];
如果您打算让每个线程为每个内核启动计算多个计算,那么您可以调整网格大小以“填充”目标GPU上的每个SM,而不是使用与输入大小相同数量的线程,然后做类似的事情:
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int gsize = blockDim.x * gridDim.x;
int i,j;
for(; tid <nx*ny; tid+=gsize)
{
j = tid/nx;
i = tid - j*nx;
x[tid] *= y[i];
}
这将使您至少合并读取和写入x
,并删除发布版本中的大量冗余计算。可以进行许多进一步的优化,但它需要有关问题的更多信息,而不是问题和后续评论中提供的信息。您的索引方案包含整数除法,然后是每次计算的整数乘法加法。对于每个输入值的单个FLOP来说,这是很多开销。但是,尽管如此,如果我引用的问题大小是你感兴趣的实际问题大小,那么GPU永远不会比一个适度的主机CPU更快。你需要多个数量级的大问题来实现使用GPU进行这种低算术强度操作的有用加速。
答案 2 :(得分:-1)
块有多大?可能是将少量数据复制到GPU并设置环境所需的时间比计算时间长得多。
还要记住,CUDA会在第一次运行时进行jit编译,以便获得运行多次所需的准确基准测试。
答案 3 :(得分:-2)
使用共享内存尝试此操作。最好的实现之一:
// Matrices are stored in row-major order:
// M(row, col) = *(M.elements + row * M.stride + col)
typedef struct {
int width;
int height;
int stride; // In number of elements
float *elements;
} Matrix;
// Thread block size
#define BLOCK_SIZE 16
// Get a matrix element
__device__ float GetElement(const Matrix A, int row, int col)
{
return A.elements[row * A.stride + col];
}
// Set a matrix element
__device__ void SetElement(Matrix A, int row, int col, float value)
{
A.elements[row * A.stride + col] = value;
}
// Get the BLOCK_SIZExBLOCK_SIZE sub-matrix Asub of A that is
// located col sub-matrices to the right and row sub-matrices down
// from the upper-left corner of A
__device__ Matrix GetSubMatrix(Matrix A, int row, int col)
{
Matrix Asub;
Asub.width = BLOCK_SIZE; Asub.height = BLOCK_SIZE;
Asub.stride = A.stride;
Asub.elements = &A.elements[A.stride * BLOCK_SIZE * row +
BLOCK_SIZE * col];
return Asub;
}
// Forward declaration of the matrix multiplication kernel
__global__ void MatMulKernel(const Matrix, const Matrix, Matrix);
// Matrix multiplication - Host code
// Matrix dimensions are assumed to be multiples of BLOCK_SIZE
void MatMul(const Matrix A, const Matrix B, Matrix C)
{
// Same as in previous example, except the followings:
// d_A.width = d_A.stride = A.width;
// d_B.width = d_B.stride = B.width;
// d_C.width = d_C.stride = C.width;
}
// Matrix multiplication kernel called by MatMul()
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)
{
// Block row and column
int blockRow = blockIdx.y;
int blockCol = blockIdx.x;
// Each thread block computes one sub-matrix Csub of C
Matrix Csub = GetSubMatrix(C, blockRow, blockCol);
// Each thread computes one element of Csub
// by accumulating results into Cvalue
float Cvalue = 0;
// Thread row and column within Csub
int row = threadIdx.y;
int col = threadIdx.x;
// Loop over all the sub-matrices of A and B that are
// required to compute Csub
// Multiply each pair of sub-matrices together
// and accumulate the results
for (int m = 0; m < (A.width / BLOCK_SIZE); ++m)
{
// Get sub-matrix Asub of A and Bsub of B
Matrix Asub = GetSubMatrix(A, blockRow, m);
Matrix Bsub = GetSubMatrix(B, m, blockCol);
// Shared memory used to store Asub and Bsub respectively
__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
__shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
// Load Asub and Bsub from device memory to shared memory
// Each thread loads one element of each sub-matrix
As[row][col] = GetElement(Asub, row, col);
Bs[row][col] = GetElement(Bsub, row, col);
// Synchronize to make sure the sub-matrices are loaded
// before starting the computation
__syncthreads();
// Multiply Asub and Bsub together
for (int e = 0; e < BLOCK_SIZE; ++e)
Cvalue += As[row][e] * Bs[e][col];
// Synchronize to make sure that the preceding
// computation is done before loading two new
// sub-matrices of A and B in the next iteration
__syncthreads();
}
// Write Csub to device memory
// Each thread writes one element
SetElement(Csub, row, col, Cvalue);
}