learn.lianglianglee.com/专栏/深入浅出计算机组成原理/20 面向流水线的指令设计(上):一心多用的现代CPU.md.html
2022-05-11 18:57:05 +08:00

1101 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>20 面向流水线的指令设计一心多用的现代CPU.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/深入浅出计算机组成原理/00 开篇词 为什么你需要学习计算机组成原理?.md.html">00 开篇词 为什么你需要学习计算机组成原理?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/01 冯·诺依曼体系结构:计算机组成的金字塔.md.html">01 冯·诺依曼体系结构:计算机组成的金字塔.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/02 给你一张知识地图,计算机组成原理应该这么学.md.html">02 给你一张知识地图,计算机组成原理应该这么学.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/03 通过你的CPU主频我们来谈谈“性能”究竟是什么.md.html">03 通过你的CPU主频我们来谈谈“性能”究竟是什么.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/04 穿越功耗墙,我们该从哪些方面提升“性能”?.md.html">04 穿越功耗墙,我们该从哪些方面提升“性能”?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/05 计算机指令:让我们试试用纸带编程.md.html">05 计算机指令:让我们试试用纸带编程.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/06 指令跳转原来if...else就是goto.md.html">06 指令跳转原来if...else就是goto.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/07 函数调用为什么会发生stack overflow.md.html">07 函数调用为什么会发生stack overflow.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/08 ELF和静态链接为什么程序无法同时在Linux和Windows下运行.md.html">08 ELF和静态链接为什么程序无法同时在Linux和Windows下运行.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/09 程序装载“640K内存”真的不够用么.md.html">09 程序装载“640K内存”真的不够用么.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/10 动态链接:程序内部的“共享单车”.md.html">10 动态链接:程序内部的“共享单车”.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/11 二进制编码:“手持两把锟斤拷,口中疾呼烫烫烫”?.md.html">11 二进制编码:“手持两把锟斤拷,口中疾呼烫烫烫”?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/12 理解电路:从电报机到门电路,我们如何做到“千里传信”?.md.html">12 理解电路:从电报机到门电路,我们如何做到“千里传信”?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/13 加法器:如何像搭乐高一样搭电路(上)?.md.html">13 加法器:如何像搭乐高一样搭电路(上)?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/14 乘法器:如何像搭乐高一样搭电路(下)?.md.html">14 乘法器:如何像搭乐高一样搭电路(下)?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/15 浮点数和定点数怎么用有限的Bit表示尽可能多的信息.md.html">15 浮点数和定点数怎么用有限的Bit表示尽可能多的信息.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/16 浮点数和定点数(下):深入理解浮点数到底有什么用?.md.html">16 浮点数和定点数(下):深入理解浮点数到底有什么用?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/17 建立数据通路(上):指令加运算=CPU.md.html">17 建立数据通路(上):指令加运算=CPU.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/18 建立数据通路(中):指令加运算=CPU.md.html">18 建立数据通路(中):指令加运算=CPU.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/19 建立数据通路(下):指令加运算=CPU.md.html">19 建立数据通路(下):指令加运算=CPU.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/深入浅出计算机组成原理/20 面向流水线的指令设计一心多用的现代CPU.md.html">20 面向流水线的指令设计一心多用的现代CPU.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/21 面向流水线的指令设计奔腾4是怎么失败的.md.html">21 面向流水线的指令设计奔腾4是怎么失败的.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/22 冒险和预测hazard是“危”也是“机”.md.html">22 冒险和预测hazard是“危”也是“机”.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/23 冒险和预测(二):流水线里的接力赛.md.html">23 冒险和预测(二):流水线里的接力赛.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/24 冒险和预测CPU里的“线程池”.md.html">24 冒险和预测CPU里的“线程池”.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/25 冒险和预测(四):今天下雨了,明天还会下雨么?.md.html">25 冒险和预测(四):今天下雨了,明天还会下雨么?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/26 Superscalar和VLIW如何让CPU的吞吐率超过1.md.html">26 Superscalar和VLIW如何让CPU的吞吐率超过1.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/27 SIMD如何加速矩阵乘法.md.html">27 SIMD如何加速矩阵乘法.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/28 异常和中断:程序出错了怎么办?.md.html">28 异常和中断:程序出错了怎么办?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/29 CISC和RISC为什么手机芯片都是ARM.md.html">29 CISC和RISC为什么手机芯片都是ARM.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/30 GPU为什么玩游戏需要使用GPU.md.html">30 GPU为什么玩游戏需要使用GPU.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/31 GPU为什么深度学习需要使用GPU.md.html">31 GPU为什么深度学习需要使用GPU.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/32 FPGA、ASIC和TPU计算机体系结构的黄金时代.md.html">32 FPGA、ASIC和TPU计算机体系结构的黄金时代.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/33 解读TPU设计和拆解一块ASIC芯片.md.html">33 解读TPU设计和拆解一块ASIC芯片.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/34 理解虚拟机:你在云上拿到的计算机是什么样的?.md.html">34 理解虚拟机:你在云上拿到的计算机是什么样的?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/35 存储器层次结构全景:数据存储的大金字塔长什么样?.md.html">35 存储器层次结构全景:数据存储的大金字塔长什么样?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/36 局部性原理:数据库性能跟不上,加个缓存就好了?.md.html">36 局部性原理:数据库性能跟不上,加个缓存就好了?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/37 理解CPU Cache“4毫秒”究竟值多少钱.md.html">37 理解CPU Cache“4毫秒”究竟值多少钱.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/38 高速缓存(下):你确定你的数据更新了么?.md.html">38 高速缓存(下):你确定你的数据更新了么?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/39 MESI协议如何让多核CPU的高速缓存保持一致.md.html">39 MESI协议如何让多核CPU的高速缓存保持一致.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/40 理解内存(上):虚拟内存和内存保护是什么?.md.html">40 理解内存(上):虚拟内存和内存保护是什么?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/41 理解内存解析TLB和内存保护.md.html">41 理解内存解析TLB和内存保护.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/42 总线:计算机内部的高速公路.md.html">42 总线:计算机内部的高速公路.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/43 输入输出设备我们并不是只能用灯泡显示“0”和“1”.md.html">43 输入输出设备我们并不是只能用灯泡显示“0”和“1”.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/44 理解IO_WAITIO性能到底是怎么回事儿.md.html">44 理解IO_WAITIO性能到底是怎么回事儿.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/45 机械硬盘Google早期用过的“黑科技”.md.html">45 机械硬盘Google早期用过的“黑科技”.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/46 SSD硬盘如何完成性能优化的KPI.md.html">46 SSD硬盘如何完成性能优化的KPI.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/47 SSD硬盘如何完成性能优化的KPI.md.html">47 SSD硬盘如何完成性能优化的KPI.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/48 DMA为什么Kafka这么快.md.html">48 DMA为什么Kafka这么快.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/49 数据完整性(上):硬件坏了怎么办?.md.html">49 数据完整性(上):硬件坏了怎么办?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/50 数据完整性(下):如何还原犯罪现场?.md.html">50 数据完整性(下):如何还原犯罪现场?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/51 分布式计算:如果所有人的大脑都联网会怎样?.md.html">51 分布式计算:如果所有人的大脑都联网会怎样?.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/52 设计大型DMP系统MongoDB并不是什么灵丹妙药.md.html">52 设计大型DMP系统MongoDB并不是什么灵丹妙药.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/53 设计大型DMP系统SSD拯救了所有的DBA.md.html">53 设计大型DMP系统SSD拯救了所有的DBA.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/54 理解Disruptor带你体会CPU高速缓存的风驰电掣.md.html">54 理解Disruptor带你体会CPU高速缓存的风驰电掣.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/55 理解Disruptor不需要换挡和踩刹车的CPU有多快.md.html">55 理解Disruptor不需要换挡和踩刹车的CPU有多快.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出计算机组成原理/结束语 知也无涯,愿你也享受发现的乐趣.md.html">结束语 知也无涯,愿你也享受发现的乐趣.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>20 面向流水线的指令设计一心多用的现代CPU</h1>
<p>前面我们用了三讲,用一个个的电路组合,制作出了一个完整功能的 CPU。这里面一下子给你引入了三个“周期”的概念分别是指令周期、机器周期或者 CPU 周期)以及时钟周期。</p>
<p>你可能会有点摸不着头脑了,为什么小小一个 CPU有那么多的周期Cycle我们在专栏一开始不是把 CPU 的性能定义得非常清楚了吗我们说程序的性能是由三个因素相乘来衡量的我们还专门说过“指令数×CPI×时钟周期”这个公式。这里面和周期相关的只有一个时钟周期也就是我们 CPU 的主频倒数。当时讲的时候我们说,一个 CPU 的时钟周期可以认为是可以完成一条最简单的计算机指令的时间。</p>
<p>那么,为什么我们在构造 CPU 的时候,一下子出来了那么多个周期呢?这一讲,我就来为你说道说道,带你更深入地看看现代 CPU 是怎么一回事儿。</p>
<h2>愿得一心人,白首不相离:单指令周期处理器</h2>
<p>学过前面三讲,你现在应该知道,一条 CPU 指令的执行是由“取得指令Fetch- 指令译码Decode- 执行指令Execute ”这样三个步骤组成的。这个执行过程,至少需要花费一个时钟周期。因为在取指令的时候,我们需要通过时钟周期的信号,来决定计数器的自增。</p>
<p>那么很自然地我们希望能确保让这样一整条指令的执行在一个时钟周期内完成。这样我们一个时钟周期可以执行一条指令CPI 也就是 1看起来就比执行一条指令需要多个时钟周期性能要好。采用这种设计思路的处理器就叫作单指令周期处理器Single Cycle Processor也就是在一个时钟周期内处理器正好能处理一条指令。</p>
<p>不过,我们的时钟周期是固定的,但是指令的电路复杂程度是不同的,所以实际一条指令执行的时间是不同的。在<a href="https://time.geekbang.org/column/article/95883">第 13 讲</a><a href="https://time.geekbang.org/column/article/97477">第 14 讲</a>讲加法器和乘法器电路的时候,我给你看过,随着门电路层数的增加,由于门延迟的存在,位数多、计算复杂的指令需要的执行时间会更长。</p>
<p>不同指令的执行时间不同,但是我们需要让所有指令都在一个时钟周期内完成,那就只好把时钟周期和执行时间最长的那个指令设成一样。这就好比学校体育课 1000 米考试,我们要给这场考试预留的时间,肯定得和跑得最慢的那个同学一样。因为就算其他同学先跑完,也要等最慢的同学跑完间,我们才能进行下一项活动。</p>
<p><img src="assets/6c85e2dd9b9988d8a458fb1200d96eee.jpeg" alt="img" /></p>
<p>快速执行完成的指令,需要等待满一个时钟周期,才能执行下一条指令</p>
<p>所以,在单指令周期处理器里面,无论是执行一条用不到 ALU 的无条件跳转指令,还是一条计算起来电路特别复杂的浮点数乘法运算,我们都等要等满一个时钟周期。在这个情况下,虽然 CPI 能够保持在 1但是我们的时钟频率却没法太高。因为太高的话有些复杂指令没有办法在一个时钟周期内运行完成。那么在下一个时钟周期到来开始执行下一条指令的时候前一条指令的执行结果可能还没有写入到寄存器里面。那下一条指令读取的数据就是不准确的就会出现错误。</p>
<p><img src="assets/3097988ae8dfc82e33ab80234bd5a29b.jpeg" alt="img" /></p>
<p>前一条指令的写入,在后一条指令的读取之前</p>
<p>到这里你会发现,这和我们之前<a href="https://time.geekbang.org/column/article/92215">第 3 讲</a><a href="https://time.geekbang.org/column/article/93246">第 4 讲</a>讲时钟频率时候的说法不太一样。当时我们说,一个 CPU 时钟周期,可以认为是完成一条简单指令的时间。为什么到了这里,单指令周期处理器,反而变成了执行一条最复杂的指令的时间呢?</p>
<p>这是因为,无论是 PC 上使用的 Intel CPU还是手机上使用的 ARM CPU都不是单指令周期处理器而是采用了一种叫作<strong>指令流水线</strong>Instruction Pipeline的技术。</p>
<h2>无可奈何花落去,似曾相识燕归来:现代处理器的流水线设计</h2>
<p>其实CPU 执行一条指令的过程和我们开发软件功能的过程很像。</p>
<p>如果我们想开发一个手机 App 上的功能,并不是找来一个工程师,告诉他“你把这个功能开发出来”,然后他就吭哧吭哧把功能开发出来。真实的情况是,无论只有一个工程师,还是有一个开发团队,我们都需要先对开发功能的过程进行切分,把这个过程变成“撰写需求文档、开发后台 API、开发客户端 App、测试、发布上线”这样多个独立的过程。每一个后面的步骤都要依赖前面的步骤。</p>
<p>我们的指令执行过程也是一样的,它会拆分成“取指令、译码、执行”这样三大步骤。更细分一点的话,执行的过程,其实还包含从寄存器或者内存中读取数据,通过 ALU 进行运算,把结果写回到寄存器或者内存中。</p>
<p>如果我们有一个开发团队,我们不会让后端工程师开发完 API 之后,就歇着等待前台 App 的开发、测试乃至发布,而是会在客户端 App 开发的同时,着手下一个需求的后端 API 开发。那么,同样的思路我们可以一样应用在 CPU 执行指令的过程中。</p>
<p>通过过去三讲你应该已经知道了CPU 的指令执行过程,其实也是由各个电路模块组成的。我们在取指令的时候,需要一个译码器把数据从内存里面取出来,写入到寄存器中;在指令译码的时候,我们需要另外一个译码器,把指令解析成对应的控制信号、内存地址和数据;到了指令执行的时候,我们需要的则是一个完成计算工作的 ALU。这些都是一个一个独立的组合逻辑电路我们可以把它们看作一个团队里面的产品经理、后端工程师和客户端工程师共同协作来完成任务。</p>
<p><img src="assets/1e880fa8b1eab511583267e68f0541ad.jpeg" alt="img" /></p>
<p>流水线执行示意图</p>
<p>这样一来,我们就不用把时钟周期设置成整条指令执行的时间,而是拆分成完成这样的一个一个小步骤需要的时间。同时,每一个阶段的电路在完成对应的任务之后,也不需要等待整个指令执行完成,而是可以直接执行下一条指令的对应阶段。</p>
<p>这就好像我们的后端程序员不需要等待功能上线,就会从产品经理手中拿到下一个需求,开始开发 API。这样的协作模式就是我们所说的<strong>指令流水线</strong>。这里面每一个独立的步骤,我们就称之为<strong>流水线阶段</strong>或者流水线级Pipeline Stage</p>
<p>如果我们把一个指令拆分成“取指令 - 指令译码 - 执行指令”这样三个部分那这就是一个三级的流水线。如果我们进一步把“执行指令”拆分成“ALU 计算(指令执行)- 内存访问 - 数据写回”,那么它就会变成一个五级的流水线。</p>
<p>五级的流水线,就表示我们在同一个时钟周期里面,同时运行五条指令的不同阶段。这个时候,虽然执行一条指令的时钟周期变成了 5但是我们可以把 CPU 的主频提得更高了。<strong>我们不需要确保最复杂的那条指令在时钟周期里面执行完成,而只要保障一个最复杂的流水线级的操作,在一个时钟周期内完成就好了。</strong></p>
<p>如果某一个操作步骤的时间太长,我们就可以考虑把这个步骤,拆分成更多的步骤,让所有步骤需要执行的时间尽量都差不多长。这样,也就可以解决我们在单指令周期处理器中遇到的,性能瓶颈来自于最复杂的指令的问题。像我们现代的 ARM 或者 Intel 的 CPU流水线级数都已经到了 14 级。</p>
<p>虽然我们不能通过流水线,来减少单条指令执行的“延时”这个性能指标,但是,通过同时在执行多条指令的不同阶段,我们提升了 CPU 的“吞吐率”。在外部看来,我们的 CPU 好像是“一心多用”,在同一时间,同时执行 5 条不同指令的不同阶段。在 CPU 内部,其实它就像生产线一样,不同分工的组件不断处理上游传递下来的内容,而不需要等待单件商品生产完成之后,再启动下一件商品的生产过程。</p>
<h2>超长流水线的性能瓶颈</h2>
<p>既然流水线可以增加我们的吞吐率,你可能要问了,为什么我们不把流水线级数做得更深呢?为什么不做成 20 级,乃至 40 级呢?这个其实有很多原因,我在之后几讲里面会详细讲解。这里,我先讲一个最基本的原因,就是增加流水线深度,其实是有性能成本的。</p>
<p>我们用来同步时钟周期的不再是指令级别的而是流水线阶段级别的。每一级流水线对应的输出都要放到流水线寄存器Pipeline Register里面然后在下一个时钟周期交给下一个流水线级去处理。所以每增加一级的流水线就要多一级写入到流水线寄存器的操作。虽然流水线寄存器非常快比如只有 20 皮秒ps10121012 秒)。</p>
<p><img src="assets/d9e141af3f2c5eedd5aed438388cfe26.jpeg" alt="img" /></p>
<p>但是,如果我们不断加深流水线,这些操作占整个指令的执行时间的比例就会不断增加。最后,我们的性能瓶颈就会出现在这些 overhead 上。如果我们指令的执行有 3 纳秒,也就是 3000 皮秒。我们需要 20 级的流水线,那流水线寄存器的写入就需要花费 400 皮秒,占了超过 10%。如果我们需要 50 级流水线,就要多花费 1 纳秒在流水线寄存器上,占到 25%。这也就意味着,单纯地增加流水线级数,不仅不能提升性能,反而会有更多的 overhead 的开销。所以,设计合理的流水线级数也是现代 CPU 中非常重要的一点。</p>
<h2>总结延伸</h2>
<p>讲到这里,相信你已经能够理解,为什么我们的 CPU 需要流水线设计了,也能把每一个流水线阶段在干什么,和上一讲的整个 CPU 的数据通路的连接过程对上了。</p>
<p>可以看到,为了能够不浪费 CPU 的性能,我们通过把指令的执行过程,切分成一个一个流水线级,来提升 CPU 的吞吐率。而我们本身的 CPU 的设计,又是由一个个独立的组合逻辑电路串接起来形成的,天然能够适合这样采用流水线“专业分工”的工作方式。</p>
<p>因为每一级的 overhead一味地增加流水线深度并不能无限地提高性能。同样地因为指令的执行不再是顺序地一条条执行而是在上一条执行到一半的时候下一条就已经启动了所以也给我们的程序带来了很多挑战。这些挑战和对应的解决方案就要请你坚持关注后面的几讲我们一起来揭开答案了。</p>
<h2>推荐阅读</h2>
<p>想要了解 CPU 的流水线设计,可以参看《深入理解计算机系统》的 4.4 章节,以及《计算机组成与设计 硬件 / 软件接口》的 4.5 章节。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/深入浅出计算机组成原理/19 建立数据通路(下):指令加运算=CPU.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/深入浅出计算机组成原理/21 面向流水线的指令设计奔腾4是怎么失败的.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997ab14bbb3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>