当前位置: 首页 > news >正文

3.cuda执行模型

3. cuda执行模型

本章主要介绍的是gpu的架构和如何配置达到最高运算的,增加其吞吐量(gpu的并行性)。

3.1 cuda执行模型概述

3.1.1 gpu架构描述

RTX 对应的架构是 Ampere SM(流式多处理器)

组件名称功能描述
INT32 核心用于整数运算,支持并发执行(与 FP32 并行),适合逻辑、索引类任务。
FP32 核心(CUDA Cores)用于执行单精度浮点运算(如加减乘除等),Ampere 每个 SM 有 128 个 FP32 核心,吞吐量翻倍。
Tensor Cores(张量核心)第三代 Tensor Core,支持 FP16、BF16、TF32、INT8 等多种数据类型,广泛用于深度学习中的矩阵乘法(如 GEMM)。
LD/ST 单元(Load/Store Units)负责全局内存与 SM 寄存器/共享内存之间的数据传输。
共享内存 / L1 Cache(可配置)每个 SM 有高达 164 KB 的共享内存 + L1 Cache,程序可配置(如 64 KB L1 + 100 KB shared)。共享内存加快线程间通信。
寄存器文件(Register File)用于存储每个线程的局部变量,每个 SM 共有 65536 个 32-bit 寄存器。
调度器(Warp Scheduler)每个 SM 有 4 个 warp 调度器,可并发调度 4 个 warp,提升并发性和执行效率。
SFU(特殊功能单元)执行特殊数学函数,如三角函数、开根号、指数等。
纹理单元 / 数据缓存单元(Texture Units)加速图像、纹理等访问操作,也可以用于普通内存数据采样。
项目数量
CUDA Cores(FP32)128 个(每 SM)
Tensor Cores4 个(每 SM)
Warp 调度器–>线程束4 个
共享内存最多 164 KB(与 L1 Cache 可配置共享)
最大线程数(每 SM)2048
最大 Warps(每 SM)64
最大寄存器数量(32-bit)65536

关系:一个线程块只能在一个SM上被调度。一旦这个线程块在SM上被调度的话,就会保存在改SM上直至执行完成。

注:wrap 是32 个线程。这是固定的。(固定的,这是硬件执行单元的基本单位)

#include <stdio.h>
#include <cuda_runtime.h>int main() {int dev = 0;cudaDeviceProp prop;cudaGetDeviceProperties(&prop, dev);printf("Device %d: %s\n", dev, prop.name);//gpu的型号printf("  CUDA Capability: %d.%d\n", prop.major, prop.minor);//计算能力(Compute Capability)是 8.6printf("  Total Global Memory: %.2f GB\n", prop.totalGlobalMem / (1024.0 * 1024 * 1024));//显存大小(可用的全局内存),用于存储数据、模型、权重等。printf("  Shared Memory Per Block: %zu bytes\n", prop.sharedMemPerBlock);//每个线程块(block)可用的共享内存大小,共享内存是 Block 内线程共享的高速内存,类似“线程局部缓存”。printf("  Registers Per Block: %d\n", prop.regsPerBlock);//每个线程块最多可使用的寄存器数量。寄存器是线程最快的本地内存资源。printf("  Warp Size: %d\n", prop.warpSize);//一个 warp 有 32 个线程。warp 是 GPU 执行的最小调度单位。printf("  Max Threads Per Block: %d\n", prop.maxThreadsPerBlock);//一个线程块中最多可以容纳线程数量printf("  Max Threads Per Multiprocessor: %d\n", prop.maxThreadsPerMultiProcessor);//一个 SM(流式多处理器)最多可以同时调度的线程数量printf("  Multiprocessor Count: %d\n", prop.multiProcessorCount);//表示该 GPU 拥有多少个个 SM,即 82 个并行计算单元。printf("  Max Threads Dimensions (block): (%d, %d, %d)\n",prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);//线程块(block)的最大形状,表示你可以定义一个 dim3 block(1024, 1, 1) 或 dim3 block(32, 32, 1) 这样的块printf("  Max Grid Size: (%d, %d, %d)\n",prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);//网格(grid)维度的最大值,用于定义 block 的布局,通常非常大。printf("  Clock Rate: %.2f MHz\n", prop.clockRate / 1000.0);//核心频率(SM 的时钟频率),单位是 MHz,影响执行速度。printf("  Memory Clock Rate: %.2f MHz\n", prop.memoryClockRate / 1000.0);//显存的运行频率,决定显存带宽。printf("  Memory Bus Width: %d bits\n", prop.memoryBusWidth);//显存总线宽度,和 memory clock 一起决定显存带宽。printf("  L2 Cache Size: %d bytes\n", prop.l2CacheSize);//L2 缓存大小,所有 SM 共用。printf("  Max Threads per SM: %d\n", prop.maxThreadsPerMultiProcessor);// 每个 SM(Streaming Multiprocessor)中可同时活跃线程的最大数量printf("  Concurrent Kernels: %s\n", prop.concurrentKernels ? "Yes" : "No");//是否支持多个 kernel 并发执行(支持更复杂的并行调度)。printf("  Compute Mode: %d\n", prop.computeMode);//设备工作模式,常见值有:0: 默认,可被多个进程访问。1: 只允许一个进程访问(独占)。2: 只允许主机访问(禁用 GPU 计算)。printf("  ECC Enabled: %s\n", prop.ECCEnabled ? "Yes" : "No");//是否启用 ECC(错误检测纠正),开启时数据更可靠但性能略下降。return 0;
}
Device 0: GeForce RTX 3090CUDA Capability: 8.6Total Global Memory: 23.70 GBShared Memory Per Block: 49152 bytesRegisters Per Block: 65536Warp Size: 32Max Threads Per Block: 1024Max Threads Per Multiprocessor: 1536Multiprocessor Count: 82Max Threads Dimensions (block): (1024, 1024, 64)Max Grid Size: (2147483647, 65535, 65535)Clock Rate: 1695.00 MHzMemory Clock Rate: 9751.00 MHzMemory Bus Width: 384 bitsL2 Cache Size: 6291456 bytesMax Threads per SM: 1536Concurrent Kernels: YesCompute Mode: 0ECC Enabled: No

3.2 理解线程束执行的本质

Warp 是 CUDA 中执行的最小调度单元。

  • 一个 Warp 包含 32 个线程(这是固定的,NVIDIA 定义的标准)。
  • 当你在 CUDA 中启动一个线程块(block)时,线程会被自动划分成若干个 warp
  • GPU 中的 SM(流处理器)以 warp 为单位进行调度与执行

3.2.1 线程束和线程块

线程束是SM中基本的执行单位。当一个线程块的网格被启动时,网格中的线程块分布在SM中。一旦线程块被调度在SM上,线程块中的线程会被进一步划分为线程束。一个线程束由32个线程组成,在一个线程束中,所有的线程将按照单指令多线程(SIMT)方式执行。

线程块:

逻辑角度看:线程块是线程的集合,它们可以被组织为一维、二维、三维布局。

硬件角度看:线程块是一维线程束的集合。在线程块中线程被组成一维布局,每32个连续的线程组成一个线程束。

3.2.2 线程束的分化。

概念:在同一个线程束里面执行不同的指令,称作是线程束的分化。

eg:

if(cond) {

} else {

}

为了获取最佳性能,应该避免同一个线程束中有不同的执行路径。

#include<cuda_runtime.h>
#include<stdio.h>
#include<sys/time.h>
double cpuSecond() {struct timeval tp;gettimeofday(&tp,NULL);return ((double)tp.tv_sec + (double)tp.tv_usec*1e-6);}
//线程束分化
// warp 中 32 个线程中,有一半是奇数 tid,一半是偶数 tid;// 所以同一个 warp 中一半线程执行 a=100,另一半执行 b=200;// 会产生 warp 分歧,GPU 内部必须分别串行执行两个路径。
__global__ void mathKernel1(float* c) {int tid = blockIdx.x * blockDim.x + threadIdx.x;float a,b;a = b = 0.0f;if(tid % 2 == 0) {a = 100.0f;} else {b = 200.0f;}c[tid] = a + b;
}
//线程束方法
// (tid / warpSize) 代表当前线程属于哪一个 warp;// 所以同一个 warp 里的线程 (tid / warpSize) % 2 是相同的;// 所以 同一个 warp 全部执行 a=100 或全部执行 b=200;// 无 warp 分歧,效率高,GPU 可最大化并行执行。
__global__ void mathKernel2(float* c) {int tid = blockIdx.x * blockDim.x + threadIdx.x;float a,b;a = b = 0.0f;if((tid / warpSize) % 2 == 0) {a = 100.0f;} else {b = 200.0f;}c[tid] = a + b;
}
__global__ void warmup_kernel() {// 空操作,或简单加法
}
int main(int argc,char** argv) {int dev = 0;cudaDeviceProp deviceprop;int size = 64;int blocksize = 64;cudaGetDeviceProperties(&deviceprop , dev);if(argc > 1) {blocksize = atoi(argv[1]);}if(argc > 2) {size = atoi(argv[2]);}dim3 block(blocksize,1);dim3 grid((size + block.x -1)/block.x,1);printf("block.x %d \t grid.x : %d\n",block.x,grid.x);float* d_c;size_t nBytes = size * sizeof(float);cudaMalloc( (float**)&d_c,nBytes);double isStart,isEnd;cudaDeviceSynchronize();isStart = cpuSecond();warmup_kernel<<<grid,block>>> ();cudaDeviceSynchronize();isEnd = cpuSecond();printf("warmup <<<%4d,%4d>>> cost %f sec\n",grid.x,block.x,isEnd - isStart);cudaDeviceSynchronize();isStart = cpuSecond();mathKernel1<<<grid,block>>> (d_c);cudaDeviceSynchronize();isEnd = cpuSecond();printf("mathKernel1 <<<%4d,%4d>>> cost %f sec\n",grid.x,block.x,isEnd - isStart);isStart = cpuSecond();mathKernel2<<<grid,block>>> (d_c);cudaDeviceSynchronize();isEnd = cpuSecond();printf("mathKernel2 <<<%4d,%4d>>> cost %f sec\n",grid.x,block.x,isEnd - isStart);cudaFree(d_c);cudaDeviceReset();return 0;
//     block.x 64       grid.x : 1
// warmup <<<   1,  64>>> cost 0.000145 sec
// mathKernel1 <<<   1,  64>>> cost 0.000171 sec
// mathKernel2 <<<   1,  64>>> cost 0.000009 sec
}

3.2.3 资源分配

线程束的上下文主要由以下资源组成:

程序计数器:程序计数器是一个寄存器,用于记录当前要执行的指令地址;在 CUDA 中,一个 warp 共用一个 PC,因为一个 warp 内的线程是SIMD 执行(同一指令)

寄存器:寄存器是每个线程私有的高速内存空间,用于保存局部变量、中间计算结果等;每个线程都有自己独立的一组寄存器,warp 中不同线程不共享寄存器。

共享内存:是线程块(block)内所有线程共享的内存区域;用于线程间通信和数据共享;在硬件上位于 SM(Streaming Multiprocessor)内部,访问速度比全局内存快得多。

当计算资源(如寄存器和共享内存) 已分配给线程块时。线程块被称为活跃的快。它所包含的线程束被称为活跃的线程束。活跃的线程束可以进一步被分为以下三种类型:

选定线程束

阻塞的线程束

符合条件的线程束

如何判断线程束是否满足执行条件?

  1. 32个CUDA核心均可用于执行。
  2. 当前指令中所有的参数都已经就绪。

3.2.4 延迟隐藏

指令延迟可分为以下两种类型:

  1. 算数指令延迟:一个算数操作从开始到它产生输出之间的时间。10~20个周期
  2. 内存指令延迟:发送出的加载或内存操作和数据到达目的地之间的时间。400~800个周期

利特尔法则:

​ 所需线程束的数量 = 延迟 * 吞吐量

假设你要保持每个周期执行6个线程束的吞吐量,延迟是5个周期的话,则至少需要30个线程束

带宽与吞吐量的概念:

带宽:理论峰值

吞吐量:已达到的值。

如何提高并行:

  1. 指令级并行:一个线程中有许多独立的指令。在一个线程内部,尽量安排多个相互独立、不依赖的指令,让 GPU 在执行时可以同时处理它们。
float a = x + y;      // 指令 1
float b = m * n;      // 指令 2(和上面的无依赖)
float c = a + b;      // 指令 3(依赖 a 和 b)
  1. 线程级并行:很多并发地符号条件的线程。让尽可能多的线程同时运行,通过 CUDA 的调度器隐藏延迟、提高 GPU 利用率。
__global__ void addKernel(float* a, float* b, float* c) {int i = threadIdx.x + blockDim.x * blockIdx.x;c[i] = a[i] + b[i];
}

查看gpu的内存吞吐量:

nvidia-smi -q | grep -A 5 “Max Clocks”

RTX 3090 有 82 个 SM,所以至少需要:


32 warps × 82 SM = 2624 个线程束(活跃 warp)

换算线程数:


2624 warps × 32 线程 = 83,968 个线程

3.2.5 占用率

占用率 = 活跃的线程束的数量/ 最大的线程束的数量

#include<stdio.h>
#include<cuda_runtime.h>
int main() {int dev = 0;cudaDeviceProp iprop;cudaGetDeviceProperties(&iprop ,dev );printf("number of multiprocessors %d\n",iprop.multiProcessorCount);//含义:GPU 上的多处理器(SM,Streaming Multiprocessor)数量。//作用:每个 SM 可以并行处理多个线程块,是 GPU 并行处理能力的重要指标。printf("const memeory :%4.2f kb\n",iprop.totalConstMem / 1024.0);//含义:设备上常量内存(constant memory)的总大小(以字节为单位)。//作用:常量内存是一个只读的内存区域,适用于线程之间共享的常量数据,访问速度比全局内存快。printf("shared memory per block %4.2fkb\n",iprop.sharedMemPerBlock / 1024.0);printf("total of registers available per bolck %d\n",iprop.regsPerBlock);//含义:每个线程块可用的寄存器数量。作用:每个线程在执行时会使用寄存器,寄存器越多,线程执行速度越快;但数量有限,会影响线程并发度。printf("Warp size :%d\n",iprop.warpSize);//一个 warp 中包含的线程数量。printf("max number of per block :%d\n",iprop.maxThreadsPerBlock);//:每个线程块可容纳的最大线程数。printf("max number of threads of per multiprocessor :%d\n",iprop.maxThreadsPerMultiProcessor);//每个 SM(多处理器)可同时执行的最大线程数量。printf("max number of warps of per multiprocessor : %d\n",iprop.maxThreadsPerMultiProcessor / 32);//每个多处理器上最大可支持的 warp 数量。return 0;
}

网格和线程块大小准则:

   	1. 保证每个块中的线程数量是线程束大小(32)的倍数。2. 避免块太小,每个块至少要有128 或者256个线程。3. 根据内核资源的需求调整块的大小。4. 块的数量要远远大于SM的数量,从而在设备中可以显示足够多的并行。
#include <stdio.h>
#include <cuda_runtime.h>// 核函数
__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() {const int N = 1 << 20; // 1Msize_t size = N * sizeof(float);float *h_A = (float*)malloc(size);float *h_B = (float*)malloc(size);float *h_C = (float*)malloc(size);// 初始化for (int i = 0; i < N; ++i) {h_A[i] = i;h_B[i] = N - i;}// 设备内存float *d_A, *d_B, *d_C;cudaMalloc(&d_A, size);cudaMalloc(&d_B, size);cudaMalloc(&d_C, size);cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);// 使用 dim3 设置 block 和 griddim3 threadsPerBlock(32 * 8);  // 256 threads, 是 warp 大小的倍数dim3 blocksPerGrid((N + threadsPerBlock.x - 1) / threadsPerBlock.x); // 计算需要的 blocks 数printf("Using dim3: %d blocks × %d threads\n", blocksPerGrid.x, threadsPerBlock.x);// 调用核函数vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);// 验证for (int i = 0; i < 10; i++) {printf("C[%d] = %f\n", i, h_C[i]);}// 清理cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);free(h_A);free(h_B);free(h_C);return 0;
}

3.2.6 同步

栅栏同步是一个原语,它在诸多编程语言中都很常见。在cuda中,同步可以在两个级别执行:

系统级:等待主机和设备完成所有工作。cudaDeviceSynchronize(void)

块级:在设备执行过程中等待一个线程块中所有线程达到同一点。_syncthreads(void) 这个函数启动时,在同一个线程块的每个线程都必须等待所有线程执行完成后才可返回。

3.3 并行性的表现

#include<stdio.h>
#include<cuda_runtime.h>
#include<sys/time.h>
#define CHECK(call)                                                      \do {                                                                 \const cudaError_t error = call;                                  \if (error != cudaSuccess) {                                      \printf("CUDA Error: %s:%d, ", __FILE__, __LINE__);           \printf("code: %d, reason: %s\n", error, cudaGetErrorString(error)); \exit(1);                                                     \}                                                                \} while (0)
void init_data(float *p,const int size) {for(int i = 0;i < size; ++i) {p[i] = i;}
}
void print_check(float *c ,const int nx, const int ny) {float* ic = c;printf("nx : %d \t ny :%d \n",nx,ny);for(int i = 0 ;i < ny ; ++i) {for(int y = 0; y < nx; ++y) {printf("%f\t",ic[y]);}ic += nx;printf("\n");}printf("\n");
}
__global__ void printThreadIndex(float *a,const int nx, const int ny) {int ix = blockIdx.x * blockDim.x + threadIdx.x;int iy = blockIdx.y * blockDim.y + threadIdx.y;unsigned int idx = iy * nx + ix;printf("thread_id :(%d,%d) block_id:(%d,%d),coordinate:(%d,%d) global_index :%2d,ival :%2f\n",threadIdx.x,threadIdx.y,blockIdx.x,blockIdx.y,ix,iy,idx,a[idx]);
}
void SumOnHost(float* a, float* b,float*c ,const int nx, const int ny) {float* ia = a;float* ib = b;float* ic = c;for(int i = 0; i < ny; ++i) {for(int j = 0; j < nx; ++j) {ic[j] = ia[j] + ib[j]; }ic += nx;ib += nx;ia += nx;}
}
__global__ void SumOnDevice(float* a, float* b,float* c,const int nx,const int ny) {unsigned int ix = blockIdx.x * blockDim.x + threadIdx.x;unsigned int iy = blockIdx.y * blockDim.y + threadIdx.y;int idx = iy * nx + ix;if(ix < nx && iy < ny) {c[idx] = a[idx] + b[idx];}
}
void checkResult(float* hostRef, float* gpuRef,const int n) {double ep = 1.0E-8;bool match = 1;for(int i = 0; i < n; ++i) {if(abs(hostRef[i] - gpuRef[i]) > ep) {match = 0;printf("array not match\n");break;}}if(match) {printf("array match\n");}
}
int main(int argc,char** argv) {int dimx = 16,dimy = 16;if(argc > 1) {dimx = atoi(argv[1]);}if(argc > 2) {dimy = atoi(argv[1]);}int dev = 0;cudaDeviceProp deviceProp;CHECK(cudaGetDeviceProperties( &deviceProp,dev ));printf("using device :%d\t name :%s \n",dev,deviceProp.name);CHECK(cudaSetDevice(dev));int nx = 1 << 14;int ny = 1 << 14;int nxy = nx * ny;int nBytes = nxy * sizeof(float);float *h_a,*h_b,*hostRef,*gpuRef;h_a = (float*) malloc(nBytes);h_b = (float*) malloc(nBytes);hostRef = (float*) malloc(nBytes);gpuRef = (float*) malloc(nBytes);memset(h_a,0,nBytes);init_data(h_a,nxy);init_data(h_b,nxy);memset(hostRef,0,nBytes);double start = clock();SumOnHost(h_a,h_b,hostRef,nx,ny);double end = clock();double cpu_time_used = (double)(end - start) / CLOCKS_PER_SEC;printf("cpu cost : %f sec\n",cpu_time_used);// print_check(h_a,nx,ny);float* d_a,*d_b,*d_c;cudaMalloc( (void**)&d_a,nBytes);cudaMemcpy( d_a, h_a,nBytes , cudaMemcpyHostToDevice);cudaMalloc( (void**)&d_b,nBytes);cudaMemcpy( d_b, h_b,nBytes , cudaMemcpyHostToDevice);cudaMalloc( (void**)&d_c,nBytes);dim3 block(dimx,dimy);dim3 grid((nx + block.x - 1)/block.x, (ny + block.y -1)/ block.y);start = clock();printf("grid:<%d,%d>,block:<%d,%d>\n",grid.x,grid.y,block.x,block.y);//(32,32) cost:0.88 (32,16) cost:1.89s (16,32):0.8 (16,16) cost:1.34sSumOnDevice<<<grid,block>>>(d_a,d_b,d_c,nx,ny);cudaDeviceSynchronize();CHECK(cudaGetLastError());cudaMemcpy(gpuRef,d_c ,nBytes , cudaMemcpyDeviceToHost);end = clock();double gpu_time_used = (double)(end - start) / CLOCKS_PER_SEC;printf("gpu cost : %f sec\n",gpu_time_used);checkResult(hostRef,gpuRef,nxy);cudaFree( d_a);cudaFree( d_b);cudaFree( d_c);free(h_a);free(h_b);free(hostRef);free(gpuRef);cudaDeviceReset();return 0;}

3.3.1 查看线程束的活跃度

ncu --metrics sm__warps_active.avg.per_cycle_elapsed ./your_cuda_program

  • sm__warps_active.avg.per_cycle_elapsed:表示在 kernel 执行期间,每个周期平均有多少个活跃 warp(执行状态)。

解读:数值越高,SM 的并行资源利用越充分。比如一个 SM 理论上可以同时运行 64 个 warp,如果这个值是 16,就只有 25% 利用率。

如果太低,优化策略?

优化策略说明
✅ 避免线程分歧改写条件逻辑,让一个 warp 中线程尽量走同一条执行路径
✅ 增大线程块(如 dim3(256)通常建议 block 内线程数为 128–1024,最好是 32 的倍数
✅ 使用 cudaOccupancyMaxPotentialBlockSize自动帮你算出最优 block size
✅ 减少寄存器/共享内存占用使用 __launch_bounds__ 限制线程使用资源,换取更高占用率
✅ 多并发 kernel(高级)用多个流(stream)提高 GPU 利用率(适合小 kernel)
// #include <stdio.h>
// #include <cuda_runtime.h>
// cudaError_t cudaOccupancyMaxPotentialBlockSize(
//     int* minGridSize,       // 输出:推荐的最小 grid size
//     int* blockSize,         // 输出:推荐的 block size(线程数)
//     const void* func,       // kernel 函数指针
//     size_t dynamicSMemSize = 0, // 每个 block 动态共享内存大小(单位:字节)
//     int blockSizeLimit = 0       // 限制最大 block size(0 表示不限)
// );
// // 简单的 CUDA 核函数:每个元素乘以 2
// __global__ void myKernel(float* data) {
//     int idx = threadIdx.x + blockIdx.x * blockDim.x;
//     if (idx < 100000) {  // 防止越界
//         data[idx] *= 2.0f;
//     }
// }// int main() {
//     const int numElements = 100000;
//     size_t size = numElements * sizeof(float);//     // 1. 分配 host 和 device 内存
//     float* h_data = (float*)malloc(size);
//     float* d_data;
//     cudaMalloc((void**)&d_data, size);//     // 2. 初始化 host 数据
//     for (int i = 0; i < numElements; ++i) {
//         h_data[i] = 1.0f;
//     }//     // 3. 拷贝数据到 device
//     cudaMemcpy(d_data, h_data, size, cudaMemcpyHostToDevice);//     // 4. 使用 cudaOccupancyMaxPotentialBlockSize 获取最佳 blockSize
//     int minGridSize, blockSize;
//     cudaOccupancyMaxPotentialBlockSize(
//         &minGridSize,
//         &blockSize,
//         myKernel,
//         0  // 动态共享内存
//     );//     int gridSize = (numElements + blockSize - 1) / blockSize;
//     printf("Recommended block size: %d\n", blockSize);
//     printf("Calculated grid size:   %d\n", gridSize);//     // 5. 启动 kernel
//     myKernel<<<gridSize, blockSize>>>(d_data);
//     cudaDeviceSynchronize();//     // 6. 拷贝结果回 host
//     cudaMemcpy(h_data, d_data, size, cudaMemcpyDeviceToHost);//     // 7. 打印部分结果验证
//     printf("Result[0] = %f\n", h_data[0]);  // 应该是 2.0
//     printf("Result[99999] = %f\n", h_data[99999]);  // 也是 2.0//     // 8. 释放资源
//     free(h_data);
//     cudaFree(d_data);//     return 0;
// }
#include <stdio.h>
#include <cuda_runtime.h>__global__ void myKernel2D(float* data, int width, int height) {int x = threadIdx.x + blockIdx.x * blockDim.x;int y = threadIdx.y + blockIdx.y * blockDim.y;int idx = y * width + x;if (x < width && y < height) {data[idx] *= 2.0f;}
}int main() {int width = 512;int height = 512;int numElements = width * height;size_t size = numElements * sizeof(float);float* h_data = (float*)malloc(size);float* d_data;cudaMalloc(&d_data, size);for (int i = 0; i < numElements; ++i) h_data[i] = 1.0f;cudaMemcpy(d_data, h_data, size, cudaMemcpyHostToDevice);// 获取推荐 blockSizeint minGridSize, blockSize;cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, myKernel2D, 0);printf("Recommended blockSize = %d\n", blockSize);//只能算出dimx* dimy的值,具体得你自己去实验// 把 blockSize 拆成二维,如 (16, 16)dim3 blockDim(16, blockSize / 16);  // 例如 blockSize=256 -> (16, 16)dim3 gridDim((width + blockDim.x - 1) / blockDim.x,(height + blockDim.y - 1) / blockDim.y);myKernel2D<<<gridDim, blockDim>>>(d_data, width, height);cudaDeviceSynchronize();cudaMemcpy(h_data, d_data, size, cudaMemcpyDeviceToHost);printf("Result[0] = %f\n", h_data[0]);free(h_data);cudaFree(d_data);return 0;
}

3.3.2 检查内存操作的效率

ncu --metrics dram__throughput.avg.pct_of_peak_sustained_active ./your_cuda_program

  • dram__throughput.avg.pct_of_peak_sustained_active:表示 DRAM 实际读取带宽相对于设备峰值带宽的使用百分比。

解读:越高越好,低于 50% 通常说明你可能存在访存模式不合并、不连续、缓存不命中等问题。

优化策略效果
✅ 确保全局内存访问合并让线程顺序访问相邻地址,确保内存访问 coalesced
✅ 使用共享内存(__shared__把全局读的数据加载到共享内存,线程间复用
✅ 数据对齐(align to 32/64 bytes)避免 misaligned 访问
✅ 避免 bank conflict共享内存访问时,避免多个线程访问同一个 memory bank
✅ 压缩结构体(__align__, __pack__节省不必要的 padding 空间

3.4 避免分支分化

3.4.1 并行规约问题

假设:要对一个有N个元素的整数数组求和:

int sum =0;

for(int i = 0 ;i < N; ++i) {

​ sum += array[i];

}

如何加速呢?

  1. 将输入向量划分到更小的数据块中。
  2. 用一个线程计算出一个数据块的部分和。
  3. 对每个数据块的部分和再求和得出最终结果。

根据每次迭代后输出元素就地存储的位置,成对的并行求和实现可以被进一步分成以下两种类型:

  1. 相邻配对:元素与它们直接相邻的元素配对
  2. 交错配对:根据给定的跨度配对元素

int recursiveReduce(float* data,const int size) {

​ if(size == 1) return data[0];

​ const int stride = size / 2;

​ for(int i = 0; i < stride; ++i) {

	data[i] += data[i + stride];

​ }

​ return recursiveReduce(data,stride);

}

如果可以并行的话,这段代码是很快的,比如我把其分为256个block的话,我只需要再计算完成后对这256的block 求和即可,速度还是特别快的。

规约问题:再向量中满足交换律与结合律的运算,被称为规约问题。

3.4.2 并行规约中的分化

有两个全局数组,一个存储的是整个数组,进行规约,另一个存储的是每个线程块的部分和。每个线程块再数组的一部分上执行操作。__syncthreads 语句可以保证,线程块中的任一线程在进入下一次迭代之前,在当前迭代里的所有部分和都被保存在了全局内存中。进入下一次迭代的所有线程都使用的是上一步所产生的数值。在最后一次循环后,整个线程块的和被保存在了全局内存中。

#include <stdio.h>
#include <cuda_runtime.h>
#include <sys/time.h>double cpuSecond() {struct timeval tp;gettimeofday(&tp, NULL);return ((double)tp.tv_sec + (double)tp.tv_usec * 1e-6);
}// 设备端归约 kernel(邻居合并法)
__global__ void reduceNeighbored(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x;int idx = blockIdx.x * blockDim.x + tid;// 边界检查if (idx >= size) return;// 归约:邻居合并法for (int stride = 1; stride < blockDim.x; stride *= 2) {if (tid % (2 * stride) == 0 && (tid + stride) < blockDim.x) {idata[tid] += idata[tid + stride];}__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}// CPU 递归归约
int recursiveReduce(int* data, const int size) {if (size == 1) return data[0];int stride = size / 2;for (int i = 0; i < stride; ++i) {data[i] += data[i + stride];}return recursiveReduce(data, stride);
}int main(int argc, char** argv) {int dev = 0;cudaDeviceProp iProp;cudaGetDeviceProperties(&iProp, dev);cudaSetDevice(dev);int size = 1 << 24;  // 16777216printf("array size: %d\n", size);int blocksize = 512;if (argc > 1) {blocksize = atoi(argv[1]);}dim3 block(blocksize);dim3 grid((size + block.x - 1) / block.x);size_t nBytes = sizeof(int) * size;int* h_idata = (int*)malloc(nBytes);int* h_odata = (int*)malloc(grid.x * sizeof(int));  // 只需 grid.x 大小int* tmp = (int*)malloc(nBytes);for (int i = 0; i < size; ++i) {h_idata[i] = (int)(rand() & 0xFF);}memcpy(tmp, h_idata, nBytes);// CPU 归约double start = cpuSecond();int cpu_sum = recursiveReduce(tmp, size);double end = cpuSecond();printf("CPU sum: %d\t Time: %.4f s\n", cpu_sum, (end - start));// GPU 归约int* d_idata = NULL;int* d_odata = NULL;cudaMalloc((void**)&d_idata, nBytes);cudaMalloc((void**)&d_odata, grid.x * sizeof(int));cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();start = cpuSecond();reduceNeighbored<<<grid, block>>>(d_idata, d_odata, size);cudaDeviceSynchronize();  // 保证 kernel 执行完成end = cpuSecond();//为啥只取gird.x 的数据呢?因为 kernel 每个 block 只算出自己 block 内的数据总和,而你要获得整个数组的总和,就必须对所有 grid.x 个 block 的部分和再进行一次合并。cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);int gpu_sum = 0;for (int i = 0; i < grid.x; ++i) {gpu_sum += h_odata[i];}printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));// 校验结果bool result = (gpu_sum == cpu_sum);printf(result ? "Test PASSED\n" : "Test FAILED\n");free(h_idata);free(h_odata);free(tmp);cudaFree(d_idata);cudaFree(d_odata);cudaDeviceReset();return 0;
}
array size: 16777216
CPU sum: 2139353471      Time: 0.0407 s
GPU sum: 2139353471      Time: 0.0005 s
Test PASSED

3.4.3 改善并行规约中的分化

if (tid % (2 * stride) == 0 && (tid + stride) < blockDim.x) {

idata[tid] += idata[tid + stride];

}

这个代码:tid % (2 * stride) 注定了只有偶数的线程号才可以被调用,也就是说,理想情况下,只有一半的线程参与计算,这在程序层面来说是不合适的。如何改善?

#include <stdio.h>
#include <cuda_runtime.h>
#include <sys/time.h>double cpuSecond() {struct timeval tp;gettimeofday(&tp, NULL);return ((double)tp.tv_sec + (double)tp.tv_usec * 1e-6);
}// 设备端归约 kernel(邻居合并法)
__global__ void reduceNeighbored(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x;int idx = blockIdx.x * blockDim.x + tid;// 边界检查if (idx >= size) return;// 归约:邻居合并法for (int stride = 1; stride < blockDim.x; stride *= 2) {if (tid % (2 * stride) == 0 && (tid + stride) < blockDim.x) {idata[tid] += idata[tid + stride];}__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}
//减少并行规约了,但是内存不连续了,所以慢点,但也比之前的块
__global__ void reduceNeighboredLess(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x;int idx = blockIdx.x * blockDim.x + tid;// 边界检查if (idx >= size) return;// 归约:邻居合并法for (int stride = 1; stride < blockDim.x; stride *= 2) {int index = 2 * stride * tid;if(index < blockDim.x) {idata[index] += idata[index + stride]; }__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}// CPU 递归归约
int recursiveReduce(int* data, const int size) {if (size == 1) return data[0];int stride = size / 2;for (int i = 0; i < stride; ++i) {data[i] += data[i + stride];}return recursiveReduce(data, stride);
}int main(int argc, char** argv) {int dev = 0;cudaDeviceProp iProp;cudaGetDeviceProperties(&iProp, dev);cudaSetDevice(dev);int size = 1 << 24;  // 16777216printf("array size: %d\n", size);int blocksize = 512;if (argc > 1) {blocksize = atoi(argv[1]);}dim3 block(blocksize);dim3 grid((size + block.x - 1) / block.x);size_t nBytes = sizeof(int) * size;int* h_idata = (int*)malloc(nBytes);int* h_odata = (int*)malloc(grid.x * sizeof(int));  // 只需 grid.x 大小int* tmp = (int*)malloc(nBytes);for (int i = 0; i < size; ++i) {h_idata[i] = (int)(rand() & 0xFF);}memcpy(tmp, h_idata, nBytes);// CPU 归约double start = cpuSecond();int cpu_sum = recursiveReduce(tmp, size);double end = cpuSecond();printf("CPU sum: %d\t Time: %.4f s\n", cpu_sum, (end - start));// GPU 归约int* d_idata = NULL;int* d_odata = NULL;cudaMalloc((void**)&d_idata, nBytes);cudaMalloc((void**)&d_odata, grid.x * sizeof(int));cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();// start = cpuSecond();// reduceNeighbored<<<grid, block>>>(d_idata, d_odata, size);// cudaDeviceSynchronize();  // 保证 kernel 执行完成// end = cpuSecond();// cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);int gpu_sum = 0;// for (int i = 0; i < grid.x; ++i) {//     gpu_sum += h_odata[i];// }// printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));start = cpuSecond();reduceNeighboredLess<<<grid, block>>>(d_idata, d_odata, size);cudaDeviceSynchronize();  // 保证 kernel 执行完成end = cpuSecond();cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);gpu_sum = 0;for (int i = 0; i < grid.x; ++i) {gpu_sum += h_odata[i];}printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));// 校验结果bool result = (gpu_sum == cpu_sum);printf(result ? "Test PASSED\n" : "Test FAILED\n");free(h_idata);free(h_odata);free(tmp);cudaFree(d_idata);cudaFree(d_odata);cudaDeviceReset();return 0;
}
上一个gpu是0.0005s
下一个方法是0.0003s

3.4.4 交错配对的规约

#include <stdio.h>
#include <cuda_runtime.h>
#include <sys/time.h>double cpuSecond() {struct timeval tp;gettimeofday(&tp, NULL);return ((double)tp.tv_sec + (double)tp.tv_usec * 1e-6);
}// 设备端归约 kernel(邻居合并法)
__global__ void reduceNeighbored(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x;int idx = blockIdx.x * blockDim.x + tid;// 边界检查if (idx >= size) return;// 归约:邻居合并法for (int stride = 1; stride < blockDim.x; stride *= 2) {if (tid % (2 * stride) == 0 && (tid + stride) < blockDim.x) {idata[tid] += idata[tid + stride];}__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}
__global__ void reduceInterLevel(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x;int idx = blockIdx.x * blockDim.x + tid;// 边界检查if (idx >= size) return;// 归约:邻居合并法//第一次规约1/2 第二次 1/4依次类推,速度越来越快,耗时0.0002sfor (int stride =  blockDim.x / 2; stride > 0; stride >>= 1) {if(tid < stride) {idata[tid] += idata[tid + stride];}__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}// CPU 递归归约
int recursiveReduce(int* data, const int size) {if (size == 1) return data[0];int stride = size / 2;for (int i = 0; i < stride; ++i) {data[i] += data[i + stride];}return recursiveReduce(data, stride);
}int main(int argc, char** argv) {int dev = 0;cudaDeviceProp iProp;cudaGetDeviceProperties(&iProp, dev);cudaSetDevice(dev);int size = 1 << 24;  // 16777216printf("array size: %d\n", size);int blocksize = 512;if (argc > 1) {blocksize = atoi(argv[1]);}dim3 block(blocksize);dim3 grid((size + block.x - 1) / block.x);size_t nBytes = sizeof(int) * size;int* h_idata = (int*)malloc(nBytes);int* h_odata = (int*)malloc(grid.x * sizeof(int));  // 只需 grid.x 大小int* tmp = (int*)malloc(nBytes);for (int i = 0; i < size; ++i) {h_idata[i] = (int)(rand() & 0xFF);}memcpy(tmp, h_idata, nBytes);// CPU 归约double start = cpuSecond();int cpu_sum = recursiveReduce(tmp, size);double end = cpuSecond();printf("CPU sum: %d\t Time: %.4f s\n", cpu_sum, (end - start));// GPU 归约int* d_idata = NULL;int* d_odata = NULL;cudaMalloc((void**)&d_idata, nBytes);cudaMalloc((void**)&d_odata, grid.x * sizeof(int));cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();// start = cpuSecond();// reduceNeighbored<<<grid, block>>>(d_idata, d_odata, size);// cudaDeviceSynchronize();  // 保证 kernel 执行完成// end = cpuSecond();// cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);int gpu_sum = 0;// for (int i = 0; i < grid.x; ++i) {//     gpu_sum += h_odata[i];// }// printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));cudaMemset( d_odata, 0, grid.x * sizeof(int));start = cpuSecond();reduceInterLevel<<<grid, block>>>(d_idata, d_odata, size);cudaDeviceSynchronize();  // 保证 kernel 执行完成end = cpuSecond();cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);gpu_sum = 0;for (int i = 0; i < grid.x; ++i) {gpu_sum += h_odata[i];}printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));// 校验结果bool result = (gpu_sum == cpu_sum);printf(result ? "Test PASSED\n" : "Test FAILED\n");free(h_idata);free(h_odata);free(tmp);cudaFree(d_idata);cudaFree(d_odata);cudaDeviceReset();return 0;
}

3.5 展开循环

for(int i = 0; i < 100; ++i) {

a[i] = b[i] + c[i];

}

for(int i = 0; i < 100; i += 2) {

a[i] = b[i] + c[i];

a[i+1] = b[i + 1] + c[i+1];

}

优化目标:通过减少指令消耗和增加更多的独立指令来提高性能。因此,更多的并发操作被添加到流水线上,以产生更高的指令来提高性能。

3.5.1 展开的规约

只需要一半的线程,速度提升一倍相比之前。

#include <stdio.h>
#include <cuda_runtime.h>
#include <sys/time.h>double cpuSecond() {struct timeval tp;gettimeofday(&tp, NULL);return ((double)tp.tv_sec + (double)tp.tv_usec * 1e-6);
}//展开的规约
__global__ void reduceUnrolling2(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x * 2;int idx = blockIdx.x * blockDim.x * 2 + tid;//在这里。每个线程都添加一个来自于相邻数据块的元素if(idx + blockDim.x < size) {g_idata[idx] += g_idata[idx + blockDim.x];}__syncthreads();for (int stride = blockDim.x / 2; stride > 0 ; stride >>= 1) {if(tid < stride) {idata[tid] += idata[tid + stride];}__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}
__global__ void reduceInterLevel(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x;int idx = blockIdx.x * blockDim.x + tid;// 边界检查if (idx >= size) return;// 归约:邻居合并法//第一次规约1/2 第二次 1/4依次类推,速度越来越快,耗时0.0002sfor (int stride =  blockDim.x / 2; stride > 0; stride >>= 1) {if(tid < stride) {idata[tid] += idata[tid + stride];}__syncthreads();}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}// CPU 递归归约
int recursiveReduce(int* data, const int size) {if (size == 1) return data[0];int stride = size / 2;for (int i = 0; i < stride; ++i) {data[i] += data[i + stride];}return recursiveReduce(data, stride);
}int main(int argc, char** argv) {int dev = 0;cudaDeviceProp iProp;cudaGetDeviceProperties(&iProp, dev);cudaSetDevice(dev);int size = 1 << 24;  // 16777216printf("array size: %d\n", size);int blocksize = 512;if (argc > 1) {blocksize = atoi(argv[1]);}dim3 block(blocksize);dim3 grid((size + block.x - 1) / block.x);size_t nBytes = sizeof(int) * size;int* h_idata = (int*)malloc(nBytes);int* h_odata = (int*)malloc(grid.x * sizeof(int));  // 只需 grid.x 大小int* tmp = (int*)malloc(nBytes);for (int i = 0; i < size; ++i) {h_idata[i] = (int)(rand() & 0xFF);}memcpy(tmp, h_idata, nBytes);// CPU 归约double start = cpuSecond();int cpu_sum = recursiveReduce(tmp, size);double end = cpuSecond();printf("CPU sum: %d\t Time: %.4f s\n", cpu_sum, (end - start));// GPU 归约int* d_idata = NULL;int* d_odata = NULL;cudaMalloc((void**)&d_idata, nBytes);cudaMalloc((void**)&d_odata, grid.x * sizeof(int));cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();// start = cpuSecond();// reduceNeighbored<<<grid, block>>>(d_idata, d_odata, size);// cudaDeviceSynchronize();  // 保证 kernel 执行完成// end = cpuSecond();// cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);int gpu_sum = 0;// for (int i = 0; i < grid.x; ++i) {//     gpu_sum += h_odata[i];// }// printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));cudaMemset( d_odata, 0, grid.x * sizeof(int));start = cpuSecond();reduceUnrolling2<<<grid.x / 2 , block>>>(d_idata, d_odata, size);cudaDeviceSynchronize();  // 保证 kernel 执行完成end = cpuSecond();cudaMemcpy(h_odata, d_odata, grid.x /2 * sizeof(int), cudaMemcpyDeviceToHost);gpu_sum = 0;for (int i = 0; i < grid.x / 2; ++i) {gpu_sum += h_odata[i];}printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));// 校验结果bool result = (gpu_sum == cpu_sum);printf(result ? "Test PASSED\n" : "Test FAILED\n");free(h_idata);free(h_odata);free(tmp);cudaFree(d_idata);cudaFree(d_odata);cudaDeviceReset();return 0;
}

3.5.2 展开线程的规约

#include <stdio.h>
#include <cuda_runtime.h>
#include <sys/time.h>double cpuSecond() {struct timeval tp;gettimeofday(&tp, NULL);return ((double)tp.tv_sec + (double)tp.tv_usec * 1e-6);
}//展开的规约
__global__ void reduceUnrollWarps8(int* g_idata, int* g_odata, unsigned int size) {unsigned int tid = threadIdx.x;int* idata = g_idata + blockIdx.x * blockDim.x * 8;int idx = blockIdx.x * blockDim.x *8 + tid;//在这里。每个线程都添加一个来自于相邻数据块的元素if(idx + 7 * blockDim.x < size) {int a1= g_idata[idx];int a2= g_idata[idx + 1 * blockDim.x];int a3= g_idata[idx + 2 * blockDim.x];int a4= g_idata[idx + 3 * blockDim.x];int a5= g_idata[idx + 4 * blockDim.x];int a6= g_idata[idx + 5 * blockDim.x];int a7= g_idata[idx + 6 * blockDim.x];int a8= g_idata[idx + 7 * blockDim.x];g_idata[idx] = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;}__syncthreads();for (int stride = blockDim.x / 2; stride > 32 ; stride >>= 1) {if(tid < stride) {idata[tid] += idata[tid + stride];}__syncthreads();}if(tid < 32) {volatile int* vem = idata;vem[tid] += vem[tid + 32];vem[tid] += vem[tid + 16];vem[tid] += vem[tid + 8];vem[tid] += vem[tid + 4];vem[tid] += vem[tid + 2];vem[tid] += vem[tid + 1];}// 写出每个 block 的结果if (tid == 0) g_odata[blockIdx.x] = idata[0];
}// CPU 递归归约
int recursiveReduce(int* data, const int size) {if (size == 1) return data[0];int stride = size / 2;for (int i = 0; i < stride; ++i) {data[i] += data[i + stride];}return recursiveReduce(data, stride);
}int main(int argc, char** argv) {int dev = 0;cudaDeviceProp iProp;cudaGetDeviceProperties(&iProp, dev);cudaSetDevice(dev);int size = 1 << 24;  // 16777216printf("array size: %d\n", size);int blocksize = 512;if (argc > 1) {blocksize = atoi(argv[1]);}dim3 block(blocksize);dim3 grid((size + block.x - 1) / block.x);size_t nBytes = sizeof(int) * size;int* h_idata = (int*)malloc(nBytes);int* h_odata = (int*)malloc(grid.x * sizeof(int));  // 只需 grid.x 大小int* tmp = (int*)malloc(nBytes);for (int i = 0; i < size; ++i) {h_idata[i] = (int)(rand() & 0xFF);}memcpy(tmp, h_idata, nBytes);// CPU 归约double start = cpuSecond();int cpu_sum = recursiveReduce(tmp, size);double end = cpuSecond();printf("CPU sum: %d\t Time: %.4f s\n", cpu_sum, (end - start));// GPU 归约int* d_idata = NULL;int* d_odata = NULL;cudaMalloc((void**)&d_idata, nBytes);cudaMalloc((void**)&d_odata, grid.x * sizeof(int));cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();// start = cpuSecond();// reduceNeighbored<<<grid, block>>>(d_idata, d_odata, size);// cudaDeviceSynchronize();  // 保证 kernel 执行完成// end = cpuSecond();// cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int), cudaMemcpyDeviceToHost);int gpu_sum = 0;// for (int i = 0; i < grid.x; ++i) {//     gpu_sum += h_odata[i];// }// printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));cudaMemset( d_odata, 0, grid.x * sizeof(int));start = cpuSecond();reduceUnrollWarps8<<<grid.x / 8 , block>>>(d_idata, d_odata, size);cudaDeviceSynchronize();  // 保证 kernel 执行完成end = cpuSecond();cudaMemcpy(h_odata, d_odata, grid.x /8 * sizeof(int), cudaMemcpyDeviceToHost);gpu_sum = 0;for (int i = 0; i < grid.x / 8; ++i) {gpu_sum += h_odata[i];}printf("GPU sum: %d\t Time: %.4f s\n", gpu_sum, (end - start));// 校验结果bool result = (gpu_sum == cpu_sum);printf(result ? "Test PASSED\n" : "Test FAILED\n");free(h_idata);free(h_odata);free(tmp);cudaFree(d_idata);cudaFree(d_odata);cudaDeviceReset();return 0;
}
http://www.dtcms.com/a/513424.html

相关文章:

  • 小兔自助建站宽屏网页设计尺寸
  • 微网站移交北京建设管理有限公司官网
  • 网站源码检测推广引流最快的方法
  • 网站备案多久一次安徽网站建设推荐 晨飞网络
  • 制作app连接网站有哪些专门制作网页的软件
  • 依托git交付代码,并提供技术支持的方案
  • 新网站seo外包怎么申请免费企业邮箱账号
  • 《算法千题(1)--- 第31场蓝桥算法挑战赛》
  • 网站加载动画效果看车二手车网站源码
  • 徐州本地网站网站流量功能更怎么做
  • 网站开发搭建有个网站是做视频相册的
  • 揭阳网站制作企业discuz 分类网站
  • 帮做网站制作挣钱wordpress菜单小图标不显示
  • jsp做的当当网站的文档东莞建设监督网
  • HashMap为什么线程不安全? ConcurrentHashMap如何保证线程安全? AQS如何实现锁的获取与释放?用男女关系进行解释,一看就懂
  • 免费开源网站系统有哪些门户网站建设方案费用
  • 动易网站后台管理系统新昌县住房和城乡建设局网站
  • 网站切片 做程序数据分析师报名入口
  • 宿迁市网站建设口腔医院网站开发
  • Redis 特性/应用场景/通用命令
  • 学生个人网站建设模板网站为什么做站外推广
  • 零基础学网站建设 知乎长治长治那有做网站的
  • RPC服务
  • 北京外贸网站设计备案邯郸网页
  • 素马网站制作开发腾讯朋友圈广告怎么投放
  • 网站开发包括哪些网站推广怎么做
  • SwiftUI自定义一个水平渐变进度条
  • 电力电子技术 第四章——半导体功率器件
  • 网站运营专员具体每天怎么做wordpress音乐加载慢
  • 网站建设中图片是什么意思推特是谁的公司