草庐IT

计算机硬件的读写速度差异

小牛呼噜噜 2023-03-28 原文
  • 现代计算机系统
  • 存储器
  • 寄存器
  • CPU时钟周期
  • 高速缓存
  • 主存
  • 固态硬盘
  • 机械硬盘
  • 压榨CPU性能带来的问题
有序性问题

可见性问题

原子性问题

现代计算机系统

现代计算机系统与冯·诺依曼计算机差别不大,最大的区别冯·诺依曼计算机 是 以运算器为中心的,而现代计算机 以储存器为中心:

我们主要来看一下其中与储存相关的组件:

存储器

存储器是用来存放数据和程序。存储器 包含主存和辅存

  • 主存:直接与CPU交换信息,就是我们熟悉的内存。断电后内存的数据是会丢失的
  • 辅存:辅存可作为主存的后备存储器,不直接与CPU交换信息,容量比主存大,但速度比主存慢。比如机械硬盘、固态硬盘等。断电后硬盘的数据是不会丢失,硬盘是持久化存储设备。
  • 辅存、输入设备、输出设备 统称为IO设备;主机一般包含:CPU、主存
我们先来看看存储器的层次结构,来初步对各个储存器部件有所认识

我们可以发现存储器速度越快的话,相应的价格也会越发昂贵!

寄存器

CPU中 还有一个常见的组件: 寄存器,是CPU内部用来存放数据的一些小型的存储区域,用来暂时存放参与运算的数据以及运算结果。寄存器由电子线路组成,存取速度非常快,寄存器的成本较高,因而数量较少。

CPU时钟周期

CPU时钟周期:通常为节拍脉冲或T周期,即主频的倒数,它是CPU中基本时间单位。平时我们打游戏常说的超频,超的就是这个CPU主频。

举个例子,主频为3.0GHZ的CPU,一个时钟周期大约是0.3纳秒,内存访问大约需要120纳秒,固态硬盘访问大约需要50-150微秒,机械硬盘访问大约需要1-10毫秒,最后网络访问最慢,得几十毫秒左右。

这个大家可能对时间不怎么敏感,那如果我们把一个时钟周期如果按1秒算的话,内存访问大约就是6分钟 ,固态硬盘大约是2-6天 ,传统硬盘大约是1-12个月,网络访问就得几年了!我们可以发现CPU的速度和内存等存储器的速度,完全不是一个量级上的。

高速缓存

为了弥补 CPU 与内存两者之间的性能差异,就在 CPU 内部引入了 CPU Cache,也称高速缓存。CPU Cache用的是 SRAM(Static Random-Access Memory)的芯片,也叫静态随机存储器。其只要有电,数据就可以保持存在,而一旦断电,数据就会丢失。

CPU Cache 通常分为大小不等的三级缓存,分别是 L1 Cache、L2 Cache 和 L3 Cache

部件

CPU访问所需时间

备注

L1 高速缓存

2~4 个时钟周期

每个 CPU 核心都有一块属于自己的 L1 高速缓存,L1 高速缓存通常分成指令缓存和数据缓存。

L2 高速缓存

10~20 个时钟周期

L2 高速缓存同样是每个 CPU 核心都有的

L3 高速缓存

20~60个时钟周期

L3 高速缓存是多个 CPU 核心共用的

我们可以发现越靠近 CPU 核心的缓存其访问速度越快。

程序执行时,会先将内存中的数据加载到共享的 L3 Cache 中,再加载到每个核心独有的 L2 Cache,最后 进入到最快的 L1 Cache,之后才会被 CPU 读取。层级关系如下图:

主存

主存,直接与CPU交换信息,就是我们熟悉的内存。它使用的是一种叫作 DRAM(Dynamic Random Access Memory)的芯片,也叫动态随机存取存储器。断电后内存的数据是会丢失。DRAM 芯片的密度更高,功耗更低,有更大的容量,造价比 SRAM 芯片便宜很多,但速度比SRAM 芯片慢的多。

内存速度大概在 200~300 个 时钟周期之间

固态硬盘

固体硬盘(Solid-state Disk, SSD),数据直接存在闪存颗粒中,并且由主控单元记录数据存储位置和数据操作,每一个闪存颗粒的存储容量是有限的;

但是它相比内存的优点是断电后数据还是存在的,SSD固体硬盘的读写速度虽然比内存的大概慢10~1000 倍,但比机械硬盘快多了,当然价格也昂贵很多。不过随着时代的发展,固态硬盘的价格慢慢趋向接近机械硬盘。

机械硬盘

机械硬盘(_Hard Disk Drive, HDD_),它是通过物理读写的方式来访问数据的,机械硬盘在盘面上写数据、磁盘转动,机械臂移动,比较原始的数据读写方式,就像近现代的留声机发声原理一样。

由于受限于转盘转速与指针寻址的时间限制,因此它访问速度是非常慢的,它的速度比内存慢 10W 倍左右。当然机械硬盘也是有其优点的:容量大,价格便宜,恢复数据难度低,因此数据放在机械硬盘中比较保险。

压榨CPU性能带来的问题

由于CPU速度非常快,且价格非常昂贵,我们必须得充分压榨CPU,得像生产队的驴一样,让它不停地工作。

为了合理利用 CPU 的高性能,同时尽可能地节约成本,现代计算机将这些储存器充分的结合起来,由于这些硬件的数据存取速度差异导致了计算机系统编程中的各种问题:

有序性问题

为了充分压榨CPU的性能,CPU 会对指令乱序执行或者语言的编译器会指令重排,让CPU一直工作不停歇,但同时会导致有序性问题。

在CPU中为了能够让指令的执行尽可能地同时运行起来,采用了指令流水线。一个 CPU 指令的执行过程可以分成 4 个阶段:取指、译码、执行、写回。这 4 个阶段分别由 4 个独立物理执行单元来完成。

理想的情况是:指令之间无依赖,可以使流水线的并行度最大化。但是如果两条指令的前后存在依赖关系,比如数据依赖,控制依赖等,此时后一条语句就必需等到前一条指令完成后,才能开始。所以CPU为了提高流水线的运行效率,对无依赖的前后指令做适当的乱序和调度。

还有一种情况编译器会指令重排,比如java语言,JVM 的编译器会对其指令进行重排序的优化(指令重排)。

所谓指令重排是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快。JVM中并没有规定编译器优化相关的内容,也就是说JVM可以自由的进行指令重排序的优化。

无论是编译期的指令重排还是CPU 的乱序执行,主要都是为了让 CPU 内部的指令流水线可以“填满”,提高指令执行的并行度,充分利用CPU的高性能。

可见性问题

为了平衡CPU的寄存器和内存的速度差异,计算机的CPU 增加了高速缓存,但同时导致了 可见性问题。我们知道当程序执行时,一般CPU会去从内存中读取数据,来进行计算。CPU计算完之后,需要把数据重新放回到内存中。

当CPU的多个核心参与一个程序的运行,从内存中读取一个共享变量的数据,当不同核心间进行了各自的计算,把计算后的值放入自己的缓存中而不选择立即写入内存中(CPU写入内存的时机是不确定的)。那么在CPU的缓存中,这个共享变量有可能存放着不同的数据,这就导致了缓存的可见性问题。即一个线程对数据的修改无法对其他线程可见。

原子性问题

为了平衡CPU 与 I/O 设备的速度差异,操作系统增加了进程、线程概念,以分时复用 CPU,但同时导致了原子性问题。

原子操作就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。

当一个程序去I/O 设备读取数据, 由于I/O 设备数据存入读取速度,相比于CPU的执行速度来说度日如年,CPU这么牛逼这么昂贵的宝贝,怎么能让它歇着,得让它一直干活,去切换执行其他程序。也就是将CPU的时间进行分片,让各个程序在CPU上轮转执行。但被剥夺执行权的程序,等它从IO读取完数据后,还是得让CPU继续执行的,这时需要一个数据结构来保存,以便之后恢复继续执行,这个就是进程。

一开始进程中 只有一个"执行流",干活的人就一个。随着任务越来越多,发现进程不够用了,经常导致整个程序被阻塞,这时计算机让进程有多个执行流,干活的人变多了,那程序就不会再被阻塞了,"执行流" 就是线程。

如何解决这3个问题,就是并发、多线程需要处理的事,当然这是后话。

参考资料:

  • 《深入理解计算机系统》
  • 《计算机组成原理》
  • 《计算机组成原理》--唐朔飞
  • https://zhuanlan.zhihu.com/p/379947484
本文转载自微信公众号「 小牛呼噜噜」,作者「小牛呼噜噜」,可以通过以下二维码关注。

转载本文请联系「 小牛呼噜噜」公众号。

有关计算机硬件的读写速度差异的更多相关文章

  1. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  2. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  3. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. ruby - 使用 Ruby,计算 n x m 数组的每一列中有多少个 true 的简单方法是什么? - 2

    给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in

  6. arrays - 计算数组中的匹配元素 - 2

    给定两个大小相等的数组,如何找到不考虑位置的匹配元素的数量?例如:[0,0,5]和[0,5,5]将返回2的匹配项,因为有一个0和一个5共同;[1,0,0,3]和[0,0,1,4]将返回3的匹配项,因为0有两场,1有一场;[1,2,2,3]和[1,2,3,4]将返回3的匹配项。我尝试了很多想法,但它们都变得相当粗糙和令人费解。我猜想有一些不错的Ruby习惯用法,或者可能是一个正则表达式,可以很好地回答这个解决方案。 最佳答案 您可以使用count完成它:a.count{|e|index=b.index(e)andb.delete_at

  7. ruby - :variable and @variable 之间的差异 - 2

    作为RubyonRails新手,我明白“@”和“:”引用有不同的含义。我看到了thispost在SO中,其中描述了一些差异。@表示实例变量(例如@my_selection):表示别名(例如:my_selection)我遇到了一个情况,我有一个标准的MVC页面,类似于我的网络应用程序中的所有其他表单/页面。html.erb片段route.rb片段resources:my_selections当我尝试访问此页面时,出现此错误:NoMethodErrorinselections#createShowingC:/somedir/myapp/app/views/my_selections/ind

  8. ruby-on-rails - 如何计算 Ruby/Rails 中 JSON 对象的数量 - 2

    Ruby中如何“一般地”计算以下格式(有根、无根)的JSON对象的数量?一般来说,我的意思是元素可能不同(例如“标题”被称为其他东西)。没有根:{[{"title":"Post1","body":"Hello!"},{"title":"Post2","body":"Goodbye!"}]}根包裹:{"posts":[{"title":"Post1","body":"Hello!"},{"title":"Post2","body":"Goodbye!"}]} 最佳答案 首先,withoutroot代码不是有效的json格式。它将没有包

  9. ruby - 如何计算自 Ruby 中给定日期以来的周数? - 2

    目标我正在尝试计算自给定日期以来周的距离,而无需跳过任何步骤。我更喜欢用普通的Ruby来做,但ActiveSupport无疑是一个可以接受的选择。我的代码我写了以下内容,这似乎可行,但对我来说似乎还有很长的路要走。require'date'DAYS_IN_WEEK=7.0defweeks_sincedate_stringdate=Date.parsedate_stringdays=Date.today-dateweeks=days/DAYS_IN_WEEKweeks.round2endweeks_since'2015-06-15'#=>32.57ActiveSupport的#weeks

  10. 最新版人脸识别小程序 图片识别 生成二维码签到 地图上选点进行位置签到 计算签到距离 课程会议活动打卡日常考勤 上课签到打卡考勤口令签到 - 2

    技术选型1,前端小程序原生MINA框架cssJavaScriptWxml2,管理后台云开发Cms内容管理系统web网页3,数据后台小程序云开发云函数云开发数据库(基于MongoDB)云存储4,人脸识别算法基于百度智能云实现人脸识别一,用户端效果图预览老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。1-1,登录注册页可以看到登录页有注册入口,注册页如下我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序1-2,个人中心页登录成功以后,我们会进入个人中心页我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进

随机推荐