我正在尝试在 CUDA C++ 代码上运行 vector 步长加法函数,但对于大小为 5,000,000 的大型 float 组,它的运行速度也比我的 CPU 版本慢。以下是我正在谈论的相关 CUDA 和 cpu 代码:
#define THREADS_PER_BLOCK 1024
typedef float real;
__global__ void vectorStepAddKernel2(real*x, real*y, real*z, real alpha, real beta, int size, int xstep, int ystep, int zstep)
{
int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < size)
{
x[i*xstep] = alpha* y[i*ystep] + beta*z[i*zstep];
}
}
cudaError_t vectorStepAdd2(real *x, real*y, real* z, real alpha, real beta, int size, int xstep, int ystep, int zstep)
{
cudaError_t cudaStatus;
int threadsPerBlock = THREADS_PER_BLOCK;
int blocksPerGrid = (size + threadsPerBlock -1)/threadsPerBlock;
vectorStepAddKernel2<<<blocksPerGrid, threadsPerBlock>>>(x, y, z, alpha, beta, size, xstep, ystep, zstep);
// cudaDeviceSynchronize waits for the kernel to finish, and returns
// any errors encountered during the launch.
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching vectorStepAddKernel!\n", cudaStatus);
exit(1);
}
return cudaStatus;
}
//CPU function:
void vectorStepAdd3(real *x, real*y, real* z, real alpha, real beta, int size, int xstep, int ystep, int zstep)
{
for(int i=0;i<size;i++)
{
x[i*xstep] = alpha* y[i*ystep] + beta*z[i*zstep];
}
}
当 3 个数组的大小均为 5,000,000 且大小为 50,000(即,以这种逐步方式将 50,000 个元素相加)时,调用 vectorStepAdd2 的计算速度比 vectorStepAdd3 慢。
关于我可以做什么来加速 GPU 代码有什么想法吗? 我的设备是 Tesla M2090 GPU
谢谢
最佳答案
回答您的问题“我可以做些什么来加速 GPU 代码?”
首先,让我先说明所提议的操作 X = alpha * Y + beta * Z 对每字节数据传输所需的计算强度并不高。结果,我无法在这个特定代码上击败 CPU 时间。然而,涵盖 2 个想法以加速此代码可能是有益的:
使用 page-locked用于数据传输操作的存储器。这使 GPU 版本的数据传输时间减少了大约 2 倍,这在 GPU 版本的整体执行时间中占主导地位。
在 cudaMemcpy2D 中使用跨步复制技术正如@njuffa 所提议的那样here .结果是双重的:我们可以将数据传输量减少到仅计算所需的数量,并且然后我们可以重新编写内核以按照建议连续地对数据进行操作评论(再次由 njuffa 发表)。这使数据传输时间进一步缩短了 3 倍,内核计算时间缩短了约 10 倍。
此代码提供了这些操作的示例:
#include <stdio.h>
#include <stdlib.h>
#define THREADS_PER_BLOCK 1024
#define DSIZE 5000000
#define WSIZE 50000
#define XSTEP 47
#define YSTEP 43
#define ZSTEP 41
#define TOL 0.00001f
#define cudaCheckErrors(msg) \
do { \
cudaError_t __err = cudaGetLastError(); \
if (__err != cudaSuccess) { \
fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
msg, cudaGetErrorString(__err), \
__FILE__, __LINE__); \
fprintf(stderr, "*** FAILED - ABORTING\n"); \
exit(1); \
} \
} while (0)
typedef float real;
__global__ void vectorStepAddKernel2(real *x, real *y, real *z, real alpha, real beta, int size, int xstep, int ystep, int zstep)
{
int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < size)
{
x[i*xstep] = alpha* y[i*ystep] + beta*z[i*zstep];
}
}
__global__ void vectorStepAddKernel2i(real *x, real *y, real *z, real alpha, real beta, int size)
{
int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < size)
{
x[i] = alpha* y[i] + beta*z[i];
}
}
void vectorStepAdd2(real *x, real *y, real *z, real alpha, real beta, int size, int xstep, int ystep, int zstep)
{
int threadsPerBlock = THREADS_PER_BLOCK;
int blocksPerGrid = (size + threadsPerBlock -1)/threadsPerBlock;
vectorStepAddKernel2<<<blocksPerGrid, threadsPerBlock>>>(x, y, z, alpha, beta, size, xstep, ystep, zstep);
cudaDeviceSynchronize();
cudaCheckErrors("kernel2 fail");
}
void vectorStepAdd2i(real *x, real *y, real *z, real alpha, real beta, int size)
{
int threadsPerBlock = THREADS_PER_BLOCK;
int blocksPerGrid = (size + threadsPerBlock -1)/threadsPerBlock;
vectorStepAddKernel2i<<<blocksPerGrid, threadsPerBlock>>>(x, y, z, alpha, beta, size);
cudaDeviceSynchronize();
cudaCheckErrors("kernel3 fail");
}
//CPU function:
void vectorStepAdd3(real *x, real*y, real* z, real alpha, real beta, int size, int xstep, int ystep, int zstep)
{
for(int i=0;i<size;i++)
{
x[i*xstep] = alpha* y[i*ystep] + beta*z[i*zstep];
}
}
int main() {
real *h_x, *h_y, *h_z, *c_x, *h_x1;
real *d_x, *d_y, *d_z, *d_x1, *d_y1, *d_z1;
int dsize = DSIZE;
int wsize = WSIZE;
int xstep = XSTEP;
int ystep = YSTEP;
int zstep = ZSTEP;
real alpha = 0.5f;
real beta = 0.5f;
float et;
/*
h_x = (real *)malloc(dsize*sizeof(real));
if (h_x == 0){printf("malloc1 fail\n"); return 1;}
h_y = (real *)malloc(dsize*sizeof(real));
if (h_y == 0){printf("malloc2 fail\n"); return 1;}
h_z = (real *)malloc(dsize*sizeof(real));
if (h_z == 0){printf("malloc3 fail\n"); return 1;}
c_x = (real *)malloc(dsize*sizeof(real));
if (c_x == 0){printf("malloc4 fail\n"); return 1;}
h_x1 = (real *)malloc(dsize*sizeof(real));
if (h_x1 == 0){printf("malloc1 fail\n"); return 1;}
*/
cudaHostAlloc((void **)&h_x, dsize*sizeof(real), cudaHostAllocDefault);
cudaCheckErrors("cuda Host Alloc 1 fail");
cudaHostAlloc((void **)&h_y, dsize*sizeof(real), cudaHostAllocDefault);
cudaCheckErrors("cuda Host Alloc 2 fail");
cudaHostAlloc((void **)&h_z, dsize*sizeof(real), cudaHostAllocDefault);
cudaCheckErrors("cuda Host Alloc 3 fail");
cudaHostAlloc((void **)&c_x, dsize*sizeof(real), cudaHostAllocDefault);
cudaCheckErrors("cuda Host Alloc 4 fail");
cudaHostAlloc((void **)&h_x1, dsize*sizeof(real), cudaHostAllocDefault);
cudaCheckErrors("cuda Host Alloc 5 fail");
cudaMalloc((void **)&d_x, dsize*sizeof(real));
cudaCheckErrors("cuda malloc1 fail");
cudaMalloc((void **)&d_y, dsize*sizeof(real));
cudaCheckErrors("cuda malloc2 fail");
cudaMalloc((void **)&d_z, dsize*sizeof(real));
cudaCheckErrors("cuda malloc3 fail");
cudaMalloc((void **)&d_x1, wsize*sizeof(real));
cudaCheckErrors("cuda malloc4 fail");
cudaMalloc((void **)&d_y1, wsize*sizeof(real));
cudaCheckErrors("cuda malloc5 fail");
cudaMalloc((void **)&d_z1, wsize*sizeof(real));
cudaCheckErrors("cuda malloc6 fail");
for (int i=0; i< dsize; i++){
h_x[i] = 0.0f;
h_x1[i] = 0.0f;
c_x[i] = 0.0f;
h_y[i] = (real)(rand()/(real)RAND_MAX);
h_z[i] = (real)(rand()/(real)RAND_MAX);
}
cudaEvent_t t_start, t_stop, k_start, k_stop;
cudaEventCreate(&t_start);
cudaEventCreate(&t_stop);
cudaEventCreate(&k_start);
cudaEventCreate(&k_stop);
cudaCheckErrors("event fail");
// first test original GPU version
cudaEventRecord(t_start);
cudaMemcpy(d_x, h_x, dsize * sizeof(real), cudaMemcpyHostToDevice);
cudaCheckErrors("cuda memcpy 1 fail");
cudaMemcpy(d_y, h_y, dsize * sizeof(real), cudaMemcpyHostToDevice);
cudaCheckErrors("cuda memcpy 2 fail");
cudaMemcpy(d_z, h_z, dsize * sizeof(real), cudaMemcpyHostToDevice);
cudaCheckErrors("cuda memcpy 3 fail");
cudaEventRecord(k_start);
vectorStepAdd2(d_x, d_y, d_z, alpha, beta, wsize, xstep, ystep, zstep);
cudaEventRecord(k_stop);
cudaMemcpy(h_x, d_x, dsize * sizeof(real), cudaMemcpyDeviceToHost);
cudaCheckErrors("cuda memcpy 4 fail");
cudaEventRecord(t_stop);
cudaEventSynchronize(t_stop);
cudaEventElapsedTime(&et, t_start, t_stop);
printf("GPU original version total elapsed time is: %f ms.\n", et);
cudaEventElapsedTime(&et, k_start, k_stop);
printf("GPU original kernel elapsed time is: %f ms.\n", et);
//now test CPU version
cudaEventRecord(t_start);
vectorStepAdd3(c_x, h_y, h_z, alpha, beta, wsize, xstep, ystep, zstep);
cudaEventRecord(t_stop);
cudaEventSynchronize(t_stop);
cudaEventElapsedTime(&et, t_start, t_stop);
printf("CPU version total elapsed time is: %f ms.\n", et);
for (int i = 0; i< dsize; i++)
if (fabsf((float)(h_x[i]-c_x[i])) > TOL) {
printf("cpu/gpu results mismatch at i = %d, cpu = %f, gpu = %f\n", i, c_x[i], h_x[i]);
return 1;
}
// now test improved GPU version
cudaEventRecord(t_start);
// cudaMemcpy2D(d_x1, sizeof(real), h_x, xstep * sizeof(real), sizeof(real), wsize, cudaMemcpyHostToDevice);
// cudaCheckErrors("cuda memcpy 5 fail");
cudaMemcpy2D(d_y1, sizeof(real), h_y, ystep * sizeof(real), sizeof(real), wsize, cudaMemcpyHostToDevice);
cudaCheckErrors("cuda memcpy 6 fail");
cudaMemcpy2D(d_z1, sizeof(real), h_z, zstep * sizeof(real), sizeof(real), wsize, cudaMemcpyHostToDevice);
cudaCheckErrors("cuda memcpy 7 fail");
cudaEventRecord(k_start);
vectorStepAdd2i(d_x1, d_y1, d_z1, alpha, beta, wsize);
cudaEventRecord(k_stop);
cudaMemcpy2D(h_x1, xstep*sizeof(real), d_x1, sizeof(real), sizeof(real), wsize, cudaMemcpyDeviceToHost);
cudaCheckErrors("cuda memcpy 8 fail");
cudaEventRecord(t_stop);
cudaEventSynchronize(t_stop);
cudaEventElapsedTime(&et, t_start, t_stop);
printf("GPU improved version total elapsed time is: %f ms.\n", et);
cudaEventElapsedTime(&et, k_start, k_stop);
printf("GPU improved kernel elapsed time is: %f ms.\n", et);
for (int i = 0; i< dsize; i++)
if (fabsf((float)(h_x[i]-h_x1[i])) > TOL) {
printf("gpu/gpu improved results mismatch at i = %d, gpu = %f, gpu imp = %f\n", i, h_x[i], h_x1[i]);
return 1;
}
printf("Results:i CPU GPU GPUi \n");
for (int i = 0; i< 20*xstep; i+=xstep)
printf(" %d %f %f %f %f %f\n",i, c_x[i], h_x[i], h_x1[i]);
return 0;
}
如前所述,我仍然无法超越 CPU 时间,我将这归因于我自己缺乏编码技能,或者这个操作根本没有足够的计算复杂性,无法在 GPU 上发挥作用。不过这里有一些示例结果:
GPU original version total elapsed time is: 13.352256 ms.
GPU original kernel elapsed time is: 0.195808 ms.
CPU version total elapsed time is: 2.599584 ms.
GPU improved version total elapsed time is: 4.228288 ms.
GPU improved kernel elapsed time is: 0.027392 ms.
Results:i CPU GPU GPUi
0 0.617285 0.617285 0.617285
47 0.554522 0.554522 0.554522
94 0.104245 0.104245 0.104245
....
我们可以看到,与原始内核相比,改进后的内核总体上减少了大约 3 倍,这几乎都是由于数据复制时间的减少。数据复制时间的减少是由于使用改进的 2D memcpy,我们只需要复制我们实际使用的数据。 (没有页面锁定内存,这些数据传输时间大约是原来的两倍)。我们还可以看到内核计算时间比原始内核的 CPU 计算快约 10 倍,比改进内核的 CPU 计算快约 100 倍。然而,考虑到数据传输时间,我们无法克服 CPU 速度。
最后一个评论是 cudaMemcpy2D 操作的“成本”仍然很高。对于减少 100 倍的 vector 大小,我们只看到复制时间减少了 3 倍。因此,跨步访问仍然是使用 GPU 的一种相对昂贵的方式。如果我们只是简单地传输包含 50,000 个连续元素的 vector ,我们预计复制时间几乎线性减少 100 倍(与包含 5000000 个元素的原始复制 vector 相比)。这意味着复制时间将少于 1 毫秒,并且我们的 GPU 版本将比 CPU 更快,至少在这个简单的单线程 CPU 代码中是这样。
关于c++ - cuda 上的 vector 步长加法较慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15194798/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
我有一个.pfx格式的证书,我需要使用ruby提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o
我了解instance_eval和class_eval之间的基本区别。我在玩弄时发现的是一些涉及attr_accessor的奇怪东西。这是一个例子:A=Class.newA.class_eval{attr_accessor:x}a=A.newa.x="x"a.x=>"x"#...expectedA.instance_eval{attr_accessor:y}A.y="y"=>NoMethodError:undefinedmethod`y='forA:Classa.y="y"=>"y"#WHATTT?这是怎么回事:instance_eval没有访问我们的A类(对象)然后它实际上将它添加到
我有一个集合选择:此方法的单选按钮是什么?谢谢 最佳答案 Rails3中没有这样的助手。在Rails4中,它是collection_radio_buttons. 关于ruby-on-rails-rails上的ruby:radiobuttonsforcollectionselect,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/18525986/
我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“