概要

量化的第三章主要讲的就是ILP,我感觉这一章的章节组织地相当清晰,逻辑递进关系很明显。

首先讲什么是ILP,以及ILP的主要挑战,ILP的主要挑战就是data dependency以及hazards以及control dependency。 然后就是如何解决这些问题,分别有哪些编译或者硬件方案,然后这些方案组织起来就成了哪些典型的架构。当然还有就是为了更进一步地发掘ILP,自然就到了multiple issue以及speculation。我之前看完这章,只是知道了用分支预测+乱序执行就行了,其他的那些statically scheduled superscalar,VLIW之类的都是什么鬼,从来没分得清,也不知道它们有什么特点,什么用。现在再看,发现,当年对这些关键问题提出了许多不同的方案,组合起不同的架构,出现的这些架构就在性能,功耗,复杂度,可编程性等方面与许多不同的特性。不是只有乱序才是这场混战的胜利者,其他的架构都仍然广泛存在,甚至比乱序应用得还要广。顺序核在低功耗,IOT领域大获全胜;VLIW在DSP领域广为使用。所以,我决定再仔细地探索这棵树。我又仔细阅读了附录H(关于VLIW的)以及一系列讲述DSP架构的文章,试图把这棵树尽可能完整地呈现出来。

control dependency

control dependency主要就是要处理branch的问题。要处理branch的问题,主要有降低分支延迟,延迟槽,分支预测以及分支消除等。

书里主要描述的是分支预测,分支预测是一个单独的故事了,历史上出现了许多静态或者动态的分支预测方案。现今在通用处理器中广泛使用的是动态分支预测。 但是静态的分支预测也值得注意,静态分支预测对于通用计算这种分支复杂,动态性高的方案或许不太适用,但是它对于DSP等比较regular的应用比较好,而且静态的方案相比于动态的方案有一个好处是:行为完全可控,适合硬实时应用。静态的方案可以是处理器用一个静态的方法或者可以是编译器辅助,如branch linkely之类的。

还有就是分支消除,分支消除主要是编译期的方案。在编译期,可以用条件执行指令来消除分支。当把条件执行的思路发挥到极致时,就变成了EPIC中那样的,所有的指令都支持条件执行。分支本质上是执行流的diverge,这对于SIMD类的程序,更加是问题,实际上,SIMD以及GPU也是用类似条件执行的方案,来消除分支。

当然,如果分支不能消除,那可以减少。例如,用loop unrolling的方法,执行同样多的任务,少执行几次分支。

对于常见的循环分支,还经常有纯硬件方案,那就是循环指令,或者自带了循环行为的算子。例如x86里面有loop指令,还有strcmp这种自带循环行为的复杂指令。

我们后面会总结一下静态方案与动态方案的异同以及各自的优劣。

data dependency

data dependency分为data dependency以及name dependency。当在流水线里面,当相邻比较近的指令之间有data dependency,那就称为data hazard了。data dependency是真的依赖,name dependency是伪依赖。有data dependency不是问题,程序要正确计算得保持真数据依赖,只有当有data dependency的指令靠得太近,引起了hazard,进而影响了流水线效率时,data dependency才值得我们的关注。

消除name dependency

消除name dependency,主要方案就是重命名。重命名的主要缺点就是要多占用寄存器,尤其是在speculation比较多的情况下,speculation中大量重命名占用的寄存器,可能很久都得不到释放。重命名也分为硬件方案以及编译方案。

硬件方案就是寄存器重命名了。

软件方案就是可以看做是一种寄存器绑定的替换。例如原先这个变量是绑定在这个寄存器里的,然后我把它给绑到其他寄存器上面去。在loop unrolling,local/global code scheduling里面经常出现这种情况。例如,loop unrolling,把loop展开多次,那每个loop iteration中用到的局部变量,现在要放到多个寄存器里面去了。

消除了name dependency后,下面的问题就是,让true data dependency不要引起hazard。或者引起hazard后,也不要让流水线堵住。

overcoming data hazards

不引起hazard主要就是靠调度,而出了hazard还要不让流水线堵住,这似乎只有乱序能做到了。

要调度好,首先就是要识别出依赖,然后把有依赖的指令调度好。

首先是识别以及保持依赖

数据依赖主要有寄存器数据依赖以及内存数据依赖。

寄存器数据依赖的识别直接从指令里面就可以静态地看出来,编译时维护依赖只要保证把寄存器和变量绑定好就是了,硬件上要保持数据依赖,主要靠一堆比较器,在乱序里面就是带了一堆比较器的issue queue;在顺序里面,主要就是流水线前递以及一堆比较器。

而内存数据的依赖用编译器静态分析则非常困难,主要难点如:指针分析,函数调用参数分析等静态分析就非常困难。还有就是内存依赖可能是非常动态的,甚至是输入相关的。例如,我们完全可以构造一个函数,它根据输入,对对应的数据进行交换,那这种内存数据依赖自然就是输入相关的了。总体来说,完全靠静态分析来确定内存数据依赖是非常困难,几乎是不可能的。而识别好、保持好内存数据依赖,对于调度以及speculation至关重要。所以不管是基于VLIW思路的EPIC或者传统的乱序superscalar,都需要一些硬件支持来动态地识别依赖。乱序的superscalar的LSQ里面有一堆比较器来实现dynamic memory disambiguation,而EPIC里有叫ALAT的表,本质上也是一堆CAM。

识别出这些依赖后,下一步就是做好调度了。

调度优化的目标,从全局来看,是降低这个指令依赖图的高度,缩短全局的执行时间;从局部来看,例如不要浪费任何一个cycle,不要让任何一个功能单元空转。局部的优化通常是贪心的,是最好做的。而全局优化通常是非常困难,甚至是NP难,是只能根据一些实际经验来做一些intuitive的优化。例如,乱序里面的wake up,select,用age based scheduler。

编译期的调度方案,主要分为basic block内的调度以及跨basic block甚至跨procedure的global scheduling。basic block内部的调度是相对好做的。 loop unrolling,通过把一小段代码展开几份,变成一大段代码,搞了个更大的basic block,调度出的并发度就更多。 软流水,乍看起来也是调度,但是是把本来在一个iteration里面的数据的生产以及数据消费划分到不同的iteration里面去。或者说是把不同iteration的数据生产与消费放到同一个iteration里面来,它们之间无依赖,自然就能直接发射执行。理解软流水,最好的方法,就是看下面这张图。

量化附录H里面对软流水有一个更本质的描述,即它是软件版本的Tomasolu,从效果上看,的确是的。

硬件的调度方案自然就是Tomasolu算法了。

speculation

speculation就是要尽可能多地向前执行,speculation主要的难点在于: 1、speculation要尽可能的准确 2、speculation如果错误了,要能把所做的计算回退。回退,本质上就是要恢复状态。要把程序可见的状态恢复过去,主要就是要处理好寄存器以及memory的speculation恢复。

所以speculation依赖于精确的分支预测,以及寄存器还有memory的speculation。

分支预测上面已经描述过了。

寄存器的speculation

硬件上主要靠寄存器重命名,而恢复主要靠刷ROB或者寄存器重命名表的恢复。 编译时的方案也是靠变量名重新绑定寄存器,并且对于不同的路径要分析处理好寄存器以及变量的liveness以及绑定关系。

memory的speculation

由于memory的状态数量比寄存器大太多太多,所以对于memory的speculation无法用寄存器那种重命名再恢复的方式。所以为了方便状态的恢复,写是写在core里的store buffer里的,不会对外面的内存状态造成破坏。而读是可以随便读的。

memory speculation主要是要让load跨过前面堵住的store,毕竟store只是把数据写下去,后面再读这块内存的消费者距离不会太近(毕竟距离如果太近,那就是编译器的问题了,要么说明这个store是多余,要么说明这个load应该直接用寄存器里面的值)。

而load是要使用数据,如果load数据被阻塞了,那后面的一系列运算都会被阻塞。store的东西主要是写在LSQ里,所以恢复不是大问题。memory的speculation的问题在于,load跨过前面的store后,如果后续发现load的地址是和前面store的地址冲突,那怎么把之前load上来的数据以及后续的对这个数据的一系列运算cancel掉。

硬件上主要靠LSQ进行dynamic memory disambiguation,然后还会有一套配套的重做方案。最简单的,可能是冲刷ROB,重置寄存器映射表。复杂的,就是要选择性地重做load以及某些运算了。

编译时,也可以进行memory的speculation,例如在不知道某个load地址是否与其之前的store地址冲突时,就把load指令调度到store前面,这种一般在后面都需要有配套的fix up指令,用来检查是否违背了memory的RAW,并且用一些软件代码来fix。

multiple issue

multiple issue就是要多发射几条指令,争取多发掘一些并行性,让后端功能单元忙起来。在后端堆了多个执行单元并且能乱序发射,乱序执行后,要把后端执行单元喂饱,就要用multiple issue来提供足够的指令流了。

架构

多发射一般是有如下常见的架构,量化书上称之为flavor。

  1. Statically scheduled superscalar processors
  2. VLIW (very long instruction word) processors
  3. Dynamically scheduled superscalar processors

区分它们的主要就是调度方式以及speculation。

静态流水超标量就是ARM A7,A53那种,静态流水线,但是流水线宽度不是太宽,然后在issue阶段是顺序发射的,并且必须等hazard全部解决后,才发射。执行部分估计也是顺序执行的。

所以,他们受静态发射,静态调度的影响,没法充分发掘并行性。另外它们没有乱序那样支持speculation的设施,speculation能力有限,或者说就是没有有效的speculation。它们主要靠好的分支预测+充分的流水线前递+好的编译优化+好的memory系统,来降低hazard的影响。他们没有乱序里支持speculation所必须的复杂的寄存器重命名,LSQ,ROB,所以面积小,能耗低,性能功耗比高。我最喜欢的一代神U骁龙625就是八个A53小核。

VLIW,very long instruction word,这个名字非常不直观。但是我看到一个说法说,可以把VLIW看做RISC的一个后继。因为依赖于先进的编译技术,RISC取代了CISC,极大地降低了硬件的复杂度。VLIW也是想靠先进的编译技术,把乱序处理器的复杂度给降下来。乱序里面为了能在硬件上充分地发掘并行性,使用了大量的芯片资源,并且控制逻辑十分复杂。VLIW是想靠编译技术发现程序的并行性,并且把这些并行性显式地表示出来,这样子硬件可以就不用来自己发掘并行性,而可以直接按照指令中的并行性说明,直接并行执行,所以VLIW技术需要更改ISA。传统的指令集是线性执行的ISA,而VLIW是把一系列可以并行执行的指令组成一个bundle,一起丢给处理器,处理器直接派发给执行单元并行执行。然后许多个bundle组成的序列,就是一个程序。可以看到,bundle里面的操作是并行的,如果是理想的情况,所有FU都能在固定延迟内完成运算,并且所有FU的延迟基本相同,这种情况是有点同步数据流的意思,那这样就可以做到完美的并行。

看到一个说法说,CISC到RISC时,CISC内复杂操作是用微码,微程序来实现的,软件不能直接修改微程序。RISC是把这些微程序暴露出来,让程序员、编译器直接在微码级别编程。而从乱序到VLIW,则是把乱序内复杂的FU之类的暴露出来,让程序员、编译器直接对FU进行编程,如果仔细看看VLIW的指令集,确实颇有这个意思。

还有就是动态流水超标量,就是标准的乱序处理器了。

要对这些架构进行比较,可以在很多的维度上进行比较。可以考虑应用场景,实现复杂度,性能,功耗。

首先就是静态流水超标量,由于静态流水顺序发射的缘故,出现长的stall,例如L1 cache miss之类的,是掩盖不了的。但是主要优点就是实现简单,功耗低,性能功耗比高,性能做高了,放在手机里做小核,例如现在满天下的A53。

而VLIW,它那种架构更加适合执行流相对比较静态,并且没有太多无法预测的latency的情况,例如DSP之类的。VLIW为了能处理通用计算领域的,复杂的执行流,以及cache miss等,需要用speculation来掩盖,而这些speculation是硬件配合来实现的。具体可以看看附录H中,对EPIC架构的描述。可以看到,当VLIW要支持激进的speculation后,整体架构变复杂了好多,架构复杂度以及实现上,和乱序都差不多了。关键的是,性能还没乱序好,这就歇菜了。VLIW另外还有软件兼容性的问题,一个是和传统的x86指令记得兼容性,还有就是代码在不同代微架构之间的兼容性。按照我们上面的那个说法,VLIW相当于直接把CPU的微体系结构暴露给程序员,直接进行编程,那兼容性自然就成为问题了。但是VLIW在高端DSP领域获得了广泛的应用,主要是因为DSP领域和通用计算领域有很大的不同,而DSP的这个场景正好适合VLIW。例如:

  • 相对规整、静态的执行流,数据流
  • 要保证硬实时性,memory都是片上的memory,访存延迟固定
  • 对软件兼容性要求不高

VLIW是一个非常有意思的design philosophy,我后续会再看一些VLIW在DSP领域应用的文章,总结出一篇。

乱序主要是靠speculation以及动态调度,掩盖较长的stall。而乱序的复杂度比较高,功耗也相对较高。

csgo降低延迟指令_ILP——指令级并行相关推荐

  1. csgo降低延迟指令_【显示FPS、延迟丢包率等参数】net_graph及相关指令

    本文将介绍CSGO控制台指令net_graph,用于显示画面帧数(fps)和延迟(ping).丢包率(loss和choke)等服务器/网络状态参数,除此之外,还有net_graph相关的控制台命令,用 ...

  2. csgo降低延迟指令_【显示FPS、延迟丢包率等网络参数】net_graph及相关控制台命令...

    本文将介绍CSGO控制台指令net_graph,用于显示画面帧数(fps)和延迟(ping).丢包率(loss和choke)等服务器/网络状态参数,除此之外,还有net_graph相关的控制台命令,用 ...

  3. csgo降低延迟指令_打开NVIDIA 驱动程序FPS降低操作延迟技术,让你的游戏操作如丝般润滑...

    再8月20日,NVIDIA 驱动程序(以下简称N卡)更新了版本436.02,在这个版本更新中,在某些单机大作上的表现帧数和性能提升了最高23%,优化了性能之外,技术宅比较在意的还是FPS降低操作延迟技 ...

  4. csgo降低延迟指令_CSGO控制台指令介绍 看看显示FPS、延迟丢包率这些都在哪

    除此之外,还有net_graph相关的控制台命令,用于调整服务器/网络状态参数位置.字体大小等. 首先介绍图中各个参数代表意思: fps:游戏画面帧数,越高越流畅 var:第一行的var代表电脑硬件输 ...

  5. HLS直播降低延迟的方法

    HLS推流,我们一般常用的搞法是通过ffmpeg接收直播流,进行转码切片后,将生成的ts放在磁盘上通过nginx代理进行对外推流服务,基于这种情况HLS直播降低延迟主要集中在以下几个点 1.将磁盘进行 ...

  6. DNF【地下城】修改IP降低延迟方法防封号方法

    对于大部分搬砖党而言,搬砖是一件持之以恒的事情.你只需要几个成型号,当然是轻松无比,需要的只是时间而已.但是在你的账户都还没有成型前呢?一点点技巧可以给你剩下很多时间! 第一,血色防线 血色防线 搬砖 ...

  7. csgo服务器比分修改指令,csgo服务器炸了指令

    csgo服务器炸了指令 内容精选 换一换 远程桌面链接Windows云服务器报错:连接被拒绝,因为没有授权此用户账户进行远程登录.Windows远程桌面相关权限配置异常.在运行窗口输入secpol.m ...

  8. Switch搭配“廉价采集卡”时,稍稍降低延迟的设置方法

    由于软硬件配置等差异因素,不同设备上的延迟可能存在差异,以下仅供参考. Switch通过廉价采集卡接笔记本电脑时,降低延迟的设置方法. 1. 测试软硬件环境 电脑:笔记本电脑 测试显卡:Intel三代 ...

  9. tcgames使用有延迟_教你用tcgames电脑玩刺激战场匹配手机的正确姿势:如何降低延迟卡顿...

    电脑玩刺激战场如何不被检测模拟器匹配手机玩家?用tcgames模拟器玩刺激战场怎么解决延迟卡顿问题?玩吃鸡应该大部分都用过模拟器和tcgames吧,一个是在电脑上下载游戏键鼠玩,一个是把手机投屏在电脑 ...

  10. 战地无服务器看不到延迟,《战地1》降低延迟技巧 如何进入低延迟服务器

    当前位置:电玩巴士游戏专题 战地1 <战地1>降低延迟技巧 如何进入低延迟服务器 作者:李南辛嗷嗷叫 来源:战地1吧 发布时间:2016年10月21日 <战地1>最大的魅力就在 ...

最新文章

  1. vue + webpack 模拟后台数据
  2. 完美解决distinct中使用多个字段的方法
  3. AcWing之找替换空格
  4. GitHub 5.9K,目标检测、跟踪、关键点全覆盖的年度开源项目来了!
  5. Webpack实战(五):轻松读懂Webpack如何分离样式文件
  6. c语言中判断输入是否为数字_C语言编程判断回文数
  7. Python稳基修炼的经典案例3(计算机二级、初学者必须掌握的例题)
  8. 等高线地图_高中地理——每日讲1题(北美洲的气候、等高线的阅读)
  9. bzoj2423[HAOI2010]最长公共子序列
  10. LeetCode 5376. 非递增顺序的最小子序列
  11. 百度举办区块链论坛,携手多伙伴加速区块链产业落地
  12. ajax分片上传,ajax异步实现文件分片上传
  13. 支持的存储类型有_跟我一起看博途1200系统手册之数据类型的介绍
  14. 4.数据库表的高级查询
  15. matlab chi2gof,chi2gof函数里的检验值P为什么总等于NaN呢
  16. iphone 扩容测试软件,拯救iPhone 12 64G!闪迪打造的扩容神器上手:轻松省钱
  17. Windows10系统迁移
  18. 【hadoop学习项目】10. 使用多级MR找出两两用户之间的共同好友
  19. 《那些年啊,那些事——一个程序员的奋斗史》六
  20. 〖每天学点管理〗——GTD时间管理

热门文章

  1. 关于call()的this指向研究
  2. 通过 Azure 媒体管理门户开始使用直播流媒体
  3. Android自定义的属性使用双向绑定
  4. 微信小程序初识到躺坑
  5. codeIgniter3 学习笔记四(文件上传)
  6. Linux下sdio设备扫描过程,[mmc]Linux下MMC/SD/SDIO的识别与操作
  7. MAC系统上grep使用办法
  8. 全网首发:怎样制作CDKEY(5)-让CDKEY更混乱
  9. fatal error: hb.h: 没有那个文件或目录
  10. UOS应用商店deb打包的正确目录结构