CUDA编程:其三、CUDA向量加法

为什么CUDA并行计算特别适用于向量加法呢?因为计算向量加法的时候各个元素之间互相独立,互不影响。假如所我要计算两个1024长度的向量相加,那么就可以启动1024个线程,每个线程分别计算一个位置上的值。

一、重要函数介绍
(1)cudaMalloc
这个函数负责在GPU申请内存,对应CPU之下的malloc函数。
函数声明如下:

cudaError_t cudaMalloc (void **devPtr, size_t  size ); 

第一个参数表示传入的用来记录数据的指针的地址(有点拗口),第二次参数很好理解,表示申请的GPU空间的大小。
先看一个例子:

int *devPtr = nullptr;
    
// 分配GPU内存
cudaMalloc((void**)&devPtr, 100 * sizeof(int));

这表示在GPU上开辟了一个长度100的int数组。为什么第一个参数要定义成指针的指针呢?
首先回忆一下CPU下面的malloc函数:

void * malloc(size_t  size)

因为返回的是开辟的空间的首地址,调用方可以拿到这个首地址。但是cudaMalloc返回值是void,是通过出参的方式拿到这个地址。在C++里面可以使用引用变量作为入参很好地解决这个问题,但是cuda接口更偏向于c语言,只能使用指针的指针作为出参。
简单地说,第一个参数是形参,函数里面赋值的是局部变量,外界变量不会被改变。所谓指针,就是存储地址的变量,他本身也是有地址的,把devPtr指针的地址传进去,就可以改变函数外部指针的值。
很别扭,C++工程师估计得反应一会,c工程师估计很好理解。

(2)cudaMemcpy
该函数是负责在CPU和GPU之间传递数据的。
函数声明如下:

//表示从CPU到GPU拷贝数据
cudaMemcpy(d_A,h_A,nBytes,cudaMemcpyHostToDevice);

//表示从GPU到CPU拷贝数据
cudaMemcpy(h_A,d_A,nBytes,cudaMemcpyDeviceToHost);

一般的流程是,先在CPU处生成数据,之后拷贝给GPU;GPU处理好后拷贝回给CPU。

(3)cudaFree
不多说了,释放指针而已。

二、向量加法的不同实现
(1)一个block多个thread。
假如向量长度是n,一个block中线程个数是m,相对于每个线程要计算n/m个数字。看代码:

#include <iostream>
#define  N 10

__global__ void addVector(int *a, int *b, int *c, int n)
{
    int threadId = threadIdx.x;
    int step = blockDim.x;
    while(threadId < n){
        c[threadId] = a[threadId]+b[threadId];
        threadId += step;
    }
}

int main() {
    int a[N], b[N], c[N];
    int *gpu_a, *gpu_b, *gpu_c;
    for(int i=0; i<N; ++i) // 为数组a、b赋值
    {
        a[i] = i;
        b[i] = i * i;
    }
    cudaMalloc(&gpu_a, sizeof(int) * N);
    cudaMemcpy(gpu_a, a, sizeof(int) * N, cudaMemcpyHostToDevice);

    cudaMalloc(&gpu_b, sizeof(int) * N);
    cudaMemcpy(gpu_b, b, sizeof(int) * N, cudaMemcpyHostToDevice);

    cudaMalloc(&gpu_c, sizeof(int) * N);
    cudaMemcpy(gpu_c, c, sizeof(int) * N, cudaMemcpyHostToDevice);

    addVector<<<1, 3>>>(gpu_a, gpu_b, gpu_c, N);
    cudaMemcpy(c, gpu_c, sizeof(int) * N, cudaMemcpyDeviceToHost);

    for(int i=0; i<N; ++i)
    {
        printf("%d + %d = %d \n", a[i], b[i], c[i]);
    }

    cudaFree(gpu_a);
    cudaFree(gpu_b);
    cudaFree(gpu_c);

    return 0;
}

线程ID的作用就体现出来了,通过线程id,可以划分出来每个线程处理的区间。

(2)多个block,一个block下面多个线程:

#include <iostream>
#define  N 10

__global__ void addVector(int *a, int *b, int *c, int n)
{
    int threadId = blockIdx.x* blockDim.x + threadIdx.x;
    int step = gridDim.x * blockDim.x; //线程数量
    while(threadId < n){
        c[threadId] = a[threadId]+b[threadId];
        threadId += step;
    }
}

int main() {
    int a[N], b[N], c[N];
    int *gpu_a, *gpu_b, *gpu_c;
    for(int i=0; i<N; ++i) // 为数组a、b赋值
    {
        a[i] = i;
        b[i] = i * i;
    }
    cudaMalloc(&gpu_a, sizeof(int) * N);
    cudaMemcpy(gpu_a, a, sizeof(int) * N, cudaMemcpyHostToDevice);

    cudaMalloc(&gpu_b, sizeof(int) * N);
    cudaMemcpy(gpu_b, b, sizeof(int) * N, cudaMemcpyHostToDevice);

    cudaMalloc(&gpu_c, sizeof(int) * N);
    cudaMemcpy(gpu_c, c, sizeof(int) * N, cudaMemcpyHostToDevice);

    addVector<<<2, 2>>>(gpu_a, gpu_b, gpu_c, N);
    cudaMemcpy(c, gpu_c, sizeof(int) * N, cudaMemcpyDeviceToHost);

    for(int i=0; i<N; ++i)
    {
        printf("%d + %d = %d \n", a[i], b[i], c[i]);
    }

    cudaFree(gpu_a);
    cudaFree(gpu_b);
    cudaFree(gpu_c);

    return 0;
}

相关推荐

  1. CUDA编程:其三、CUDA向量加法

    2024-04-24 03:58:02       35 阅读
  2. cuda

    2024-04-24 03:58:02       48 阅读
  3. 源码编译OpenCV 启用cuda 加速

    2024-04-24 03:58:02       38 阅读
  4. CUDA编程:其四、CUDA矩阵乘法

    2024-04-24 03:58:02       35 阅读
  5. 源代码编译cuda opencv

    2024-04-24 03:58:02       58 阅读
  6. Opencv cuda版本编译

    2024-04-24 03:58:02       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-24 03:58:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-24 03:58:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-24 03:58:02       82 阅读
  4. Python语言-面向对象

    2024-04-24 03:58:02       91 阅读

热门阅读

  1. leveldb 键值数据库

    2024-04-24 03:58:02       38 阅读
  2. Spring源码中的简单工厂模式

    2024-04-24 03:58:02       41 阅读
  3. 【无标题】

    2024-04-24 03:58:02       39 阅读
  4. ionic 中对Input输入框、select下拉框进行solr检索

    2024-04-24 03:58:02       38 阅读
  5. C++ day1

    C++ day1

    2024-04-24 03:58:02      32 阅读
  6. LeetCode-11-盛最多水的容器

    2024-04-24 03:58:02       31 阅读
  7. npm——基本使用

    2024-04-24 03:58:02       39 阅读
  8. CUDA_cudaFree_释放Stream_cudaError_t 错误类型码解释

    2024-04-24 03:58:02       29 阅读
  9. 算法训练营day21

    2024-04-24 03:58:02       35 阅读
  10. springCloud是什么,怎么创建

    2024-04-24 03:58:02       35 阅读
  11. 数据安全:口令

    2024-04-24 03:58:02       34 阅读