CUDA学习:进一步理解块、线程

摘要:
1.CUDA中的块和线程的概念可以用下图表示:每个网格包含可以用二维数组表示的块,每个块包含一个可以用二维阵列表示的线程。

1. CUDA里的块和线程概念可以用下面的图来表示:

CUDA学习:进一步理解块、线程第1张

  每个grid里包含可以用二维数组表示的block(块),每个block又包含一个可以用二维数组表示的thread(线程)。

 2.  二维数组块和线程可以用dim3来定义:

  dim3 blockPerGrid(3,2); //定义了3*2=6个blocks

  dim3 threadsPerBlock(3,3);//定义了3*3=9个threads

3. 运行时每个线程的代码,如何知道自己是在哪个块里的哪个线程中运行呢?通过下面的变量计算:

    * 块的二维索引:(blockIdx.x,blockIdx.y), 块二维数组x方向长度 gridDim.x,y方向长度 gridDim.y

    * 每个块内线程的二维索引:(threadIdx.x,threadIdx.y) ,线程二维数组x方向长度 blockDim.x,y方向长度 blockDim.y

    * 每个grid内有gridDim.x*gridDim.y个块,每个块内有 blockDim.x*blockDim.y个线程

     通过上述参数可以确定每个线程的唯一编号:

     tid= (blockIdx.y*gridDim.x+blockIdx.x)* blockDim.x*blockDim.y+threadIdx.y*blockDim.x+threadIdx.x;

    

4.下面具体一个例子,来引用上诉这些变量(仍引用上一个博客的N个数求和例子) 

   上一篇文章其实是用块和线程都是一维素组,现在我们用二维数组来实现

  关键语句:

  dim3 blockPerGrid(BLOCKS_PerGridX,BLOCKS_PerGridY); //定义了块二维数组

  dim3 threadsPerBlock(THREADS_PerBlockX,THREADS_PerBlockY);//定义了线程二维数组

      SumArray<<<blockPerGrid, threadsPerBlock>>>(dev_c, dev_a);

    完整代码如下:

   //////////////////////////////////////////

   //////////////////////////////////////////

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

#include <stdio.h>

cudaError_t addWithCuda(int *c, int *a);


#define TOTALN 72120

#define BLOCKS_PerGridX 2
#define BLOCKS_PerGridY 2
#define BLOCKS_PerGrid (BLOCKS_PerGridX*BLOCKS_PerGridY)

#define THREADS_PerBlockX 2 //2^8
#define THREADS_PerBlockY 4 //2^8
#define THREADS_PerBlock (THREADS_PerBlockX*THREADS_PerBlockY)

dim3 blockPerGrid(BLOCKS_PerGridX,BLOCKS_PerGridY); //定义了块二维数组
dim3 threadsPerBlock(THREADS_PerBlockX,THREADS_PerBlockY);//定义了线程二维数组

//grid 中包含BLOCKS_PerGridX*BLOCKS_PerGridY(2*2)个block
// blockIdx.x方向->,最大gridDim.x
// |***|***|*
// |0,0|0,1| blockIdx.y
// |***|***|* 方
// |1,0|1,1| 向
// |--------
// * ↓
// * 最大gridDim.y
// *


//每个block中包括THREADS_PerBlockX*THREADS_PerBlockY(4*2)个线程
// threadIdx.x方向->,最大值blockDim.x
// |***|***|*
// |0,0|0,1|
// |***|***|* threadIdx.y
// |1,0|1,1| 方
// |-------- 向
// |2,0|2,1| ↓
// |-------- 最大blockDim.y
// |3,0|3,1|
// |--------
// /


__global__ void SumArray(int *c, int *a)//, int *b)
{
__shared__ unsigned int mycache[THREADS_PerBlock];//设置每个块内同享内存threadsPerBlock==blockDim.x
//i为线程编号
int tid= (blockIdx.y*gridDim.x+blockIdx.x)* blockDim.x*blockDim.y+threadIdx.y*blockDim.x+threadIdx.x;
int j = gridDim.x*gridDim.y*blockDim.x*blockDim.y;//每个grid里一共有多少个线程
int cacheN;
unsigned sum,k;


cacheN=threadIdx.y*blockDim.x+threadIdx.x; //

sum=0;
while(tid<TOTALN)
{
sum += a[tid];// + b[i];
tid = tid+j;//获取下一个Grid里的同一个线程位置的编号

}

mycache[cacheN]=sum;

__syncthreads();//对线程块进行同步;等待该块里所有线程都计算结束


//下面开始计算本block中每个线程得到的sum(保存在mycache)的和
//递归方法:(参考《GPU高性能编程CUDA实战中文》)
//1:线程对半加:

k=THREADS_PerBlock>>1;
while(k)
{
if(cacheN<k)
{
//线程号小于一半的线程继续运行这里加
mycache[cacheN] += mycache[cacheN+k];//数组序列对半加,得到结果,放到前半部分数组,为下次递归准备
}
__syncthreads();//对线程块进行同步;等待该块里所有线程都计算结束
k=k>>1;//数组序列,继续对半,准备后面的递归
}

//最后一次递归是在该块的线程0中进行,所有把线程0里的结果返回给CPU
if(cacheN==0)
{
c[blockIdx.y*gridDim.x+blockIdx.x]=mycache[0];
}


}

int main()
{

int a[TOTALN] ;
int c[BLOCKS_PerGrid] ;


unsigned int j;
for(j=0;j<TOTALN;j++)
{
//初始化数组,您可以自己填写数据,我这里用1
a[j]=1;
}

// 进行并行求和
cudaError_t cudaStatus = addWithCuda(c, a);

if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addWithCuda failed!");
return 1;
}

unsigned int sum1,sum2;
sum1=0;
for(j=0;j<BLOCKS_PerGrid;j++)
{
sum1 +=c[j];
}
//用CPU验证和是否正确

sum2=0;
for(j=0;j<TOTALN;j++)
{
sum2 += a[j];
}

printf("sum1=%d; sum2=%d ",sum1,sum2);

// cudaDeviceReset must be called before exiting in order for profiling and
// tracing tools such as Nsight and Visual Profiler to show complete traces.
cudaStatus = cudaDeviceReset();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceReset failed!");
return 1;
}

return 0;
}

// Helper function for using CUDA to add vectors in parallel.

cudaError_t addWithCuda(int *c, int *a)
{
int *dev_a = 0;
int *dev_b = 0;
int *dev_c = 0;
cudaError_t cudaStatus;

// Choose which GPU to run on, change this on a multi-GPU system.
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
goto Error;
}

// 申请一个GPU内存空间,长度和main函数中c数组一样
cudaStatus = cudaMalloc((void**)&dev_c, BLOCKS_PerGrid * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
// 申请一个GPU内存空间,长度和main函数中a数组一样
cudaStatus = cudaMalloc((void**)&dev_a, TOTALN * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}

//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
// Copy input vectors from host memory to GPU buffers.
//将a的数据从cpu中复制到GPU中
cudaStatus = cudaMemcpy(dev_a, a, TOTALN * sizeof(int), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}


//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////

// dim3 threadsPerBlock(8,8);
//dim3 blockPerGrid(8,8);

// Launch a kernel on the GPU with one thread for each element.
//启动GPU上的每个单元的线程
SumArray<<<blockPerGrid, threadsPerBlock>>>(dev_c, dev_a);//, dev_b);

// cudaDeviceSynchronize waits for the kernel to finish, and returns
// any errors encountered during the launch.
//等待全部线程运行结束
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel! ", cudaStatus);
goto Error;
}

// Copy output vector from GPU buffer to host memory.
cudaStatus = cudaMemcpy(c, dev_c, BLOCKS_PerGrid * sizeof(int), cudaMemcpyDeviceToHost);
cudaStatus = cudaMemcpy(a, dev_a, TOTALN * sizeof(int), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}

Error:
cudaFree(dev_c);
cudaFree(dev_a);


return cudaStatus;
}

免责声明:文章转载自《CUDA学习:进一步理解块、线程》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Mac终端命令远程开启屏幕共享进行远程控制微信小程序上传Word文档、PDF、图片等文件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

redhat linux 从/home目录扩展空间至/根目录

查看分区大小 [root@easdb01 ~]# df -hFilesystem Size Used Avail Use% Mounted on/dev/mapper/vg_easdb01-lv_root 50G 3.5G 44G 8% /tmpfs 32G 224K 32G 1% /dev/shm/dev/sda1 485M 40M 421M 9% /b...

多线程并发详解

一、Java 线程实现/创建方式   注意:   • 新建的线程不会自动开始运行,必须通过start( )方法启动   • 不能直接调用run()来启动线程,这样run()将作为一个普通方法立即执行,执行完毕前其他线程无法并发执行   • Java程序启动时,会立刻创建主线程,main就是在这个线程上运行。当不再产生新线程时,程序是单线程的  1.1 继承...

转:WaitForSingleObject()函数、WaitForMultipleObject()函数

http://blog.csdn.net/xiaobai1593/article/details/6672193 在多线程下面,有时候我们会希望等待某一线程完成了再继续做其他事情,要实现这个目的,可以使用Windows API函数WaitForSingleObject,或者WaitForMultipleObjects。这两个函数都会等待Object被标为有...

jmeter如何设置浪涌场景

JMeter中我们使用线程组来控制测试场景, 原线程组无法设计复杂测试场景, 如浪涌。 一、jp@gc - Ultimate Thread Group 1、下载插件 下载地址:https://jmeter-plugins.org/?search=jpgc-casutg 百度网盘: 链接:https://pan.baidu.com/s/1vIB4lcz3zK...

Google-Guava Concurrent包里的Service框架浅析

原文地址  译文地址 译者:何一昕 校对:方腾飞 概述 Guava包里的Service接口用于封装一个服务对象的运行状态、包括start和stop等方法。例如web服务器,RPC服务器、计时器等可以实现这个接口。对此类服务的状态管理并不轻松、需要对服务的开启/关闭进行妥善管理、特别是在多线程环境下尤为复杂。Guava包提供了一些基础类帮助你管理复杂的状态转...

SetForegroundWindow以及 如何将一个某个窗口提到最顶层(转)

http://hi.baidu.com/gookings/item/2b7912ca8d5b3625a0b50aa2 SetForegroundWindow  函数功能:该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。系统给创建前台窗口的线程分配的权限稍高于其他线程。  函数原型:BOOL SetFore...