为何CUDA线程块可分配超1024线程且代码正常运行?
问题
查阅资料得知CUDA中每个线程块(Block)的最大线程数为1024,超出该值编译代码应报错。但使用nvcc编译如下代码时,通过<<<32,N>>>(N定义为300000000)为线程块分配了远超1024的线程,代码却能正常运行,且GPU计算速度远快于CPU,特此咨询原因。
#include <iostream> #include <chrono> #include <random> #include <cuda.h> #include <cuda_runtime.h> #define N 300000000 typedef std::chrono::high_resolution_clock Clock; using namespace std; __global__ void vector_add(float *out, float *a, float *b, int n) { int i = blockIdx.x; out[i] = a[i] + b[i]; } void vector_add_cpu(float *out, float *a, float *b, int n) { for(int i = 0; i < n; i ++){ out[i] = a[i] + b[i]; } } int main(){ srand(clock()); float *a, *b, *out; float *d_a, *d_b, *d_out; a = (float*)malloc(sizeof(float) * N); b = (float*)malloc(sizeof(float) * N); out = (float*)malloc(sizeof(float) * N); for(int i = 0; i < N; i++){ a[i] = float(rand()); b[i] = float(rand()); } auto cpu_start = Clock::now(); vector_add_cpu(out, a, b, N); auto cpu_end = Clock::now(); long long cputime =std::chrono::duration_cast<std::chrono::nanoseconds>(cpu_end - cpu_start).count(); std::cout << "vector_add_cpu: "<< cputime<< "ns.\n"; cudaMalloc((void**)&d_a, sizeof(float) * N); cudaMalloc((void**)&d_b, sizeof(float) * N); cudaMalloc((void**)&d_out, sizeof(float) * N); cudaMemcpy(d_a, a, sizeof(float) * N, cudaMemcpyHostToDevice); cudaMemcpy(d_b, b, sizeof(float) * N, cudaMemcpyHostToDevice); auto gpu_start1 = Clock::now(); vector_add<<<32,N>>>(d_out, d_a, d_b, N); auto gpu_end1 = Clock::now(); long long best_gpu=std::chrono::duration_cast<std::chrono::nanoseconds>(gpu_end1 - gpu_start1).count(); std::cout << "vector_add_gpu_finale_version: "<< best_gpu<< "ns.\n"; cout<<"Gpu is "<<(float)cputime/(float)best_gpu<<" times faster"; cudaFree(d_a); cudaFree(d_b); cudaFree(d_out); free(a); free(b); free(out); }
解答
核函数启动参数错误与未检查运行时错误
CUDA核函数的启动语法<<<grid_size, block_size>>>中,第一个参数是网格内的线程块数量,第二个参数是每个线程块内的线程数量。你写的<<<32,N>>>要求启动32个线程块,每个线程块包含3亿个线程,这明显违反了每个线程块最大1024线程的限制。但你的代码没有添加任何CUDA运行时错误检查(比如调用cudaGetLastError()),所以启动失败的错误被直接忽略,程序看似正常执行。核函数逻辑错误导致GPU未完成全部计算
你的核函数仅用blockIdx.x作为全局索引,意味着每个线程块内的所有线程都在重复计算同一个位置的元素(例如第0个线程块里的所有线程都在写入out[0])。最终GPU只计算了前32个元素,而CPU计算了全部3亿个元素——这就是GPU看起来更快的核心原因,它根本没完成你期望的全部任务。正确的实现方式
要正确实现向量加法,核函数需要计算全局线程索引,同时确保每个线程块的线程数在1024以内(通常选择256、512或1024),网格大小要足够覆盖所有元素:
__global__ void vector_add(float *out, float *a, float *b, int n) { // 计算全局线程索引 int i = blockIdx.x * blockDim.x + threadIdx.x; // 避免越界访问 if (i < n) { out[i] = a[i] + b[i]; } }
启动核函数时,合理设置参数(比如每个线程块用256个线程):
int block_size = 256; int grid_size = (N + block_size - 1) / block_size; // 向上取整计算网格大小 vector_add<<<grid_size, block_size>>>(d_out, d_a, d_b, N); // 添加错误检查 cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { cout << "CUDA error: " << cudaGetErrorString(err) << endl; }
内容的提问来源于stack exchange,提问作者max steuerman
相关产品推荐
相关产品推荐

