__global__voidsaxpy(int n,float a,float*x,float*y){for (int i =blockIdx.x *blockDim.x +threadIdx.x; i < n; i +=blockDim.x *gridDim.x) {y[i] = a *x[i] +y[i]; }}
可移植性与可读性:网格步长循环代码与原始顺序循环代码更为相似,因此对其他用户来说更加易于理解。事实上,我们可以轻松地编写一个内核版本,它可以在 GPU 上作为并行 CUDA 内核运行,也可以在 CPU 上作为顺序循环运行。 Hemi 库提供了一个 grid_stride_range() 辅助工具,通过 C++11 的基于范围的 for 循环使这一过程变得非常简单。
HEMI_LAUNCHABLEvoidsaxpy(int n,float a,float*x,float*y){for (auto i : hemi::grid_stride_range(0, n)) {y[i] = a *x[i] +y[i]; }}
我们可以使用以下代码启动内核,当编译为 CUDA 时,它将生成一个内核启动,而当编译为 CPU 时,它将生成一个函数调用。
hemi::cudaLaunch(saxpy,1<<20,2.0, x, y);
网格步长循环是让你的 CUDA 内核更具灵活性、可扩展性、易调试性,甚至可移植性的极佳方式。尽管本文中的例子都使用了 CUDA C/C++,但同样的概念也适用于其他 CUDA 语言,如 CUDA Fortran。
示例代码
#include<stdio.h>voidinit(int*a,int N) {int i;for (i =0; i < N; ++i) {a[i] = i; }}__global__ voiddoubleElements(int*a,int N) {int idx =blockIdx.x *blockDim.x +threadIdx.x;int stride =gridDim.x *blockDim.x;for (int i = idx; i < N; i += stride) {a[i] *=2; }}boolcheckElementsAreDoubled(int*a,int N) {int i;for (i =0; i < N; ++i) {if (a[i] != i*2) returnfalse; }returntrue;}intmain() {int N =10000;int*a;size_t size = N *sizeof(int);cudaMallocManaged(&a, size);init(a, N);size_t threads_per_block =256;size_t number_of_blocks =32; doubleElements<<<number_of_blocks, threads_per_block>>>(a, N);cudaDeviceSynchronize();bool areDoubled =checkElementsAreDoubled(a, N);printf("All elements were doubled? %s\n", areDoubled ?"TRUE":"FALSE");cudaFree(a);return0;}