CUDA范例精解通用GPU架构-(2)其实写个矩阵相乘并不是那么难

http://www.cnblogs.com/yusenwu/p/5300956.html

 程序代码及图解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include "book.h"
__global__  void  add(  int  a,  int  b,  int  *c ) {
*c = a + b;
}
int  main(  void  ) {
int  c;
int  *dev_c;
HANDLE_ERROR( cudaMalloc( ( void **)&dev_c,  sizeof ( int ) ) );
add<<<1,1>>>( 2, 7, dev_c );
HANDLE_ERROR( cudaMemcpy( &c,
dev_c,
sizeof ( int ),
cudaMemcpyDeviceToHost ) );
printf "2 + 7 = %d\n" , c );
cudaFree( dev_c );
return  0;
}

  

函数原型:__host__cudaError_t cudaMemcpy (void *dst, const void *src, size_t count, cudaMemcpyKind kind)

作用:在设备端和主机端拷贝数据。

参数:dst 目的地址 src 源地址 count 拷贝字节大小kind 传输的类型

返回值:

cudaSuccess, cudaErrorInvalidValue, cudaErrorInvalidDevicePointer, cudaErrorInvalidMemcpyDirection

说明:

从源地址拷贝设定数量的字节数至目的地址,kind类型有四种,分别为:

cudaMemcpyHostToHost, cudaMemcpyHostToDevice,  cudaMemcpyDeviceToHost, cudaMemcpyDeviceToDevice,

通过指定方向进行拷贝。存储器区域不可重叠。如若产生未定义拷贝方向的行为,dst和src将不匹配。

 正文

前面的图是最简单的一个CUDA程序,它引出了Grid Block Thread概念。很多threads组成1维,2维or3维的thread block. 为了标记thread在block中的位置(index),我们可以用上面讲的threadIdx。threadIdx是一个维度<=3的vector。还可以用thread index(一个标量)表示这个位置。

thread的index与threadIdx的关系:

  Thread index
1 T
2 T.x + T.y * Dx
3 T.x+T.y*Dx+z*Dx*Dy

其中T表示变量threadIdx。(Dx, Dy, Dz)为block的size(每一维有多少threads)。

因为一个block内的所有threads会在同一处理器内核上共享内存资源,所以block内有多少threads是有限制的。目前GPU限制每个 block最多有1024个threads。但是一个kernel可以在多个相同shape的block上执行,效果等效于在一个有N*#thread per block个thread的block上执行。

Block又被组织成grid。同样,grid中block也可以被组织成1维,2维or3维。一个grid中的block数量由系统中处理器个数或待处理的数据量决定。(来自这里)

 下图中描述了Thread、Block、Grid内存的访问机制。

每个thread有自己的local-memory。每一个block有自己的共享内存、grid和grid之间可以同时访问全局内存。这里要注意:block和block之间不能访问同一个共享内存,他们只能访问自己的共享内存。

cudaGetDeviceCount( &count )查询服务器的CUDA信息.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <cuda_runtime.h>
int  main()
{
         int  deviceCount;
        cudaGetDeviceCount(&deviceCount);
         int  device;
         for (device = 0; device < deviceCount; ++device)
        {
                cudaDeviceProp deviceProp;
                cudaGetDeviceProperties(&deviceProp,device);
                 printf ( "Device %d has compute capability %d.%d.\n" ,device,deviceProp.major,deviceProp.minor);
        }
}

  结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct  cudaDeviceProp {
     char  name[256];  //识别设备的ASCII字符串(例如,“GeForce GTX 280”)
     size_t  totalGlobalMem;  //全局内存大小
     size_t  sharedMemPerBlock;  //每个block内共享内存的大小
     int  regsPerBlock;  //每个block32位寄存器的个数
     int  warpSize;  // warp大小
     size_t  memPitch;  //内存中允许的最大间距字节数
     int  maxThreadsPerBlock;  //每个Block中最大的线程数是多少
     int  maxThreadsDim[3];  // 一个块中每个维度的最大线程数
     int  maxGridSize[3];  //一个网格的每个维度的块数量
     size_t  totalConstMem;  //可用恒定内存量
     int  major;  //该设备计算能力的主要修订版号
     int  minor;  //设备计算能力的小修订版本号
     int  clockRate;  //时钟速率
     size_t  textureAlignment;  //该设备对纹理对齐的要求
     int  deviceOverlap;  //一个布尔值,表示该装置是否能够同时进行cudamemcpy()和内核执行
     int  multiProcessorCount;  //设备上的处理器的数量
     int  kernelExecTimeoutEnabled;  //一个布尔值,该值表示在该设备上执行的内核是否有运行时的限制
     int  integrated;  //返回一个布尔值,表示设备是否是一个集成的GPU(即部分的芯片组、没有独立显卡等)
     int  canMapHostMemory;  //表示设备是否可以映射到CUDA设备主机内存地址空间的布尔值
     int  computeMode;  //一个值,该值表示该设备的计算模式:默认值,专有的,或禁止的
     int  maxTexture1D;  //一维纹理内存最大值
     int  maxTexture2D[2];  //二维纹理内存最大值
     int  maxTexture3D[3];  //三维纹理内存最大值
     int  maxTexture2DArray[3];  //二维纹理阵列支持的最大尺寸
     int  concurrentKernels;  //一个布尔值,该值表示该设备是否支持在同一上下文中同时执行多个内核

 

矩阵相乘也非常简单,难在如何在这个基础上提高速率。比如:引入sharememory。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <stdio.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <time.h>
#include <stdlib.h>
 
__global__  void  MatrixMuiOnDevice( int  *M, int  *N,  int  *P,  int  width)
{      
         int  x = threadIdx.x;
         int  y = threadIdx.y;  //获取该线程的位置
         
         float  Pervalue = 0;
         
         for  ( int  i = 0; i < width; i++)
        {      
                 float  Mdlement = M[y * width + i];
                 float  Ndlement = N[width * i + x];
                 
                Pervalue += Mdlement * Ndlement;
        }
         
        P[y * width + x] = Pervalue;
}
int  main()
{      
         int  a[30][30],b[30][30],c[30][30];
         int  *M, *N, *P;
         int  width = 30;
         int  NUM = 900;
        dim3 dimBlock(30,30);
        cudaEvent_t start,stop;
         float  elapsedTime;
        cudaEventCreate(&start);
        cudaEventCreate(&stop);
 
 
        cudaMalloc(( void **)&M, 900* sizeof ( int ));
        cudaMalloc(( void **)&N, 900* sizeof ( int ));
        cudaMalloc(( void **)&P, 900* sizeof ( int ));
         //初始化
         for ( int  i = 0; i < 30; i++)
                 for ( int  j = 0; j < 30; j++)
                {
                        a[i][j] = 2;
                        b[i][j] = 3;
                }
 
        cudaMemcpy(M,a,NUM* sizeof ( int ),cudaMemcpyHostToDevice);
        cudaMemcpy(N,b,NUM* sizeof ( int ),cudaMemcpyHostToDevice);
        cudaMemcpy(c,P,NUM* sizeof ( int ),cudaMemcpyDeviceToHost);
        cudaEventRecord(start,0);
        MatrixMuiOnDevice<<<1,dimBlock>>>(M,N,P,width);
        cudaThreadSynchronize();
        cudaEventRecord(stop,0);
        cudaEventSynchronize(stop);
        cudaEventElapsedTime(&elapsedTime,start,stop);
 
         printf ( "%f\n" ,elapsedTime);
         for ( int  i = 0; i < 30; i++)
                 for ( int  j = 0; j < 30; j++)
                {
                         printf ( "%d \n" ,c[i][j]);
                }
 
        cudaFree(M);
        cudaFree(N);
        cudaFree(P);
         return  0;
}

  share memory 改进。加入同步机制 __syncthreads(),即 等待之前的所有线程执行完毕后再接下去执行。

  

小结

第一个执行时间:

share memory执行时间:

 注意,核函数内不是所有线程一起进去执行,这个概念模糊不清。我们需要理解成,所有的线程并行执行核函数里面的程序,即每一个线程都会执行该函数,所有线程执行完,即结束。这个简单的概念,我一开始想了很久。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[本课程属于AI完整学习路线套餐,该套餐已“硬核”上线,点击立即学习!] 【为什么学习数学?】 人工智能的本质是数学,网上有很多AI课程,只蜻蜓点水的介绍一下算法背后的数学理论,知识点比较混乱,不成体系,学了以后一旦在实战遇到点就不知道该怎么办了。比方说老师遇到过用很多层MLP预测用户转化率的工程师,只是单纯的追求模型的“复杂度”,而忘记了底层数学的本质回归问题超过3层神经网络足以拟合空间中任一曲线,耗费了大量的运算资源却造成了模型的过拟合。 很多同学因为不理解AI底层的数学和理论,知其然不知其所以然,遇到问题不知道如何从根源上去思考排查解决问题,而是花了大量时间做一个“调参侠”,期望蒙中一个优化组合,可是调参空间之巨大如果没有方向随机的搜索和买彩票一样。但是专门的数学课学习起来非常抽象和枯燥,而且其中大量内容和人工智能关系不大。因此在设计这门专为人工智能服务的数学课,讲解从人工智能用到的底层的数学逻辑,让大家可以真正理解数学知识。 【讲师介绍】 褚英昊  技术总监深造于美国圣地亚哥国家超级计算中心,毕业后归国曾服务于世界某500强中国区AI Lab,是人工智能+智能制造领域的专家。先后发表国际期刊21篇(其中SCI收录17篇),第一作者发明专利11份。【学习目标】 1、更加高效学习、更好的理解AI知识 2、在找工作中在众多的套工程的“调参侠”中脱颖而出,获得面试官的重视 3、在实际工作和开发中,遇到问题能理解问题的本质,真正做到精准而高效的解决问题,获得领导的倚重 【梳理数学与AI知识之间的关联】 【专门为数学设计的项目案例】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值