并行编程与 GPU 编程核心笔记 + C++示例代码
一、核心笔记
1. 并行编程基础
-
定义:将大型问题分解为多个独立子任务,通过多个处理器核心同时执行+协同处理同一问题,实现效率提升。核心是“分解任务+同步协作”,而非单纯的多任务执行。
-
核心价值:
-
提速:多核心并行处理,缩短整体耗时;
-
资源高效利用:避免处理器核心闲置;
-
处理大数据集:适配数据科学、视频处理等领域的大规模数据;
-
支撑先进技术:如气象预测、AI 仿真、游戏物理引擎等。
-
2. 并行编程的三种类型
| 类型 | 核心逻辑 | 示例场景 |
|---|---|---|
数据并行 |
同一操作作用于不同数据片段 |
图像像素处理、矩阵运算、胡萝卜削皮 |
任务并行 |
多个独立子任务并行执行(无依赖) |
晚餐准备(前菜、主菜、甜点分工) |
流水线并行 |
任务分步骤执行,步骤间并行(依赖前序结果) |
水果加工(清洗 → 筛选 → 分级 → 包装) |
3. GPU 架构核心
-
核心组件:
-
流式多处理器(SM):GPU 的核心单元,包含 1 个控制单元 + 多个 CUDA 核心,支持 SIMD(单指令多数据);
-
内存层次(从慢到快):全局内存(大容量、低带宽)→ L2 缓存 → L1/共享内存(SM 内共享,中带宽)→ 寄存器(每个 CUDA 核心私有,高带宽);
-
调度器:管理线程执行,分批处理超量数据(因核心数有限)。
-
-
关键特性:通过 SM 的单控制单元驱动多 CUDA 核心,实现大规模数据并行,适合无复杂分支的规整计算。
二、C++示例代码
示例 1:CPU 多线程数据并行(OpenMP)
核心思路
基于数据并行思想,使用 OpenMP 库将数组求和任务分解到多个 CPU 核心,对应文档中“数据并行”的核心逻辑(同一操作作用于不同数据片段)。
#include <iostream>
#include <vector>
#include <omp.h> // OpenMP库,支持CPU多线程并行
#include <chrono>
using namespace std;
using namespace chrono;
// 数组求和:串行版本
long long serial_sum(const vector<int>& arr) {
long long sum = 0;
for (size_t i = 0; i < arr.size(); ++i) {
sum += arr[i];
}
return sum;
}
// 数组求和:并行版本(数据并行)
long long parallel_sum(const vector<int>& arr) {
long long sum = 0;
// OpenMP指令:并行化for循环,自动分解数据到多个核心
#pragma omp parallel for reduction(+:sum) // reduction确保sum线程安全累加
for (size_t i = 0; i < arr.size(); ++i) {
sum += arr[i];
}
return sum;
}
int main() {
// 生成1000万个随机数(大数据集,体现并行优势)
const size_t arr_size = 10'000'000;
vector<int> arr(arr_size);
for (size_t i = 0; i < arr_size; ++i) {
arr[i] = rand() % 100; // 0-99的随机数
}
// 测试串行版本
auto start = high_resolution_clock::now();
long long serial_res = serial_sum(arr);
auto serial_time = duration_cast<milliseconds>(high_resolution_clock::now() - start).count();
// 测试并行版本
start = high_resolution_clock::now();
long long parallel_res = parallel_sum(arr);
auto parallel_time = duration_cast<milliseconds>(high_resolution_clock::now() - start).count();
// 输出结果
cout << "串行求和结果:" << serial_res << ",耗时:" << serial_time << "ms" << endl;
cout << "并行求和结果:" << parallel_res << ",耗时:" << parallel_time << "ms" << endl;
cout << "并行加速比:" << (double)serial_time / parallel_time << "x" << endl;
return 0;
}
示例 2:CUDA GPU 矩阵乘法(体现 SIMD 与内存优化)
核心思路
基于 GPU 的SIMD 架构和内存层次优化,使用共享内存减少全局内存访问(文档强调“全局内存慢,需优化访问模式”),实现矩阵乘法的并行加速。
#include <iostream>
#include <vector>
#include <chrono>
#include <cuda_runtime.h>
using namespace std;
using namespace chrono;
// 矩阵大小(需为块大小的整数倍,简化示例)
const int MATRIX_SIZE = 2048;
const int BLOCK_SIZE = 32; // CUDA块大小,适配SM的并行能力
// CPU矩阵乘法(串行,用于对比)
void cpu_matrix_mult(const vector<float>& A, const vector<float>& B, vector<float>& C) {
for (int i = 0; i < MATRIX_SIZE; ++i) {
for (int j = 0; j < MATRIX_SIZE; ++j) {
float sum = 0.0f;
for (int k = 0; k < MATRIX_SIZE; ++k) {
sum += A[i * MATRIX_SIZE + k] * B[k * MATRIX_SIZE + j];
}
C[i * MATRIX_SIZE + j] = sum;
}
}
}
// GPU核函数:矩阵乘法(使用共享内存优化)
__global__ void gpu_matrix_mult(const float* d_A, const float* d_B, float* d_C) {
// 共享内存:存储A的子块和B的子块(快于全局内存)
__shared__ float shared_A[BLOCK_SIZE][BLOCK_SIZE];
__shared__ float shared_B[BLOCK_SIZE][BLOCK_SIZE];
// 当前线程在矩阵中的全局坐标
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;
// 分块加载A和B到共享内存(流水线并行思想)
for (int tile = 0; tile < MATRIX_SIZE / BLOCK_SIZE; ++tile) {
// 加载A的子块到共享内存(线程分工)
shared_A[threadIdx.y][threadIdx.x] = d_A[row * MATRIX_SIZE + tile * BLOCK_SIZE + threadIdx.x];
// 加载B的子块到共享内存
shared_B[threadIdx.y][threadIdx.x] = d_B[(tile * BLOCK_SIZE + threadIdx.y) * MATRIX_SIZE + col];
__syncthreads(); // 等待块内所有线程加载完成(同步)
// 子块内乘法累加(SIMD:同一指令作用于不同共享内存数据)
for (int k = 0; k < BLOCK_SIZE; ++k) {
sum += shared_A[threadIdx.y][k] * shared_B[k][threadIdx.x];
}
__syncthreads(); // 等待块内所有线程计算完成,避免覆盖共享内存
}
// 将结果写入全局内存
d_C[row * MATRIX_SIZE + col] = sum;
}
int main() {
// 1. 初始化CPU端矩阵(宿主内存)
vector<float> h_A(MATRIX_SIZE * MATRIX_SIZE, 1.0f); // 矩阵A全1
vector<float> h_B(MATRIX_SIZE * MATRIX_SIZE, 1.0f); // 矩阵B全1
vector<float> h_C_cpu(MATRIX_SIZE * MATRIX_SIZE, 0.0f);
vector<float> h_C_gpu(MATRIX_SIZE * MATRIX_SIZE, 0.0f);
// 2. CPU矩阵乘法(串行)
auto start = high_resolution_clock::now();
cpu_matrix_mult(h_A, h_B, h_C_cpu);
auto cpu_time = duration_cast<seconds>(high_resolution_clock::now() - start).count();
cout << "CPU矩阵乘法耗时:" << cpu_time << "s" << endl;
// 3. GPU矩阵乘法(并行)
float *d_A, *d_B, *d_C;
// 分配GPU设备内存(全局内存)
cudaMalloc((void**)&d_A, MATRIX_SIZE * MATRIX_SIZE * sizeof(float));
cudaMalloc((void**)&d_B, MATRIX_SIZE * MATRIX_SIZE * sizeof(float));
cudaMalloc((void**)&d_C, MATRIX_SIZE * MATRIX_SIZE * sizeof(float));
// 从CPU内存拷贝数据到GPU全局内存(文档:需手动管理内存传输)
cudaMemcpy(d_A, h_A.data(), MATRIX_SIZE * MATRIX_SIZE * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_B, h_B.data(), MATRIX_SIZE * MATRIX_SIZE * sizeof(float), cudaMemcpyHostToDevice);
// 定义CUDA网格和块维度
dim3 block(BLOCK_SIZE, BLOCK_SIZE); // 块内线程数:32x32=1024(适配SM能力)
dim3 grid(MATRIX_SIZE / BLOCK_SIZE, MATRIX_SIZE / BLOCK_SIZE); // 网格大小
// GPU核函数执行(并行计算)
start = high_resolution_clock::now();
gpu_matrix_mult<<<grid, block>>>(d_A, d_B, d_C);
cudaDeviceSynchronize(); // 等待GPU执行完成
auto gpu_time = duration_cast<milliseconds>(high_resolution_clock::now() - start).count();
cout << "GPU矩阵乘法耗时:" << gpu_time << "ms" << endl;
// 从GPU全局内存拷贝结果到CPU内存
cudaMemcpy(h_C_gpu.data(), d_C, MATRIX_SIZE * MATRIX_SIZE * sizeof(float), cudaMemcpyDeviceToHost);
// 4. 验证结果(简单对比前10个元素)
bool result_valid = true;
for (int i = 0; i < 10; ++i) {
if (abs(h_C_cpu[i] - h_C_gpu[i]) > 1e-5) {
result_valid = false;
break;
}
}
cout << "结果验证:" << (result_valid ? "正确" : "错误") << endl;
cout << "GPU加速比:" << (double)cpu_time * 1000 / gpu_time << "x" << endl;
// 释放GPU内存
cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);
return 0;
}
编译与运行
-
编译命令(CUDA 编译器):
nvcc gpu_matrix_mult.cu -o gpu_matrix_mult -arch=sm_75(sm_75 适配 GTX 10 系列及以上 GPU,需根据自身 GPU 架构调整) -
核心关联知识点:
-
__global__:CUDA 核函数,在 GPU 设备端执行; -
__shared__:共享内存,对应文档中“SM 内共享的高速内存”,减少全局内存访问开销; -
dim3 grid/block:CUDA 的并行组织方式,适配 GPU 的 SM 和 CUDA 核心布局; -
cudaMemcpy:CPU-GPU 内存传输,文档强调“需减少频繁传输以降低开销”; -
SIMD 特性:块内线程执行同一核函数,对共享内存中的子块数据并行计算,体现“单指令多数据”。
-