cuda编程之初窥门径

5 阅读3分钟

在高性能计算领域,CUDA(Compute Unified Device Architecture)是 NVIDIA 提供的并行计算平台和编程模型,它允许开发者利用 GPU 的强大计算能力来加速计算密集型任务。本文将通过一个简单的向量加法示例,详细介绍如何使用 CUDA 编写并行程序。

引言

向量加法是线性代数中最基本的操作之一,也是并行计算的经典示例。在传统的 CPU 编程中,向量加法通常需要逐个元素进行计算,这在处理大规模数据时非常耗时。而通过 CUDA,我们可以将计算任务分配到GPU的多个核心上并行执行,从而显著提高计算效率。

#include <stdio.h>
#include <cuda_runtime.h>

__global__ void addVectors(float *a, float *b, float *c, int n){
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < n){
        c[i] = a[i] + b[i];
    }
}

int main(){
    int n = 1024; // 向量长度
    float *a, *b, *c; // 主机端指针
    float *d_a, *d_b, *d_c; // 设备端指针

    // 主机端内存分配
    a = (float*)malloc(n * sizeof(float));
    b = (float*)malloc(n * sizeof(float));
    c = (float*)malloc(n * sizeof(float));

    // 初始化主机端数据
    for (int i = 0; i < n; i++){
        a[i] = i;
        b[i] = 2 * i;
    }

    // 设备端内存分配
    cudaMalloc(&d_a, n * sizeof(float));
    cudaMalloc(&d_b, n * sizeof(float));
    cudaMalloc(&d_c, n * sizeof(float));

    // 将数据从主机端复制到设备端
    cudaMemcpy(d_a, a, n * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, b, n * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_c, c, n * sizeof(float), cudaMemcpyHostToDevice);

    // 启动核函数
    int blockSize = 256; // 每个线程块的线程数
    int numBlocks = (n + blockSize - 1) / blockSize; // 线程块的数量
    addVectors<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n);

    // 将结果从设备端复制回主机端
    cudaMemcpy(c, d_c, n * sizeof(float), cudaMemcpyDeviceToHost);

    // 打印结果
    for (int i = 0; i < n; i++){
        printf("%f + %f = %f\n", a[i], b[i], c[i]);
    }

    // 释放设备端内存
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);

    // 释放主机端内存
    free(a);
    free(b);
    free(c);

    return 0;
}

代码解析

__global__ void addVectors(float *a, float *b, float *c, int n){
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < n){
        c[i] = a[i] + b[i];
    }
}

这里我们创建了一个addVectors的核函数,__global__修饰符表示该函数是一个 CUDA 核函数,运行在 GPU 设备上。int i = blockIdx.x * blockDim.x + threadIdx.x;,这段代码则表示计算当前线程的全局索引。

malloc,表示主机端的内存分配。

cudaMalloc,表示设备端的内存分配。

cudaMemcpy,表示设备端与主机端的内存复制。参数cudaMemcpyHostToDevice表示将数据从主机端复制到设备端,参数cudaMemcpyDeviceToHost表示将数据从设备端复制回主机端。

addVectors<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n);,表示启动核函数。使用addVectors<<<numBlocks, blockSize>>>语法启动核函数,其中的numBlock是线程块的数量,blockSize是每个线程块的线程数。

编译与运行

编译:

nvcc -o vector_add vector_add.cu

运行:

./vector_add

输出

程序运行后,将输出向量加法的结果。以下是部分输出结果:

0.000000 + 0.000000 = 0.000000
1.000000 + 2.000000 = 3.000000
2.000000 + 4.000000 = 6.000000
3.000000 + 6.000000 = 9.000000
4.000000 + 8.000000 = 12.000000
...
1023.000000 + 2046.000000 = 3069.000000

结语

通过这个简单的向量加法示例,我们初步了解了 CUDA 编程的基本流程,包括主机端和设备端的内存管理、数据传输、核函数的启动。