《CUDA C编程权威指南》—读书笔记—2.3线程组织

GPU高速运算的很大一个特点就是,利用足够多的线程来隐藏内存操作等各类延迟。选择合适的网格和块来组织线程结构,对内核性能会产生极大的影响。以矩阵加法为例。
在全局内存中,一个矩阵往往以行优先的方式进行存储,即下一行接着上一行以线性方式在物理内存中存储。如下图,为一个8*6的矩阵。
在这里插入图片描述
在kernel中,一个线程被分配一个矩阵元素进行运算 。对于一个二维组织的线程来说,我们需要在编程时明确知道一下内容:
1、线程和块索引
2、矩阵中给定点的坐标
3、全局线性内存的偏移量
当我们确定了线程结构后,对于一个二维的矩阵(图像数据也是类似),我们可以将线程索引与矩阵坐标一一映射。
如图所示

ix=threadIdx.x+blockIdx.x*blockDim.x;
iy=threadIdx.y+blockIdx.y*blockDim.y;

再将索引映射到实际的内存中,取得实际的坐标

idx=ix+iy*nx

其实不难理解,就是相当于将一个矩阵(图像)分块,利用块的维度和块内的绝对坐标来映射原来未分块时的坐标。以上的nx是矩阵在x方向上的数量,ny是在y方向上的数量。若是类似opecv读取的图像行列,nx对应cols。
以二维网格二维块的矩阵加法为例子。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include<algorithm>

int nx = 1 << 12;
int ny = 1 << 12;

void arrayGenerate(float* array, int n) {
	srand((unsigned int)time(0));
	for (int i = 0; i < n; i++) {
		array[i] = (float)(rand() & 0xFF) / 10.0f;
	}
}

void arraySumCpu(float* A, float* B, int n, float* res) {
	for (int i = 0; i < n; i++) {
		res[i] = A[i] + B[i];
	}
}

void comp(float *CPU, float* GPU, int n) {
	double epsilon = 1.0E-8;
	bool match = 1;
	for (int i = 0; i < n; i++) {
		if (abs(CPU[i] - GPU[i]) > epsilon) {
			match = 0;
			printf("Arrays do not match\n");
			printf("host %5.2f gpu %5.2f at currentt %d\n", CPU[i], GPU[i], i);
			break;
		}
	}
	if (match) printf("Array  match\n");
}

__global__ void arraySumGpu(float* d_A, float* d_B, float* d_C, int x,int y) {
	unsigned int ix = threadIdx.x+blockIdx.x*blockDim.x;
	unsigned int iy = threadIdx.y + blockIdx.y*blockDim.y;
	unsigned int idx = ix + iy*x;
	if (ix<x&&iy<y) 
		d_C[idx] = d_A[idx] + d_B[idx];
}

int main(int argc, char **argv) {
	//printf("%s starting...\n", argv[0]);

	//set device
	int dev = 0;
	cudaSetDevice(dev);

	float *A, *B, *gpuRes;
	A = (float*)malloc(sizeof(float)*nx*ny);
	B = (float*)malloc(sizeof(float)*nx*ny);
//	hostRes = (float*)malloc(sizeof(float)*nx*ny);
	gpuRes = (float*)malloc(sizeof(float)*nx*ny);

	//初始化数组
	arrayGenerate(A, nx*ny);
	arrayGenerate(B, nx*ny);


	float *d_A, *d_B, *d_C;
	cudaMalloc((float**)&d_A, sizeof(float)*nx*ny);
	cudaMalloc((float**)&d_B, sizeof(float)*nx*ny);
	cudaMalloc((float**)&d_C, sizeof(float)*nx*ny);

	cudaMemcpy(d_A, A, sizeof(float)*nx*ny, cudaMemcpyHostToDevice);
	cudaMemcpy(d_B, B, sizeof(float)*nx*ny, cudaMemcpyHostToHost);

	int block_x = 32;
	int block_y = 32;
	dim3 block(block_x,block_y);
	dim3 grid((nx+block.x-1)/block.x, (ny+block.y-1)/block.y);
	
	cudaEvent_t start, stop;
	cudaEventCreate(&start);
	cudaEventCreate(&stop);
	float elapsedTime;
	cudaEventRecord(start, 0);
	arraySumGpu << <grid, block >> > (d_A, d_B, d_C, nx, ny);
	cudaEventRecord(stop, 0);
	cudaEventSynchronize(stop);
	cudaEventElapsedTime(&elapsedTime, start, stop);
	printf("time:%fms\n", elapsedTime);

	cudaMemcpy(gpuRes, d_C, sizeof(float)*nx*ny, cudaMemcpyDeviceToHost);

	//comp(hostRes, gpuRes, nx*ny);

	cudaFree(d_A);
	cudaFree(d_B);
	cudaFree(d_C);
	free(A);
	free(B);
	free(gpuRes);

	system("pause");
	return 0;
}

由于上述代码运行的平台为Fermi架构的GT 820M,性能是在够呛。书上使用的是1638416384的数据,在我这台老爷机上,哪怕将block的thread数开启到最大的3232=1024,二维grid的维度也超过了我的硬件限制。因此,缩减数据量为40964096。不同线程结构运行时间如下。1616的内核配置会让块数又超过限制,这里就不让老爷机献丑啦哈哈哈。

内核配置内核运行时间
(32,32)25.843519ms
(32,16)17.604671ms
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值