[英]CUDA speedup for simple calculations
我在cuda_computation.cu
有以下代码
#include <iostream>
#include <stdio.h>
#include <cuda.h>
#include <assert.h>
void checkCUDAError(const char *msg);
__global__ void euclid_kernel(float *x, float* y, float* f)
{
int idx = blockIdx.x*blockDim.x + threadIdx.x;
int i = blockIdx.x;
int j = threadIdx.x;
f[idx] = sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
}
int main()
{
float *xh;
float *yh;
float *fh;
float *xd;
float *yd;
float *fd;
size_t n = 256;
size_t numBlocks = n;
size_t numThreadsPerBlock = n;
size_t memSize = numBlocks * numThreadsPerBlock * sizeof(float);
xh = (float *) malloc(n * sizeof(float));
yh = (float *) malloc(n * sizeof(float));
fh = (float *) malloc(memSize);
for(int ii(0); ii!=n; ++ii)
{
xh[ii] = ii;
yh[ii] = ii;
}
cudaMalloc( (void **) &xd, n * sizeof(float) );
cudaMalloc( (void **) &yd, n * sizeof(float) );
cudaMalloc( (void **) &fd, memSize );
for(int run(0); run!=10000; ++run)
{
//change value to avoid optimizations
xh[0] = ((float)run)/10000.0;
cudaMemcpy( xd, xh, n * sizeof(float), cudaMemcpyHostToDevice );
checkCUDAError("cudaMemcpy");
cudaMemcpy( yd, yh, n * sizeof(float), cudaMemcpyHostToDevice );
checkCUDAError("cudaMemcpy");
dim3 dimGrid(numBlocks);
dim3 dimBlock(numThreadsPerBlock);
euclid_kernel<<< dimGrid, dimBlock >>>( xd, yd, fd );
cudaThreadSynchronize();
checkCUDAError("kernel execution");
cudaMemcpy( fh, fd, memSize, cudaMemcpyDeviceToHost );
checkCUDAError("cudaMemcpy");
}
cudaFree(xd);
cudaFree(yd);
cudaFree(fd);
free(xh);
free(yh);
free(fh);
return 0;
}
void checkCUDAError(const char *msg)
{
cudaError_t err = cudaGetLastError();
if( cudaSuccess != err)
{
fprintf(stderr, "Cuda error: %s: %s.\n", msg, cudaGetErrorString( err) );
exit(-1);
}
}
在FX QUADRO 380上运行大约需要6英寸,而仅使用一个i7-870内核的相应串行版本大约需要3英寸。 我想念什么吗? 是否在某种程度上优化了代码? 还是仅出于预期的行为,对于简单的计算(如所有欧几里得距离),移动内存所需的开销超过了计算增益?
我认为您在移动数据的时间上被杀了。 特别是由于您要使用单个值调用CUDA内核,因此将一大组值上传为一维数组并对其进行操作可能更快。
同样,在Cuda的硬件中sqrt并没有完成(至少在我的GPU上没有),而CPU为此已经优化了FPU硬件,可能比GPU快10倍,而对于这样的小任务,可能会将所有结果保留在定时运行之间进行缓存。
减少全局内存读取,因为它们很昂贵。 每个线程有4个全局内存读取,使用共享内存可以减少为2个。
__global__ void euclid_kernel(const float * inX_g, const float* inY_g, float * outF_g)
{
const unsigned int threadId = blockIdx.x * blockDim.x + threadIdx.x;
__shared__ float xBlock_s;
__shared__ float yBlock_s;
if(threadIdx.x == 0)
{
xBlock_s = inX_g[blockIdx.x];
yBlock_s = inY_g[blockIdx.x];
}
__syncthreads();
float xSub = xBlock_s - inX_g[threadIdx.x];
float ySub = yBlock_s - inY_g[threadIdx.x];
outF_g[threadId] = sqrt(xSub * xSub + ySub * ySub);
}
您还应该使用不同的块大小进行测试(只要您具有100%的占用率)。
您正在分解问题,以便每个块负责一个i与所有256个j。 这是不好的位置,因为必须为每个块重新加载256个j,总共加载2 * 256 *(256 + 1)个。 相反,请拆分网格,以使每个块负责某个范围(例如16个i和16个j),这仍然是256个块* 256个线程。 但是现在每个块仅加载2 *(16 + 16)个值,总共加载2 * 256 * 32。 这个想法是,尽可能多地重用每个加载的值。 对于256x256,这可能不会产生很大的影响,但是随着尺寸的扩大,它变得越来越重要。
此优化用于有效的矩阵乘法,该乘法具有类似的局部性问题。 有关更多详细信息,请参见http://en.wikipedia.org/wiki/Loop_tiling或通过google搜索“优化矩阵乘法”。 也许NVIDIA SDK中的矩阵乘法内核提供了一些细节和想法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.