【大模型学习cuda】入们第一个例子-向量和

cpp 复制代码
        `````````#include <stdio.h>
#include <cuda_runtime.h>

// CUDA核函数:向量加法
// 每个线程处理一个元素
__global__ void vectorAdd(const float* a, const float* b, float* c, int n) {
    // 计算全局线程索引
    int idx = blockIdx.x * blockDim.x + threadIdx.x;

    // 确保不越界
    if (idx < n) {
        c[idx] = a[idx] + b[idx];
    }
}

// 主函数
int main() {
    int n = 1024 * 1024;  // 向量大小:1M个元素
    size_t size = n * sizeof(float);

    printf("CUDA向量加法程序\n");
    printf("向量大小: %d\n", n);

    // 1. 分配主机内存
    float *h_a = (float*)malloc(size);
    float *h_b = (float*)malloc(size);
    float *h_c = (float*)malloc(size);

    // 2. 初始化主机数据
    for (int i = 0; i < n; i++) {
        h_a[i] = (float)i;
        h_b[i] = (float)i * 2.0f;
    }

    // 3. 分配设备内存
    float *d_a, *d_b, *d_c;
    cudaMalloc((void**)&d_a, size);
    cudaMalloc((void**)&d_b, size);
    cudaMalloc((void**)&d_c, size);

    // 4. 将数据从主机复制到设备
    cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);

    // 5. 启动CUDA核函数
    int threadsPerBlock = 256;
    int blocksPerGrid = (n + threadsPerBlock - 1) / threadsPerBlock;

    printf("\n启动核函数...\n");
    printf("每个线程块包含 %d 个线程\n", threadsPerBlock);
    printf("总共启动 %d 个线程块\n", blocksPerGrid);

    vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_a, d_b, d_c, n);

    // 6. 等待GPU完成计算并检查错误
    cudaError_t err = cudaGetLastError();
    if (err != cudaSuccess) {
        printf("CUDA错误: %s\n", cudaGetErrorString(err));
        return -1;
    }

    // 7. 将结果从设备复制回主机
    cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);

    // 8. 验证结果
    printf("\n验证结果...\n");
    bool success = true;
    for (int i = 0; i < 10; i++) {  // 只检查前10个元素
        float expected = h_a[i] + h_b[i];
        if (fabs(h_c[i] - expected) > 1e-5) {
            printf("错误: a[%d]=%.2f, b[%d]=%.2f, c[%d]=%.2f (期望值: %.2f)\n",
                   i, h_a[i], i, h_b[i], i, h_c[i], expected);
            success = false;
            break;
        }
    }

    if (success) {
        printf("✓ 验证通过!前10个元素计算正确。\n");
        printf("示例: a[0]=%.2f + b[0]=%.2f = c[0]=%.2f\n", h_a[0], h_b[0], h_c[0]);
    }

    // 9. 释放内存
    free(h_a);
    free(h_b);
    free(h_c);
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);

    printf("\n程序执行完成!\n");
    return 0;
}
cpp 复制代码
   > (void**)&d_a  这里为什么是这个样子的?                                                                                                                                                                                    

✦ cudaMalloc 使用 (void**)&d_a 是因为 cudaMalloc 函数的签名要求传入一个 void** 类型的参数。

  让我解释一下原因:

  1. cudaMalloc 函数签名
   cudaError_t cudaMalloc(void** devPtr, size_t size);
   - 第一个参数是 void** 类型(指向指针的指针)
   - 第二个参数是分配的内存大小

  2. 为什么需要指向指针的指针?

  cudaMalloc 需要修改指针 d_a 本身的值(让它指向设备内存地址),而不仅仅是修改指针指向的内容。

  在 C/C++ 中,如果要修改一个变量的值,需要传递它的地址:
   - 修改普通变量 → 传递 &变量(地址)
   - 修改指针变量 → 传递 &指针(指针的地址)

  3. 类型转换过程

   float *d_a;  // d_a 的类型是 float*

   - &d_a 的类型是 float**(指向 float 指针的指针)
   - cudaMalloc 要求 void**
   - 所以需要强制转换:(void**)&d_a

  4. 类比:标准库的 malloc

  对比一下 CPU 端的内存分配:
   // CPU 端 - malloc 返回值
   float *d_a = (float*)malloc(size);  // 直接返回指针

   // GPU 端 - cudaMalloc 通过参数返回
   float *d_a;
   cudaMalloc((void**)&d_a, size);     // 通过参数返回指针地址

  5. 完整示例
   float *d_a;           // 声明一个 float 指针
   cudaMalloc((void**)&d_a, size);  // 将 float** 转换为 void** 传递

   // 执行后,d_a 被赋值为设备内存的地址

✦ 简单来说:因为 `cudaMalloc` 需要修改指针 `d_a` 的值,让它指向新分配的设备内存,所以必须传递 `d_a` 
  的地址(即 `&d_a`),并进行类型转换。

成!编译和执行都已成功。

总结:

  1. 编译命令:

    nvcc vector_add.cu -o vector_add

  2. 执行命令:

    ./vector_add

  3. 程序输出:

    • 程序处理了 1,048,576 (1M) 个元素的向量加法
    • 使用了 256 个线程/块,共 4096 个线程块
    • 计算结果验证通过 ✓

✦ CUDA 程序成功在 GPU 上运行并完成了向量加法计算!

相关推荐
世人万千丶18 小时前
参数管理_Flutter在鸿蒙平台路由参数最佳实践
学习·flutter·华为·harmonyos·鸿蒙
遇乐的果园21 小时前
前端学习笔记-vue加载渲染优化
前端·笔记·学习
遇乐的果园1 天前
前端学习笔记-vue状态管理优化
前端·笔记·学习
从零开始的代码生活_1 天前
C++ 继承详解:访问控制、对象模型、菱形继承与设计取舍
开发语言·c++·后端·学习·算法
可乐奶茶sky1 天前
AI Agent 学习
人工智能·学习
茯苓gao1 天前
嵌入式开发笔记:EtherCAT协议从硬件到软件完整配置指南——从零搭建一套EtherCAT通信系统
笔记·嵌入式硬件·学习
RD_daoyi1 天前
外链权重暴跌至13%,品牌提及反超传统链接——2026年不做Digital PR,你的独立站等于隐形
运维·网络·学习·机器学习·搜索引擎
chase。1 天前
【学习笔记】PointWorld:迈向通用机器人操控的3D世界模型
笔记·学习·机器人
AOwhisky1 天前
Python 学习笔记(第十三期)——运维自动化(下·前篇):远程命令执行——paramiko基础篇
运维·python·学习·云原生·自动化·运维开发·paramiko
前端世界1 天前
MySQL 基础入门(学习第4天)——约束与 DML 操作详解
数据库·学习·mysql