我正在寻找可以为CPU(使用g ++)和GPU(使用nvcc)编码的最简洁的代码量,其中GPU始终优于CPU。任何类型的算法都是可以接受的。
澄清一下:我真的在寻找两个简短的代码块,一个用于CPU(使用C ++ in g ++),另一个用于GPU(在nvcc中使用C ++),GPU表现优异。优选地,以秒或毫秒为单位。最短的代码对可能。
答案 0 :(得分:37)
首先,我将重申我的评论:GPU是高带宽,高延迟。试图让GPU在一秒纳秒的工作(或者甚至是毫秒或第二份工作)中击败CPU,完全忽略了做GPU的工作。下面是一些简单的代码,但要真正体会到GPU的性能优势,你需要一个大问题的大小来分摊启动成本......否则,它就没有意义了。我可以在两英尺的比赛中击败法拉利,仅仅是因为需要一些时间来转动钥匙,启动发动机并踩下踏板。这并不意味着我以任何有意义的方式比法拉利更快。
在C ++中使用类似的东西:
#define N (1024*1024)
#define M (1000000)
int main()
{
float data[N]; int count = 0;
for(int i = 0; i < N; i++)
{
data[i] = 1.0f * i / N;
for(int j = 0; j < M; j++)
{
data[i] = data[i] * data[i] - 0.25f;
}
}
int sel;
printf("Enter an index: ");
scanf("%d", &sel);
printf("data[%d] = %f\n", sel, data[sel]);
}
在CUDA / C中使用类似的东西:
#define N (1024*1024)
#define M (1000000)
__global__ void cudakernel(float *buf)
{
int i = threadIdx.x + blockIdx.x * blockDim.x;
buf[i] = 1.0f * i / N;
for(int j = 0; j < M; j++)
buf[i] = buf[i] * buf[i] - 0.25f;
}
int main()
{
float data[N]; int count = 0;
float *d_data;
cudaMalloc(&d_data, N * sizeof(float));
cudakernel<<<N/256, 256>>>(d_data);
cudaMemcpy(data, d_data, N * sizeof(float), cudaMemcpyDeviceToHost);
cudaFree(d_data);
int sel;
printf("Enter an index: ");
scanf("%d", &sel);
printf("data[%d] = %f\n", sel, data[sel]);
}
如果不起作用,请尝试将N和M设置得更大,或将256更改为128或512。
答案 1 :(得分:3)
一种非常非常简单的方法是计算前1000个整数或大矩阵运算的平方。通过避免分支,不需要堆栈等,易于实现并适应GPU的优势。我用OpenCL和C ++做了一段时间,并获得了一些非常令人惊讶的结果。 (2GB GTX460的性能是双核心CPU的40倍。)
您是在寻找示例代码还是只是想法?
修改强>
40倍是双核CPU,而不是四核。
一些指示:
正如我在回复@Paul R的评论中所说,考虑使用OpenCL,因为它可以轻松让你在GPU和CPU上运行相同的代码而无需重新实现它。
(回想起来,这些可能非常明显。)
答案 2 :(得分:2)
我同意David关于OpenCL是一种很好的测试方法的评论,因为在CPU和GPU上运行代码之间切换是多么容易。如果你能够在Mac上工作,Apple有一些很好的示例代码可以执行N-body simulation using OpenCL,内核可以在CPU,GPU或两者上运行。您可以实时切换它们,并在屏幕上显示FPS计数。
对于更简单的情况,他们有一个"hello world" OpenCL command line application,以类似于David所描述的方式计算方块。这可能不需要太多努力就可以移植到非Mac平台上。要在GPU和CPU使用率之间切换,我相信您只需要更改
int gpu = 1;
hello.c源文件中的行为0表示CPU,1表示GPU。
Apple在main Mac source code listing中有更多OpenCL示例代码。
博士。 David Gohara在this introductory video session on the topic结束时(大约34分钟左右)进行分子动力学计算时,有一个OpenCL GPU加速的例子。在他的计算中,他看到了从8个CPU内核运行的并行实现到单个GPU的大约27倍的加速。同样,它不是最简单的示例,但它显示了一个真实的应用程序以及在GPU上运行某些计算的优势。
我也完成了some tinkering in the mobile space using OpenGL ES shaders to perform rudimentary calculations。我发现在GPU上作为着色器运行时,在图像上运行的简单颜色阈值着色器大约比在该特定设备的CPU上执行的计算快14-28倍。
答案 3 :(得分:2)
作为参考,我用时间测量做了类似的例子。使用GTX 660,GPU加速为24倍,其操作除了实际计算外还包括数据传输。
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <time.h>
#define N (1024*1024)
#define M (10000)
#define THREADS_PER_BLOCK 1024
void serial_add(double *a, double *b, double *c, int n, int m)
{
for(int index=0;index<n;index++)
{
for(int j=0;j<m;j++)
{
c[index] = a[index]*a[index] + b[index]*b[index];
}
}
}
__global__ void vector_add(double *a, double *b, double *c)
{
int index = blockIdx.x * blockDim.x + threadIdx.x;
for(int j=0;j<M;j++)
{
c[index] = a[index]*a[index] + b[index]*b[index];
}
}
int main()
{
clock_t start,end;
double *a, *b, *c;
int size = N * sizeof( double );
a = (double *)malloc( size );
b = (double *)malloc( size );
c = (double *)malloc( size );
for( int i = 0; i < N; i++ )
{
a[i] = b[i] = i;
c[i] = 0;
}
start = clock();
serial_add(a, b, c, N, M);
printf( "c[0] = %d\n",0,c[0] );
printf( "c[%d] = %d\n",N-1, c[N-1] );
end = clock();
float time1 = ((float)(end-start))/CLOCKS_PER_SEC;
printf("Serial: %f seconds\n",time1);
start = clock();
double *d_a, *d_b, *d_c;
cudaMalloc( (void **) &d_a, size );
cudaMalloc( (void **) &d_b, size );
cudaMalloc( (void **) &d_c, size );
cudaMemcpy( d_a, a, size, cudaMemcpyHostToDevice );
cudaMemcpy( d_b, b, size, cudaMemcpyHostToDevice );
vector_add<<< (N + (THREADS_PER_BLOCK-1)) / THREADS_PER_BLOCK, THREADS_PER_BLOCK >>>( d_a, d_b, d_c );
cudaMemcpy( c, d_c, size, cudaMemcpyDeviceToHost );
printf( "c[0] = %d\n",0,c[0] );
printf( "c[%d] = %d\n",N-1, c[N-1] );
free(a);
free(b);
free(c);
cudaFree( d_a );
cudaFree( d_b );
cudaFree( d_c );
end = clock();
float time2 = ((float)(end-start))/CLOCKS_PER_SEC;
printf("CUDA: %f seconds, Speedup: %f\n",time2, time1/time2);
return 0;
}