并行编程与 GPU 编程核心笔记 + C++示例代码

      +

      一、核心笔记

      1. 并行编程基础

      • 定义:将大型问题分解为多个独立子任务,通过多个处理器核心同时执行+协同处理同一问题,实现效率提升。核心是“分解任务+同步协作”,而非单纯的多任务执行。

      • 核心价值

        • 提速:多核心并行处理,缩短整体耗时;

        • 资源高效利用:避免处理器核心闲置;

        • 处理大数据集:适配数据科学、视频处理等领域的大规模数据;

        • 支撑先进技术:如气象预测、AI 仿真、游戏物理引擎等。

      2. 并行编程的三种类型

      类型 核心逻辑 示例场景

      数据并行

      同一操作作用于不同数据片段

      图像像素处理、矩阵运算、胡萝卜削皮

      任务并行

      多个独立子任务并行执行(无依赖)

      晚餐准备(前菜、主菜、甜点分工)

      流水线并行

      任务分步骤执行,步骤间并行(依赖前序结果)

      水果加工(清洗 → 筛选 → 分级 → 包装)

      3. GPU 架构核心

      • 核心组件

        • 流式多处理器(SM):GPU 的核心单元,包含 1 个控制单元 + 多个 CUDA 核心,支持 SIMD(单指令多数据);

        • 内存层次(从慢到快):全局内存(大容量、低带宽)→ L2 缓存 → L1/共享内存(SM 内共享,中带宽)→ 寄存器(每个 CUDA 核心私有,高带宽);

        • 调度器:管理线程执行,分批处理超量数据(因核心数有限)。

      • 关键特性:通过 SM 的单控制单元驱动多 CUDA 核心,实现大规模数据并行,适合无复杂分支的规整计算。

      4. CPU 与 GPU 核心差异

      维度 CPU GPU

      核心数量

      少(通常 4-64 核,支持数十线程)

      极多(数千 CUDA 核心,支持数万线程)

      控制方式

      单核心独立控制单元

      SM 单控制单元驱动多 CUDA 核心

      擅长场景

      任务切换、复杂分支、串行+并行混合任务

      数据并行、SIMD 操作、大规模规整计算

      内存访问

      无需手动管理,开销影响小

      需手动优化访问模式(避免全局内存频繁访问)

      5. GPU 编程的优缺点

      • 优点:高吞吐量、资源利用率高、处理大数据集效率提升 10-100 倍、支持 AI/仿真等先进技术;

      • 挑战:硬件参数固定(如内存容量)、CPU-GPU 内存传输开销、复杂分支代码性能下降、需手动优化内存访问。

      二、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;
      }
      编译与运行
      • 编译命令(GCC):g++ parallel_sum.cpp -o parallel_sum -fopenmp

      • 核心关联知识点:

        • #pragma omp parallel for:实现数据并行,将数组分片给多个 CPU 核心;

        • reduction(+:sum):解决多线程累加的竞争问题,对应文档中“协同处理同一问题”的要求。

      示例 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 特性:块内线程执行同一核函数,对共享内存中的子块数据并行计算,体现“单指令多数据”。

      三、关键说明

      1. 示例 1(CPU 并行)适合小规模数据并行任务,无需 GPU 硬件,依赖 OpenMP 库;

      2. 示例 2(GPU 并行)需 NVIDIA GPU 和 CUDA Toolkit,核心优化点是共享内存复用分块计算,解决了文档中提到的“全局内存访问慢”的问题;

      3. 实际开发中需根据任务类型选择并行方式:数据规整、无复杂分支 →GPU;分支多、任务独立 →CPU 任务并行;依赖链长 → 流水线并行。