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

程序代码及图解析:

#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信息.

#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);
        }
}

  结果:

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。

代码:

#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(),即 等待之前的所有线程执行完毕后再接下去执行。

#include <stdio.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <time.h>
#include <stdlib.h>

#define TILE_WIDTH 25

__global__ void MatrixMuiOnDevice(int *M,int *N, int *P, int width)
{

        __shared__ float Mds[TILE_WIDTH][TILE_WIDTH];
        __shared__ float Nds[TILE_WIDTH][TILE_WIDTH];

        int bx = blockIdx.x;
        int by = blockIdx.y;
        int tx = threadIdx.x;
        int ty = threadIdx.y;
        int Col = bx * TILE_WIDTH + tx;
        int Row = by * TILE_WIDTH + ty; //获取该线程的位置

        int Pervalue = 0;

        for (int i = 0; i < width / TILE_WIDTH; i++)
        {
            Mds[ty][tx] = Md[Row * width+(i * TILE_WIDTH + tx)];
            Nds[ty][tx] = Nd[Col + (i * TILE_WIDTH + ty) * width];
            __syncthreads();

            for (int  k = 0; k < width / TILE_WIDTH; k++)
                Pervalue += Mds[ty][k] * Nds[k][tx];
            __syncthreads();
        }

        P[Row * width + Col] = Pervalue;
}

int main()
{
        int WID = 100;
        int a[WID][WID],b[WID][WID],c[WID][WID];
        int *M, *N, *P;
        int width = WID / 4 ;;
        int NUM = WID*WID;
        dim3 dimGrid(WID/width,WID/width);
        dim3 dimBlock(width,width);
        cudaEvent_t start,stop;
        float elapsedTime;
        cudaEventCreate(&start);
        cudaEventCreate(&stop);

        cudaMalloc((void**)&M, NUM*sizeof(int));
        cudaMalloc((void**)&N, NUM*sizeof(int));
        cudaMalloc((void**)&P, NUM*sizeof(int));
        //初始化
        for(int i = 0; i < 100; i++)
                for(int j = 0; j < 100; 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<<<dim,dimBlock>>>(M,N,P,width);
        cudaThreadSynchronize();
        cudaEventRecord(stop,0);
        cudaEventSynchronize(stop);
        cudaEventElapsedTime(&elapsedTime,start,stop);

        printf("%f\n",elapsedTime);

        cudaFree(M);
        cudaFree(N);
        cudaFree(P);
        return 0;
}

  

小结

第一个执行时间:

share memory执行时间:

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

注:转载请注明出处。

时间: 2024-10-22 05:19:49

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

Unix Shell范例精解---AWK练习(上)

Unix Shell范例精解第六章---AWK实用程序中有六个练习题,从简单到复杂,目前做了前面3道题目,后面的3题感觉做起来很吃力,关于循环.数组.函数的题目,不知道要怎么做. AWK练习1:练习1主要练习的是模式为正则表达式的操作,有些题目可以用书里后面讲解的内容解答 [[email protected] Ex_6.001-6.054]$ cat lab3.data Mike Harrington:(510) 548-1278:250:100:175 Christian Dobbins:(4

腾讯云无服务器云函数架构精解

欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 分享人:陈杰,腾讯云架构平台部技术专家,10年云计算经验,现供职于腾讯架构平台部,负责弹性计算及云函数技术研发,致力于提供领先的基础设施平台以提升资源利用率及优化提升程序员开发运维效率. 继虚拟机,容器技术,无服务器化成为新的行业热点,无服务器云函数可以让用户无需关心服务器的部署运营,只需开发最核心的业务逻辑,即可实现上线运营,具备分布容灾能力,可依据负载自动扩缩容,按照实际调用次数与时长计费.本次主要分享腾讯云无服务器云函数在技术实

Pascal GPU 架构详解

写在前面:本文假定读者有一定 CUDA 基础.如果你对 GPU, sm_60/sm_61,CUDA 这些名词感到陌生,可以看我之前写的博客<CUDA 从入门到精通>.  1. 前言 Nvidia 在今年的 GTC( GPU Technology Conference ) 上高调宣布了 Pascal 架构--专门针对每瓦性能优化的新架构,采用 16nm 工艺.接着发布了该系列的扛鼎之作 P100 及其装进机箱后的产品 DGX-1: Tesla P100 采用顶级大核心 GP100 GP100 参

CUDA 5 ---- GPU架构(Fermi、Kepler)

GPU架构 SM(Streaming Multiprocessors)是GPU架构中非常重要的部分,GPU硬件的并行性就是由SM决定的. 以Fermi架构为例,其包含以下主要组成部分: CUDA cores Shared Memory/L1Cache Register File Load/Store Units Special Function Units Warp Scheduler GPU中每个SM都设计成支持数以百计的线程并行执行,并且每个GPU都包含了很多的SM,所以GPU支持成百上千的

《Cocos2d-x游戏开发实战精解》学习笔记3--在Cocos2d-x中播放声音

<Cocos2d-x游戏开发实战精解>学习笔记1--在Cocos2d中显示图像 <Cocos2d-x游戏开发实战精解>学习笔记2--在Cocos2d-x中显示一行文字 之前的内容主要都是介绍如何在屏幕上显示图像,事实上除了图像之外,音乐的播放也可以被理解为一种显示的方式,本节将学习在Cocos2d-x中播放声音的方法. (1)在HelloWorld.h中对HelloWorld类进行如下定义: class HelloWorld : public Cocos2d::Layer { pu

软测之魂核心测试设计精解

Metasploit渗透测试手册.pdf: http://www.t00y.com/file/80348123 [计算机软件测试(原书第二版)].Cem.Kaner等.扫描版.pdf: http://www.t00y.com/file/80346316 [走出技术分析的陷阱].孙大莹.扫描版.pdf: http://www.t00y.com/file/60741464 [项目自动化之道-如何建构.部署.监控Java应用].Mike.Clark.pdf: http://www.t00y.com/f

《Cocos2d-x游戏开发实战精解》学习笔记1--在Cocos2d中显示图像

Cocos2d-x中的图像是通过精灵类来显示的.在Cocos2d-x中游戏中的每一个角色.怪物.道具都可以理解成是一个精灵,游戏背景作为一种特殊的单位将其理解成是一个精灵也没有什么不妥.在源文件本章目录下的项目ChapterThree03就展示了使用Cocos2d-x实现简单游戏开始界面的方法,主要就是通过精灵类来显示图像,其关键代码如范例3-5所示. [范例3-5 在Cocos2d-x中显示图像] Size size = Director::getInstance()->getVisibleS

Oracle PLSQL 实例精解 中文版pdf

下载地址:网盘下载 <Oracle PLSQL 实例精解>是一本逐步分解的,详尽的pl/sql编程教程,使用真实场景的试验.范例和练习来介绍读者所需的pl/sql编程技能,涵盖oracle 11g的最新特性.作者的写作手法源自于在哥伦比亚大学教授pl/sql编程技术的经验,深度探索pl/sql编程技术,融合自己的最佳实践.使用本书,读者可以快速掌握pl/sql编程基本知识,并建立工程化的概念,是市面上难得的pl/sql教程.下载地址:网盘下载

OpenGL实现通用GPU计算概述

可能比较早一点做GPU计算的开发人员会对OpenGL做通用GPU计算,随着GPU计算技术的兴起,越来越多的技术出现,比如OpenCL.CUDA.OpenAcc等,这些都是专门用来做并行计算的标准或者说接口. OpenGL用来做通用GPU计算主要是利用纹理存储器在GPU中计算以及把结果取回内存,这大概是OpenGL做通用GPU计算最好的选择. 大概的流程主要就是先创建OpenGL的环境,接着创建FBO(帧缓存对象).纹理.设置纹理参数.然后将纹理绑定到帧缓存对象,最后传输数据到纹理,接着用片段着色