华南理工大学 陈虎 博士 tommychen74@yahoo.com.cn CUDA例子程序——矩阵乘法 华南理工大学 陈虎 博士 tommychen74@yahoo.com.cn.

Slides:



Advertisements
Similar presentations
渡黑水溝 郁永河. 2 戎克船:是明末清初時期往返兩岸的主要交通工具 ∗ 1. 關於台灣的開發歷史,我們到底了解多少呢?不妨試著說出 就我們所知有關台灣開發史的故事、小說、電影、音樂與大 家分享。 ∗ 2. 什麼是黑水溝?黑水溝為什麼會成為大陸移民渡海來臺時最 大的威脅? ∗ 3. 有聽過「六死三留一回頭」、「有唐山公,無唐山嬤」這兩.
Advertisements

广西花红药业 — 花红胶囊上市策略. Slide 2 花红胶囊上市策略 市场与竞品 价格与销售目标预估 广告创意策略与创意脚本 包装设计.
北京朝外大街证券营业部 投资总监 李世彤. Slide 2 今日话题 医药产业进入新的发展期 Slide 3 主要观点  宏观调控 “ 未结束 ” ,股指期货 “ 未放开 ” ,增 量资金 “ 未进来 ” ,沪深 A 股 “ 未见底 ” ;  地产金融 “ 低估值 ” ,目前 “ 不参与、不看空.
单元二:面向对象程序设计 任务二:借书卡程序设计.
我的家乡 南通 ….
好習慣是這樣養成的 夏欣 著 Shared by Leu C. D..
練習 5月31日.
小寶寶家庭保健護理小常識 講師:郭洽利老師
现代农业创业指导 广西省兴安县农广校.
校园信息管理系统 河北科技大学网络中心 2000/4/10.
萬獸之王 獅子.
世界经济的“全球化”.
徵收苗栗市福全段147、1588及文心段10、11地號等4筆土地之
第五章 病因病机.
网络教育(综合类)本学期教学工作 网络教育办公室:周学斌.
讲 义 大家好!根据局领导的指示,在局会计科和各业务科室的安排下,我给各位简要介绍支付中心的工作职能和集中支付的业务流程。这样使我们之间沟通更融洽,便于我们为预算单位提供更优质的服务。 下面我主要从三方面介绍集中支付业务,一是网上支付系统,二是集中支付业务流程及规定等,
寶島債.
中国人民公安大学经费管理办法(试行) 第一章总则 第四条:“一支笔” “一支笔”--仅指单位主要负责人。负责对本 单位的经费进行审核审批。
第四章 存储体系.
CUDA 超大规模并行程序设计 赵开勇 香港浸会大学计算机系 浪潮GPU高性能开发顾问.
第十九章 货币均衡 一、本章主要内容与结构安排 货币供求均衡与社会总供求平衡 货币均衡 通货膨胀 通货紧缩.
五、学习方法及应考对策 (一)学习方法 1.保证复习时间,吃透教材:上课之前应该对课程相关内容进行预习,把不理解的问题记录下来,带着问题听课。考试之前务必把课本看3遍以上,第一遍一定要精读,最好能做笔记,边读边记,不要快,要记牢。第二、三遍可以查缺补漏型的看,通过做题目看书,加深课本印象。 2.加强概念、理论性内容的重复记忆:概念、理论性内容一般比较抽象,所以在理解的基础上一定要重复记忆,在接受辅导之后,再加以重点记忆,以便及时巩固所学内容,切忌走马观花似的复习,既浪费时间,效果也不好。
科學科 污染 空氣 成因 的 : 題目 及 減少空氣污染的方法 陳玉玲 (4) 姓名 : 去到目錄.
我国的人民民主专政.
Xen基础架构安全性分析 云朋
CUDA程序设计.
并行计算实验上机 国家高性能计算中心(合肥).
Cuda 平行運算機制 報告者:林威辰.
程式語言 -Visual Basic 變數、常數與資料型態.
GPU分散式演算法設計與單機系統模擬(第二季)
Scope & Lifetime 前言 Local Scope Global Functions & Objects
第3章 變數、常數與資料型態 3-1 C語言的識別字 3-2 變數的宣告與初值 3-3 指定敘述 3-4 C語言的資料型態
华南理工大学 陈虎 博士 CUDA编程模型 华南理工大学 陈虎 博士
變數命名 保留字(Reserved Word)
6 使用者函數 6.1 函數定義 宣告函數 呼叫函數 呼叫多個函數 6-6
第四章 小技巧.
新觀念的 VB6 教本 第 6 章 資料型別.
貨幣需求與貨幣市場的均衡.
第二部分 免疫系统与免疫活性分子 第二章 免疫系统 第三章 免疫球蛋白 第二 部分 第五章 细胞因子 第四章 补体系统.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
$10 可空类型.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
OOP6 結構Struct 黃兆武.
易成 Institute of High Energy Physics
世上最著名的書是那一本? 導—slide1.
Java變數 2014/6/24.
第2章 数据类型及表达式 本章导读 本章主要知识点 《 C语言程序设计》 (Visual C++ 6.0环境)
聯合採購系統操作說明 研究部 製作 101年1月 103年6月修訂.
计算机系统结构(2012年春) ----存储层次: Cache基本概念
商業行為成立的要件 動動腦 Q 請試著判斷下列何者為商業行為? 請試著判斷下列何者為商業行為?.
OpenMP程序设计 2019/4/25.
指標
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
Java 程式設計 講師:FrankLin.
微信商城系统操作说明 色卡会智能门店.
C语言程序设计 李祥 QQ:
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
GPU based online noise filtering algorithm in LHASSO-WCDA
第二章 类型、对象、运算符和表达式.
陣列 東海大學物理系‧資訊教育 施奇廷.
第二章 基本数据类型 ——数据的表示.
本节内容 指针类型.
門診透析獨立預算執行概況 附件1 中央健康保險署 附件1.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
大綱 一.受試者之禮券/禮品所得稅規範 二.範例介紹 三.自主管理 四.財務室提醒.
變數、資料型態、運算子.
106年免試入學第一次模擬 選填重要日程表說明 1.106年1月10日中午12時~106年1月16日中午12時完成第一次模擬
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

华南理工大学 陈虎 博士 tommychen74@yahoo.com.cn CUDA例子程序——矩阵乘法 华南理工大学 陈虎 博士 tommychen74@yahoo.com.cn

一个简单的矩阵乘法例子 用矩阵乘法说明CUDA编程中内存和线程管理的基本特性: 共享存储器的用法 本地存储器、寄存器的用法 线程ID的用法 主机和设备之间数据传输的API 为了方便,以方形矩阵说明

方形矩阵乘法 矩阵P = M * N 大小为 WIDTH x WIDTH 在没有采用分片优化算法的情况下: 一个线程计算P矩阵中的一个元素

串行版本的矩阵乘法 // 宿主机的双精度矩阵乘法 void MatrixMulOnHost(float* M, float* N, float* P, int Width){ for (int i = 0; i < Width; ++i) for (int j = 0; j < Width; ++j){ double sum = 0; for (int k = 0; k < Width; ++k){ double a = M[i * width + k]; double b = N[k * width + j]; sum += a * b; } P[i * Width + j] = sum; N k j WIDTH M P i WIDTH k WIDTH WIDTH

向GPU传输矩阵数据 void MatrixMulOnDevice(float* M, float* N, float* P, int Width) { int size = Width * Width * sizeof(float); float* Md, Nd, Pd; //设置调用内核函数时的线程数目 dim3 dimBlock(Width, Width); dim3 dimGrid(1, 1); //在设备存储器上给M和N矩阵分配空间,并将数据复制到设备存储器中 cudaMalloc(&Md, size); cudaMemcpy(Md, M, size, cudaMemcpyHostToDevice); cudaMalloc(&Nd, size); cudaMemcpy(Nd, N, size, cudaMemcpyHostToDevice); //在设备存储器上给P矩阵分配空间 cudaMalloc(&Pd, size);

计算结果向主机传输 //内核函数调用,将在后续部分说明 //只使用了一个线程块(dimGrid),此线程块中有Width*Width个线程 MatrixMulKernel<<<dimGrid, dimBlock>>>(Md, Nd, Pd,Width); // 从设备中读取P矩阵的数据 cudaMemcpy(P, Pd, size, cudaMemcpyDeviceToHost); // 释放设备存储器中的空间 cudaFree(Md); cudaFree(Nd); cudaFree (Pd); }

矩阵乘法的内核函数 // 矩阵乘法的内核函数——每个线程都要执行的代码 __global__ void MatrixMulKernel(float* Md, float* Nd, float* Pd, int Width) { // 2维的线程ID号 int tx = threadIdx.x; int ty = threadIdx.y; // Pvalue用来保存被每个线程计算完成后的矩阵的元素 float Pvalue = 0;

内核函数 (继上.) //每个线程计算一个元素 for (int k = 0; k < Width; ++k) { 内核函数 (继上.) //每个线程计算一个元素 for (int k = 0; k < Width; ++k) { float Melement = Md[ty * Width + k]; float Nelement = Nd[k * Width + tx]; Pvalue += Melement * Nelement; } // 将计算结果写入设备存储器中 Pd[ty * Width + tx] = Pvalue; Nd k WIDTH tx Md Pd This should be emphasized! Maybe another slide on “This is the first thing” ty ty WIDTH tx k WIDTH WIDTH

只使用了一个线程块 一个线程块中的每个线程计算Pd中的一个元素 每个线程 缺点: 载入矩阵Md中的一行 载入矩阵Nd中的一列 Grid 1 一个线程块中的每个线程计算Pd中的一个元素 每个线程 载入矩阵Md中的一行 载入矩阵Nd中的一列 为每对Md和Nd元素执行了一次乘法和加法 缺点: 计算和片外存储器存访问比例接近1:1,受存储器延迟影响很大; 矩阵的大小受到线程块所能容纳最大线程数(512个线程)的限制 Block 1 Thread (2, 2) 48 WIDTH Md Pd

处理任意大小的方形矩阵 让每个线程块计算结果矩阵中的一个大小为(TILE_WIDTH)2的子矩阵 总共有(WIDTH/TILE_WIDTH)2 个线程块 Nd WIDTH Pd Md 需要注意的是:当WIDTH/TILE_WIDTH大于最大的网格数量(64K)时,需要在内核函数附近设置一个循环!(见最后的总结) by TILE_WIDTH ty WIDTH bx tx WIDTH WIDTH

G80显卡的存储器瓶颈 所有的线程都要访问全局存储器获取输入矩阵元素 G80显卡的峰值速度为346.5GFlops 效率仅为6% 每一次的单精度浮点乘法和加法需要两次的内存访问 (8 bytes) 全局存储器的访问带宽为86.4 GB/s 每秒钟可以读取21.6G个浮点数 每秒钟最多可以完成21.6GFlops G80显卡的峰值速度为346.5GFlops 效率仅为6% 全局存储器成为计算瓶颈 要充分使用高带宽的片上局部存储器 Grid Block (0, 0) Block (1, 0) Shared Memory Shared Memory Registers Registers Registers Registers Thread (0, 0) Thread (1, 0) Thread (0, 0) Thread (1, 0) Host Global Memory Constant Memory

使用共享存储器以便重用全局存储器中的数据 每个输入元素都需要被WIDTH个线程读取 将每个元素都装载到共享存储器中,让很多线程都使用本地数据以便减少存储带宽 使用分片算法 N WIDTH P M ty WIDTH tx WIDTH WIDTH

分片矩阵乘法 每个线程块计算一个大小为TILE_WIDTH的方形子矩阵Pdsub 每个线程计算Pdsub子矩阵中的一个元素 Md Nd Pd Pdsub TILE_WIDTH WIDTH bx tx 1 TILE_WIDTH-1 2 by ty TILE_WIDTHE 每个线程块计算一个大小为TILE_WIDTH的方形子矩阵Pdsub 每个线程计算Pdsub子矩阵中的一个元素 假设Md和Nd的大小都是TILE_WIDTH 的倍数

G80中首先需要考虑的事项 每个线程块内应该有较多的线程 应该有分解为若干个线程块 TILE_WIDTH=16时有 16*16 = 256 个线程 应该有分解为若干个线程块 一个1024*1024大小的Pd矩阵有64*64 = 4096 个线程块 每个线程块从全局存储器将矩阵M和N的一小块读入到局部存储器中,然后完成计算 从全局存储器中读出2*256 = 512个单精度浮点数; 完成 256 * (2*16) = 8,192 次浮点计算操作; 浮点操作:全局存储器读出操作=16: 1 全局存储器不再是性能瓶颈!

内核函数线程数配置 //每个线程块有TILE_WIDTH2个线程 dim3 dimBlock(TILE_WIDTH, TILE_WIDTH); //有(Width/TILE_WIDTH)2个线程块 dim3 dimGrid(Width/TILE_WIDTH, Width/TILE_WIDTH); //调用内核函数 MatrixMulKernel<<<dimGrid, dimBlock>>>(Md, Nd, Pd,Width);

内核函数 //获得线程块号 int bx = blockIdx.x; int by = blockIdx.y; //获得块内的线程号 int tx = threadIdx.x; int ty = threadIdx.y; //Pvalue:线程计算完成后的子矩阵元素——自动变量 float Pvalue = 0; //循环,遍历M和N的所有子矩阵 for (int m = 0; m < Width/TILE_WIDTH; ++m) { //此处代码在下面 };

将数据装入共享存储器 // 获取指向当前矩阵M子矩阵的指针Msub Float* Mdsub = GetSubMatrix(Md, m, by, Width); //获取指向当前矩阵N的子矩阵的指针Nsub Float* Ndsub = GetSubMatrix(Nd, bx, m, Width); //共享存储器空间声明 __shared__float Mds[TILE_WIDTH][TILE_WIDTH]; __shared__float Nds[TILE_WIDTH][TILE_WIDTH]; // 每个线程载入M的子矩阵的一个元素 Mds[ty][tx] = GetMatrixElement(Mdsub, tx, ty); //每个线程载入N的子矩阵的一个元素 Nds[ty][tx] = GetMatrixElement(Ndsub, tx, ty);

从局部存储器中计算结果 //同步,在计算之前,确保子矩阵所有的元素都已载入共享存储器中__syncthreads(); //每个线程计算线程块内子矩阵中的一个元素 for (int k = 0; k < TILE_WIDTH; ++k) Pvalue += Mds[ty][k] * Nds[k][tx]; //同步,确保重新载入新的M和N子矩阵数据前,上述计算操作已全部完成 __syncthreads();

一些其它代码 GetSubMatrix(Md, x, y, Width) x*TILE_WIDTH GetSubMatrix(Md, x, y, Width) 获取第(x, y)号子矩阵的起始地址 Md + y*TILE_WIDTH*Width + x*TILE_WIDTH); GetMatrixElement(Mdsub,tx,ty,Width) 获取子矩阵中某个元素的地址 *(Mdsub+ty*Width+tx)); Md y*TILE_WIDTH MSub TILE_WIDTH ty TILE_WIDTH tx Width

CUDA 代码 – 保存结果 上面的代码在G80上运行的速度是45 GFLOP! // 获取指向矩阵P的子矩阵的指针 Matrix Psub = GetSubMatrix(P, bx, by); //向全局存储器写入线程块计算后的结果子矩阵 //每个线程写入一个元素 SetMatrixElement(Psub, tx, ty, Pvalue); 上面的代码在G80上运行的速度是45 GFLOP!