cuda数据传输之cudaMemcpy()和cudaMemcpy2D()详解

1. cudaMemcpy()函数传递一维数组方式:

num_elements = 1000;//1000个元素
double* host_a;//主机端的一维数组数据
double* host_result;//主机端的一维数组用于接收核函数计算结果
for(int i = 0; i<num_elements ; i++)
{
   host_a[i] = i; 
   host_result[i] = 0; 
}

//第一步:定义设备端一维指针用于接收主机内存数据
double* dev_a;
double* dev_result;

//第二步:定义一维数组所占内存字节大小
size_t size_element = num_elements * sizeof(double);//一个double占8个字节,共num_elements个元素

//第三步:分配设备端指针所指的显存空间大小:
cudaMallocManaged(&dev_a, size_element);
cudaMallocManaged(&host_result, size_element);

//第四步:将CPU内存数据传递给GPU显存数据
cudaStatus = cudaMemcpy(dev_a, host_a, size_element, cudaMemcpyHostToDevice);
cudaStatus = cudaMemcpy(dev_result, host_result, size_element, cudaMemcpyHostToDevice);

//定义一个block为256个线程,根据元素个数算出一个grid中分配多少个blocks,这里按一维分布。
dim3  blocks(256);
dim3  grid((num_elements_ + blocks.x - 1) / blocks.x);

// 选择哪个GPU执行工作。如果只有一个GPU,则肯定是零。
cudaStatus = cudaSetDevice(0);
kernel<<<blocks, grid>>>(dev_a, dev_result, num_elements);//执行核函数计算
cudaGetLastError();//检查核函数执行状况
cudaDeviceSynchronize();//同步操作

cudaMemcpy(host_result, dev_result, size_element, cudaMemcpyDeviceToHost);//计算结果传回

//释放设备段指针所申请的内存
cudaFree(dev_a);
cudaFree(dev_result);

//核函数实体
kernel<<<blocks, grid>>>(dev_a, num_elements);
{
    int i = blockIdx.x * blockDim.x + threadIdx.x;//定位到当前线程
    if(i<num_elements)//num_elements个线程同时计算
    {
        dev_result[i] = dev_a[i];
    }
}

2. cudaMemcpy()函数传递二维数组方式:

num_elements = 10000;//100*100个元素
double** host_a= new double* [num_elements];//主机端的二维数组数据
	for (int i = 0; i< 100; i++)
	{
		host_a[i] = new double[100];
		for (int j = 0; j< 100;j++)
		{
			host_a[i][j] = i+j;
		}
	}
double* host_result;
	for (int i = 0; i< 10000; i++)
	{
		host_result[i] = 0;
	}

//第一步:定义设备端一维指针用于接收主机内存数据
double** dev_a;
double* dev_result;

//第二步:定义一维数组所占内存字节大小
size_t size_element = 100* sizeof(double*);//一个double*占8个字节,共100行

//第三步:分配设备端指针所指的显存空间大小:
cudaMallocManaged(&dev_a, size_element);
cudaMallocManaged(&host_result, 10000* sizeof(double));

//第四步:将CPU内存数据传递给GPU显存数据
	for (int i = 0; i < 100; i++)
	{
		size_t size_a = sizeof(double) * 100;//每一行所占的字节大小
		cudaMallocManaged(&dev_a[i], size_a);//给每一行分配显存空间
		cudaStatus = cudaMemcpy(dev_a[i], host_a[i], size_a, cudaMemcpyHostToDevice);//将每一行CPU数据传递给dev_a[i]指针所指的显存空间中
	}
cudaStatus = cudaMemcpy(dev_result, host_result, size_element, cudaMemcpyHostToDevice);

//定义一个block为256个线程,根据元素个数算出一个grid中分配多少个blocks,这里按一维分布。
dim3  blocks(256);
dim3  grid((num_elements_ + blocks.x - 1) / blocks.x);

// 选择哪个GPU执行工作。如果只有一个GPU,则肯定是零。
cudaStatus = cudaSetDevice(0);
kernel<<<blocks, grid>>>(dev_a, dev_result, num_elements);//执行核函数计算
cudaGetLastError();//检查核函数执行状况
cudaDeviceSynchronize();//同步操作

cudaMemcpy(host_result, dev_result, size_element, cudaMemcpyDeviceToHost);//计算结果传回

//释放设备段指针所申请的内存
cudaFree(dev_a);
cudaFree(dev_result);

//核函数实体
kernel<<<blocks, grid>>>(dev_a, num_elements);
{
    int i = blockIdx.x * blockDim.x + threadIdx.x;//定位到当前线程
    if(i<100)//10000个线程同时计算
    {
        for(int j = 0;j<100;j++)
        {
           dev_result[i*100+j] = dev_a[i][j];
        }
    }
}

3. cudaMemcpy2D()传递一维数组、二维数组,以及核函数索引遍历的方法

double* host_a= new host_a[100];//一维数组数据
for (int i = 0; i < 100; i++)
{
	host_a[i] = 0.0;
}
double* dev_a;//定义设备段指针用于接收CPU数据



size_t width_a = 1 * sizeof(char);//定义数组宽度,一维数组,每行只有一个元素,宽度当然是 1 喽!
size_t height= 100;//一维数组共100个元素,即数组长度(或叫高度)
size_t pitch;  //这里直接声明pitch即可,后面配分显存空间的函数会自动算出来,一般是512字节,或者1024字节


//cudaMallocPitch分配显存空间,自动对齐内存,极大的加快了数据传输速度
cudaMallocPitch((void**)&dev_a, &pitch, width_a * sizeof(double), height);

//cudaMemcpy2D()来传递CUP数据到GPU
cudaMemcpy2D(dev_a, pitch, host_a, width_a * sizeof(double), width_a * sizeof(double), height, cudaMemcpyHostToDevice);

//上面是传递一维数组host_a


double* host_result;//用于接收计算结果,定义成了一维的数组来接收
for (int i = 0; i< 100; i++)
{
	host_result[i] = 0;
}
double* dev_result;//核函数中保存计算结果

//下面传递二维数组host_c

size_t width_c = 3 * sizeof(char);//定义100*3的二维数组,每行3个元素,所以宽度为3个字节
size_t height= 100;//一维数组共100个元素,即数组长度(或叫高度)
size_t pitch;
double** host_b= new double* [num_elements];//主机端的二维数组数据
for (int i = 0; i< 100; i++)
{
	host_b[i] = new double[100];
	for (int j = 0; j< 3;j++)
	{
		host_b[i][j] = i+j;
	}
}

//将二维数组host_b转化为一维数组host_c再传入设备端

double* host_c = new double[height* width_c ];//100*3个元素
for (int i = 0; i < height * width_c ; i++)//host_c初始化
{
	host_c[i] = 0.0;
}

for(int i = 0; i<height; i++ )
{
    for(j = 0; j < width_c ; j++)
    {
        host_c[i*width_c  + j] = host_b[i][j];
    ]
}

double* deb_c;//定义设备段指针 

//cudaMallocPitch分配显存空间,自动对其内存,极大的加快了数据传输速度
cudaMallocPitch((void**)&deb_c, &pitch, width_c  * sizeof(double), height);
cudaMallocPitch((void**)&dev_result, &pitch, 1* sizeof(double), height);

//cudaMemcpy2D()来传递CUP数据到GPU
cudaMemcpy2D(deb_c, pitch, host_c, width_c  * sizeof(double), width_c  * sizeof(double), height, cudaMemcpyHostToDevice);

cudaMemcpy2D(dev_result, pitch, host_result, 1* sizeof(double), 1* sizeof(double), height, cudaMemcpyHostToDevice);

dim3  blocks(32);
dim3 grid((num_elements_ + blocks.x - 1) / blocks.x);
kernel<<<blocks, grid>>>(dev_a, width_a, width_c, pitch, height);

cudaGetLastError();
cudaDeviceSynchronize();

cudaMemcpy2D(host_result, width_a * sizeof(double), dev_result, pitch, width_a * sizeof(double), height, cudaMemcpyDeviceToHost);


//核函数实体
kernel<<<blocks, grid>>>(dev_a, deb_c, dev_result,width_a, width_c, pitch, height);
{

    double* a;
    double* c;
    double* result;

    int i = blockIdx.x * blockDim.x + threadIdx.x;//定位到当前线程
    if(i<100)//个线程同时计算
    {
        a = (double*)((char*)dev_a + i*pitch);//a指针指向每行存储的空间,每行有1个元素
        c = (double*)((char*)dev_c + i*pitch);//c指针指向每行存储的空间,每行有3个元素
        result = (double*)((char*)dev_result+ i*pitch);//result指针指向每行存储的空间,每行有3个元素
        for(int j = 0;j<3;j++)
        {
           result[0] += a[0] + c[j];//因为dev_a、dev_result是一维数组,每行只有一个元素,所以索引下标直接是0,而不需要像c[j]一样来遍历
//再者计算的result[0]直接改变了dev_result的对应的值,不需要dev_result[i] = result[0],赋值操作,因为result是指针,直接指向dev_result内存,并对其进行计算。
        }
    }
}

总结:无论一维还是二维数组,都变为一维数组,进行传递,二维数组要注意数组的宽度大小。

核函数中:a = (double*)((char*)dev_a + i*pitch),这样对a的遍历方式是获取数组每一行的数据,对一维数组每行的索引,直接是0下标索引,同时计算a的结果直接作用在dev_a 上,而不需要再将a赋值给dev_a!!!!

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值