草庐IT

【动手学深度学习PyTorch版】23 深度学习硬件CPU 和 GPU

水w 2023-04-03 原文

上一篇请移步【动手学深度学习PyTorch版】22续 ResNet为什么能训练出1000层的模型_水w的博客-CSDN博客

目录

一、深度学习硬件CPU 和 GPU

1.1 深度学习硬件

◼ 计算机构成

◼ 程序执行的原理

◼ 内存

◼ 存储器

◼ 中央处理器(CPU)

1.2 如何提升cpu的利用率?(如何使运算在cpu上进行的更快,特别是数值运算:矩阵乘法、线性运算等)

◼ 提升空间和时间的内存本地性

◼ 尽量使用多核并行计算

1.3 GPU

◼ GPU

◼ cpu和gpu的对比

◼ 如何提升GPU的利用率?

◼ CPU/GPU带宽

◼ 如何在CPU上进行高性能计算编程?

1.4 总结


一、深度学习硬件CPU 和 GPU

1.1 深度学习硬件

◼ 计算机构成


(1)CPU(处理器):除了运行操作系统和其他许多功能外,还能执行程序;通常由 8 个或者更多个核心组成。

(2)内存(随机访问存储,RAM):用于存储和检索计算结果,如权重向量和激活参数,以及训练数据。

(3)以太网:一个或者多个,速度从 1 GB/s 到 100 GB/s 不等。

(4)高速扩展总线(PCle):用于系统连接一个或者多个 GPU;服务器最多有 8 个加速卡,通常以更高级的拓扑方式连接,而桌面系统则有 1 个或 2 个加速卡,具体取决于用户的预算和电源负载的大小。

(5)持久性存储设备:为系统需要的训练数据和中间检查点需要的存储提供了足够的传输速度;如磁盘驱动器、固态驱动器,在许多情况下使用高速扩展总线连接。

◼ 程序执行的原理

程序执行的原理:在计算机上运行代码的时候,需要将数据转移到处理器(GPU 或者 CPU)上执行计算,然后将结果从处理器转移回到随机访问存储和持久访问存储器中。

CPU(处理器):除了运行操作系统和其他许多功能外,还能执行程序;通常由 8 个或者更多个核心组成。

CPU的芯片图:

◼ 内存

主要用于存储需要随时访问的数据

当想要从内存中读取一部分内容时,需要先将地址(信息的位置)发送到 RAM,然后可以选择只读取一条 64 位记录还是一长串记录(突发读取,burst read),

  • 向内存发送地址并设置传输大约需要 100 ns(细节取决于所用内存芯片的特定定时系数),每个后续传输只需要 0.2 ns;
  • 第一次读取的成本是后续读取的 500 倍;
  • 每秒最多可以执行一千万次随机读取;
  • 应该尽可能地避免随机内存访问,而是使用突发模式读取和写入;

当拥有多个存储体时,每个存储体大部分时候都可以独立地读取内存,

  • 如果随机操作均匀分布在内存中,有效的随机操作次数将高达 4 倍(突发读取的速度也快了 4 倍);
  • 由于内存对齐是 64 位边界,因此最好将任何数据结构与相同的边界对齐(当设置了适当的标志时,编译器基本上就是自动地执行对齐操作);

因为 GPU 的处理单元比 CPU 多得多,因此它对内存带宽的需要也更高,

  • 一种解决办法是使内存总线变得更宽,这样可以同时传输更多的信息(首要方法);
  • 另一种方法是在 GPU 中使用特定的高性能内存(GPU 虽然速度更快,但是它的内存通常比 CPU 的内存小得多,因为成本更高,价格昂贵,通常仅限于在高端服务器的芯片上使用)。

◼ 存储器

随机访问存储的一些关键特性是带宽(bandwidth)和延迟(latency),存储设备也是如此,只是不同设备之间的特性差异可能更大。

(1)硬盘驱动器(hard disk drive,HDD)

它包含许多旋转的盘片,这些盘片的磁头可以放置在任何给定的磁道上进行读写,

优点是相对便宜,:缺点:

  • 典型的灾难性故障模式
  • 相对较高的读取延迟:
    • 硬盘驱动器的转速大约为 7200 RPM(每分钟转速),如果速度再快一点,就会由于施加在碟片上的离心力而破碎;
    • 在访问磁盘上的特定扇区时,需要等待碟片旋转到位(可以移动磁头,但是无法对磁盘加速),因此可能需要 8 毫秒才能使用请求的数据;
    • 硬盘驱动器可以以大约 100 IOPs(每秒输入/输出操作)的速度工作,在过去二十年中这个数字基本上没变,带宽(大约为 100 - 200 MB/s)也很难增加(每个磁头读取一个磁道的比特,因此比特率只随信息密度的平方根缩放);
    • 对于非常大的数据集,HDD 正迅速降级为归档存储和低级存储;

(2)固态驱动器(solid state drives,SSD)

固态驱动器使用闪存持久存储信息以更快地访问存储的记录,

缺点是:

  • 固态驱动器以块的方式( 256KB 或更大)存储信息:
    • 块只能作为一个整体写入(而且块必须被读取、擦除,然后再重新写入新的信息),需要耗费大量的时间,导致固态驱动器在按位随机写入时性能非常差;
  • 存储单元磨损比较快:
    • 通常在几千次写入之后就已经老化了;
    • 不建议将固态驱动器用于交换分区文件或大型日志文件;
  • 带宽的大幅增加迫使计算机设计者将固态驱动器与 PCIe 总线相连接,这种驱动器称为 NVMe(非易失性内存增强):
    • 最多可以使用 4 个 PCIe 通道;
    • 在 PCIe4.0 上最高可达 8 GB/s;

◼ 中央处理器(CPU)

(1)组成:

  • 处理器核心(processor cores):用于执行机器代码

前端加载指令并尝试预测将采用哪条路径,然后将指令从汇编代码解码为微指令(汇编代码通常不是处理器执行的最低级别代码,复杂的微指令可以被解码成一组更低级的操作,然后由实际的执行核心处理,通常执行核心能够同时执行许多操作)。

高效的程序可以在每个时钟周期内执行多条指令,前提是这些指令可以独立执行。

为了提高吞吐量,处理器还可以在分支指令中同时执行多条代码路径,然后丢弃未选择分支的结果。

  • 总线(bus):连接不同组件

总线会因为处理器型号、各代产品和供应商之间的特定拓扑结构有明显不同。

  • 缓存(cache):相比主内存实现更高的读取带宽和更低的延迟内存访问

为了避免出现向 CPU 传输用于处理的数据不足的情况,应该尽量避免从内存中加载新数据,而是应该将数据放在 CPU 的缓存上。

添加缓存一方面能够确保处理器核心不缺乏数据,但同时也增加了芯片的尺寸,消耗了原本可以用来提高处理能力的面积。

  • 向量处理单元(vector processing unit):为高性能线性代数和卷积运算提供辅助

    CPU在一个时钟周期内执行许多操作,是通过向量处理单元实现的(这些处理单元有不同的名称:在 ARM 上叫做 NEON,在 x86 上被称为 AVX2),常见的功能是能够执行单指令多数据操作(single instruction multiple data,SIMD)。

1.2 如何提升cpu的利用率?(如何使运算在cpu上进行的更快,特别是数值运算:矩阵乘法、线性运算等)

◼ 提升空间和时间的内存本地性

从图中我们可以看到,一个数据要参与计算,需要走很长的一条路。

如果要计算两个向量的和a+b,在计算之前需要准备数据,a 和b很有可能是放在主内存中的,如果想要将a和b进行相加,就需要将数据从主内存搬运到寄存器中(数据只有被搬运到寄存器中才能参与运算):主内存=>L3 cache(shared LLC)=>L2 cache=>L1 cache=>寄存器。

这条路里面,每一个地方的能其实是不一样的。这条路径中最快的是寄存器,寄存器可以认为是和主频一样快,

  • L1 访问延时:0.5ns (L1比寄存器要大,访问延迟比寄存器要高)
  • L2 访问延时:7ns (14 *L1 ,访问一次L2相当于访问14次L1)
  • 主内存访问延时:100ns (200 * L1 ,访问一次主内存相当于访问200次L1)

虽然cpu算的比较快、频率比较高,但实际上,实测下来,在实际测试的时候会发现远远没有达到CPU理论的算的速度,运算速度可能远低于理论值,这通常是由于内存访问速度过慢导致的。

内存访问太慢了,所以一般来说,我们所谓的加速一个比较大的关键点就是提升空间和时间的内存本地性,来使得我的缓存的效率更高。

具体来说,有两种办法:

  • 时间:重用数据使得它们在缓存里
  • 空间:按序读写数据使得可以读取

提升时间上的本地性:重用数据使得保持在他们的缓存里(计算时需要将数据从主内存搬运到寄存器中,如果计算完的数据不再使用,cpu会将数据从寄存器一直回退到主内存中,因此,对于重复使用的数据,就希望能够将重复使用的数据一直保持在寄存器或者是L1中,以便于下一次使用)。

提升空间上的本地性:按序读写数据使得可以预读取(cpu在读取内存的时候是一块一块地读,如果所要计算的数据如果在内存中是存储在一起的话,就希望下一次计算所使用到的数据和前一次计算所需要的数据是相邻的,这样能够提升cpu读取数据的效率)。

比如,例:

如果一个矩阵是按行存储的(在行上面的内存地址是连续的),访问一行会比访问一列要快,特别是当矩阵比较大的情况下。这是因为,

  • cpu一次读取64字节(缓存线),一个Float是4个字节,意味着一次性会读16个浮点数(一次性读取2行);
  • cpu在读完这16个浮点数(缓存线)之后,会提前预读取下一个(缓存线);

◼ 尽量使用多核并行计算

高端cpu有几十个核,比如:

加起来一共是64个核。

但是Intel比较有意思的是,假设从芯片上来说有4个物理核,但是从系统上你看到的核可能是有8个,因为它使用了超线程。把一个CPU超线程了两个核。

但是超线程对于计算密集型没有太多的用处,并行来利用所有核,超线程不一定提升性能,因为这2个超线程共享的是一个寄存器。

例:使用以下两种方式来计算a + b ,

左边使用循环对元素进行逐个相加,同样的道理,图的右边是使用计算框架(numpy等)进行加法运算,最终在运算的时候左边会比右边慢很多,慢个几百倍。

那是因为有2个原因:

  • 左边调用了n次函数(n是a的长度),每次调用它都是有开销的;
  • 右边很容易做并行(C++的并行实现,如下所示):
    • omp:C++中比较常用的并行方法;
    • 第一行omp的标记表示该循环可以被多个线程并行执行,可以很好地利用多核;

1.3 GPU

◼ GPU

这个是Ncidia Titan X的一个架构图, 我们可以认为一小块就是一个核。其实3080,3070,3090等等没什么区别,唯一区别就是放了不同的大核而已。

大核里面有很多小核,每个小核的每一个绿点,它其实我们可以认为是一个计算单元,可以在每一个绿点上开一个线程。所以GPU来说,每次可以上千次计算。

就算每一个绿点的计算能力比cpu弱,速度慢上4,5倍,但是计算单元数量多,最终还是快的。

◼ cpu和gpu的对比

表中的“/”表示一般型号的参数/高端型号的参数。CPU的核心数量一般来说是6核,好一点的是64。而显卡的核心数量一般来说是2K核,好一点的是4K个。

GPU的核心数量远多于CPU,所以就导致了GPU每秒能计算的浮点数(TFLOPS,可以用核心数量和主频的乘积来做一个简单的近似)就要远高于CPU。

每一次计算都需要从内存中读取数据,因此内存带宽也很重要,通常来说,如果达不到计算峰值一般是由于内存带宽的限制。

也就是说,GPU通过高的内核带宽,多核,来换取它的计算更快。核心数量和内存带宽的优势使得GPU在运算速度上要远快于CPU。但是另外付出的代价是,这也导致了GPU的内存大小不是很大,GPU的控制流很弱(CPU是做通用计算的,因此需要很强的控制流)。

◼ 如何提升GPU的利用率?

本质上与cpu一样,

(1)并行:使用数千个线程;

(2)内存本地性:

缓存更小,架构更简单(GPU为了节省面积将缓存做得比较小,这样做的好处是内存的带宽会更高一点);

(3)少用控制语句:支持有限,同步开销很大;

◼ CPU/GPU带宽

CPU和GPU并不是独立的,所有的任务都是运行在CPU上的,如果要在GPU上做运算,就会存在带宽的问题。

  • CPU和内存之间的带宽,可以认为CPU每个针脚会传输一定的数据,分配给内存的针脚越多,带宽就越大。
  • CPU和GPU之间的带宽:带宽不是很高,特别是在多个GPU的情况下,可能需要共享带宽。

因此不要频繁地在CPU和GPU之间传递数据:带宽限制,同步开销(这里的同步是由驱动决定的)。

◼ 如何在CPU上进行高性能计算编程?

(1)C++或者任何高性能语言:编译器成熟;
(2)Nvidia上用CUDA:编译器和驱动成熟;

(3)其他使用OpenCL(CUDA也支持OpenCL):质量取决于硬件厂商(编译器和驱动是根据硬件厂商决定);

1.4 总结

  • CPU:可以处理通用计算。性能优化考虑数据读写效率和多线程(多核);
  • GPU:使用更多的小核和更好的内存带宽,适合能大规模并行的计算任务;
  • 设备有运行开销,数据传输时要争取量大次少而不是量少次多;
  • 在训练过程中数据类型过小可能会导致数值的溢出(在推断过程中影响不大);

有关【动手学深度学习PyTorch版】23 深度学习硬件CPU 和 GPU的更多相关文章

  1. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  2. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  3. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  4. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  5. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  6. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  7. 机器学习——时间序列ARIMA模型(四):自相关函数ACF和偏自相关函数PACF用于判断ARIMA模型中p、q参数取值 - 2

    文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk​=Var(yt​)Cov(yt​,yt−k​)​其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞

  8. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

  9. ruby-on-rails - 这个 C 和 PHP 程序员如何学习 Ruby 和 Rails? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它

  10. ruby Hash 包括另一个哈希,深度检查 - 2

    进行这种深度检查的最佳方法是什么:{:a=>1,:b=>{:c=>2,:f=>3,:d=>4}}.include?({:b=>{:c=>2,:f=>3}})#=>true谢谢 最佳答案 我想我从那个例子中明白了你的意思(不知何故)。我们检查子哈希中的每个键是否在超哈希中,然后检查这些键的对应值是否以某种方式匹配:如果值是哈希,则执行另一次深度检查,否则,检查值是否相等:classHashdefdeep_include?(sub_hash)sub_hash.keys.all?do|key|self.has_key?(key)&&ifs

随机推荐