GitHub Repo 骨架:Makefile + CUDA 入门程序

GitHub Repo 骨架:Makefile + CUDA 入门程序

这是一个为 CUDA 项目设计的 GitHub repo 骨架,它提供了一个清晰、简洁的起始点,特别适合 CUDA 初学者或希望拥有一个标准化项目结构的开发者。

仓库结构

项目将遵循以下文件和目录结构:

css 复制代码
.
├── Makefile
├── README.md
├── inc
│   └── utils.cuh
├── src
│   ├── basic_vector_add.cu
│   └── starter_kernel.cu
└── data
    └── sample_input.txt

文件说明

Makefile

这个 Makefile 自动化了 CUDA 程序的编译过程。它定义了编译器 (NVCC)、头文件路径和源文件等变量,让你可以通过简单的 make 命令来构建项目。

Makefile

makefile 复制代码
# Makefile for CUDA projects

# 编译器和编译选项
NVCC = nvcc
NVCC_FLAGS = -g -G -std=c++17

# 目录
INC_DIR = ./inc
SRC_DIR = ./src
BUILD_DIR = ./bin

# 源文件
SRC_FILES = $(wildcard $(SRC_DIR)/*.cu)

# 可执行文件
EXECUTABLES = $(patsubst $(SRC_DIR)/%.cu, $(BUILD_DIR)/%, $(SRC_FILES))

# 所有目标
.PHONY: all
all: $(EXECUTABLES)
	@echo "所有 CUDA 程序编译成功。"

# 编译每个源文件的规则
$(BUILD_DIR)/%: $(SRC_DIR)/%.cu
	@mkdir -p $(BUILD_DIR)
	$(NVCC) $(NVCC_FLAGS) -o $@ $< -I$(INC_DIR)
	@echo "编译 $< 为 $@"

# 清理目标
.PHONY: clean
clean:
	@rm -rf $(BUILD_DIR)
	@echo "已清理构建目录。"

inc/utils.cuh

这个头文件包含了一些实用的 CUDA 编程工具,例如用于错误检查的宏。将这些通用功能放在一个专门的头文件中,可以提高代码的复用性和清晰度。

C++

arduino 复制代码
#ifndef UTILS_CUH
#define UTILS_CUH

#include <iostream>
#include <cuda_runtime.h>

#define CHECK(call)                                                              \
{                                                                                \
    const cudaError_t error = call;                                              \
    if (error != cudaSuccess)                                                    \
    {                                                                            \
        fprintf(stderr, "错误: %s:%d, ", __FILE__, __LINE__);                   \
        fprintf(stderr, "错误码: %d, 原因: %s\n", error,                         \
                cudaGetErrorString(error));                                      \
        exit(1);                                                                 \
    }                                                                            \
}

#endif // UTILS_CUH

入门级 CUDA 程序

src/basic_vector_add.cu

这是并行编程中最经典的"Hello, World!"。该程序通过在 GPU 上对两个大型向量进行相加,展示了数据并行性的核心概念。这是一个非常好的、值得运行和理解的第一个例子。

C++

scss 复制代码
#include <iostream>
#include "utils.cuh"

// 核函数:向量相加
__global__ void vectorAdd(const float* A, const float* B, float* C, int numElements) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < numElements) {
        C[i] = A[i] + B[i];
    }
}

int main() {
    int numElements = 50000;
    size_t size = numElements * sizeof(float);

    float *h_A, *h_B, *h_C; // 主机端向量
    float *d_A, *d_B, *d_C; // 设备端向量

    // 分配主机内存
    h_A = (float*)malloc(size);
    h_B = (float*)malloc(size);
    h_C = (float*)malloc(size);

    // 初始化主机向量
    for (int i = 0; i < numElements; ++i) {
        h_A[i] = 1.0f;
        h_B[i] = 2.0f;
    }

    // 分配设备内存并复制主机数据
    CHECK(cudaMalloc((void**)&d_A, size));
    CHECK(cudaMalloc((void**)&d_B, size));
    CHECK(cudaMalloc((void**)&d_C, size));
    CHECK(cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice));
    CHECK(cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice));

    // 定义网格和块的维度
    int threadsPerBlock = 256;
    int blocksPerGrid = (numElements + threadsPerBlock - 1) / threadsPerBlock;

    // 启动核函数
    vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, numElements);
    CHECK(cudaGetLastError());

    // 将结果复制回主机
    CHECK(cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost));

    // 验证结果
    float maxError = 0.0f;
    for (int i = 0; i < numElements; ++i) {
        maxError = fmax(maxError, abs(h_C[i] - 3.0f));
    }

    std::cout << "最大误差: " << maxError << std::endl;
    if (maxError > 1e-5) {
        std::cout << "验证失败。" << std::endl;
    } else {
        std::cout << "验证成功!" << std::endl;
    }

    // 释放内存
    free(h_A);
    free(h_B);
    free(h_C);
    CHECK(cudaFree(d_A));
    CHECK(cudaFree(d_B));
    CHECK(cudaFree(d_C));

    return 0;
}

src/starter_kernel.cu

一个最基础的例子,展示了核函数启动语法以及线程是如何在网格(grid)和块(block)的层次结构中组织的。它简单地打印出线程和块的索引。

C++

arduino 复制代码
#include <iostream>
#include "utils.cuh"

// 打印线程和块ID的核函数
__global__ void starterKernel() {
    int tid = threadIdx.x;
    int bid = blockIdx.x;
    printf("来自块 %d 的线程 %d,你好\n", bid, tid);
}

int main() {
    // 定义一个二维的块网格,每个块包含二维的线程网格
    dim3 gridDim(2);  // 2 个块
    dim3 blockDim(4); // 每个块 4 个线程

    // 用定义的维度启动核函数
    starterKernel<<<gridDim, blockDim>>>();

    // 同步以确保所有核函数打印完成后程序才退出
    CHECK(cudaDeviceSynchronize());

    return 0;
}

编译程序

直接在根目录下运行make编译

bash 复制代码
$ make
nvcc -g -G -std=c++17 -o bin/basic_vector_add src/basic_vector_add.cu -I./inc
编译 src/basic_vector_add.cu 为 bin/basic_vector_add
nvcc -g -G -std=c++17 -o bin/starter_kernel src/starter_kernel.cu -I./inc
编译 src/starter_kernel.cu 为 bin/starter_kernel
所有 CUDA 程序编译成功。

运行

bash 复制代码
./bin/basic_vector_add 
最大误差: 0
验证成功!

./bin/starter_kernel 
来自块 1 的线程 0,你好
来自块 1 的线程 1,你好
来自块 1 的线程 2,你好
来自块 1 的线程 3,你好
来自块 0 的线程 0,你好
来自块 0 的线程 1,你好
来自块 0 的线程 2,你好
来自块 0 的线程 3,你好
相关推荐
若天明34 分钟前
深度学习-计算机视觉-微调 Fine-tune
人工智能·python·深度学习·机器学习·计算机视觉·ai·cnn
爱喝奶茶的企鹅35 分钟前
Ethan独立开发新品速递 | 2025-08-19
人工智能
J_bean44 分钟前
Spring AI Alibaba 项目接入兼容 OpenAI API 的大模型
人工智能·spring·大模型·openai·spring ai·ai alibaba
SelectDB1 小时前
Apache Doris 4.0 AI 能力揭秘(一):AI 函数之 LLM 函数介绍
数据库·人工智能·数据分析
倔强青铜三1 小时前
苦练Python第39天:海象操作符 := 的入门、实战与避坑指南
人工智能·python·面试
飞哥数智坊1 小时前
GPT-5 初战:我用 Windsurf,体验了“结对编程”式的AI开发
人工智能·windsurf
数据超市2 小时前
香港数据合集:建筑物、手机基站、POI、职住数据、用地类型
大数据·人工智能·智能手机·数据挖掘·数据分析
视觉语言导航2 小时前
哈工深无人机目标导航新基准!UAV-ON:开放世界空中智能体目标导向导航基准测试
人工智能·深度学习·无人机·具身智能
yzx9910132 小时前
AI心理助手开发文档
人工智能·深度学习·机器学习
图灵学术计算机论文辅导2 小时前
论文推荐|迁移学习+多模态特征融合
论文阅读·人工智能·深度学习·计算机网络·算法·计算机视觉·目标跟踪