<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Easton Man&#039;s Blog</title>
	<atom:link href="https://blog.eastonman.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.eastonman.com</link>
	<description>临渊羡鱼，不如退而结网</description>
	<lastBuildDate>Tue, 19 Mar 2024 12:47:06 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.5</generator>

<image>
	<url>https://blog.eastonman.com/wp-content/uploads/2021/02/cropped-Logo-e1613298891313-32x32.png</url>
	<title>Easton Man&#039;s Blog</title>
	<link>https://blog.eastonman.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>龙芯3A6000：国产CPU中的一颗明星</title>
		<link>https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/</link>
					<comments>https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/#respond</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Tue, 19 Mar 2024 12:45:48 +0000</pubDate>
				<category><![CDATA[ChipsAndCheese 翻译]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[CPU]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=1592</guid>

					<description><![CDATA[<p>计算能力已成为全球经济体至关重要的资源，对中国来说也不例外。中国正在大力投资于国产 CPU 的开发，龙芯就处于国产 CPU 的前沿。我们之前已经介绍过该公司的 3A5000 CPU：这是一个四核处理器，提供了还算可以的 IPC，但是由于时钟频率过低，与竞争对手相比而言并不具有竞争力。</p>
The post <a href="https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/">龙芯3A6000：国产CPU中的一颗明星</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 37 分钟</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文翻译自 <a href="https://chipsandcheese.com/" target="_blank" rel="noopener" title="">ChipsAndCheese</a>，原文为英文，原文链接：<a href="https://chipsandcheese.com/2024/03/13/loongson-3a6000-a-star-among-chinese-cpus/" target="_blank" rel="noopener" title="">https://chipsandcheese.com/2024/03/13/loongson-3a6000-a-star-among-chinese-cpus/</a> 原作者：clamchowder</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文中文翻译首发自我的博客 <a href="https://blog.eastonman.com/blog/2024/03/oongson-3a6000-a-star-among-chinese-cpus/" target="_blank" rel="noopener" title=""></a><a href="https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/" target="_blank" rel="noopener" title="">https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/</a> ，翻译已取得 ChipsAndCheese 编辑/原作者授权。因英文原文无公开的授权协议，本译文也禁止转载，如需转载请先联系我或 ChipsAndCheese。</p>
</blockquote>



<figure class="wp-block-image aligncenter size-large"><img fetchpriority="high" decoding="async" width="1024" height="575" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-1024x575.jpg" alt="" class="wp-image-1596" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-1024x575.jpg 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-300x169.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-768x431.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image.jpg 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">龙芯视频的截图，视频位于&nbsp;<a href="https://www.loongson.cn/news/show?id=638" target="_blank" rel="noopener" title="">https://www.loongson.cn/news/show?id=638</a></figcaption></figure>



<p>现在，我们将看看龙芯新一代的 3A6000 CPU。3A6000 同样是一个四核的 CPU，时钟频率在 2.5GHz，但使用了新的 LA664 核心。相较于 3A5000 的 LA464 核心，LA664 是一个巨大且雄心勃勃的改进。尽管龙芯保持了相同的总体架构，但 LA664 拥有更宽、更深的流水线和更多的执行单元。让事情看起来更好的是，LA664 支持了 SMT（同时多线程）。如果正确实现的话，SMT 可以在很小的面积开销下增加多线程的性能。然而，要把 SMT 做好并不容易。</p>



<h2 class="wp-block-heading">3A6000的性能测试</h2>



<p>7-Zip 是一个文件压缩程序，它有很高的压缩率，但对CPU的要求也很高。7-Zip 几乎完全使用标量整数指令，因此 SIMD 扩展并不提供什么加速。这里，我们通过压缩一个大型的 ETL 性能追踪文件来对比性能。</p>



<p>3A6000 相比它的前身 3A5000 有 38% 的巨大性能提升。如果考虑 SMT 的话，这个提升还更大。在这个 workload 中每个核心只使用一个线程时，四核 LA664 与四核 Zen1 差不多。因此，LA664 的 IPC 性能非常好，因为它只运行在 2.5GHz 的频率上，但低频阻止了它超越 AMD 更新的产品。</p>



<figure class="wp-block-image aligncenter size-large"><img decoding="async" width="1024" height="585" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-1024x585.png" alt="" class="wp-image-1599" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-1024x585.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-300x171.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-768x438.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image.png 1228w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>当使用所有线程时，SMT 为 3A6000 提供了 20% 的性能提升。与此同时 AMD 的 SMT 在 Zen1 和 Zen2 上有 40% 以上的收益。SMT 的作用是提供了更多的显式并行性，帮助 CPU 隐藏延迟并更好地填满流水线。一方面，高 SMT 收益表明核心的 SMT 实现是经过精心调整的。另一方面，这又意味着核心在运行单线程时没有很好地隐藏延迟。</p>



<p>与 7-Zip 不同，libx264 视频编码大量使用 SIMD 指令。在 x86 CPU 上，编码器将使用 SSE、AVX、AVX2 这些扩展来加速，甚至是使用 AVX-512。在龙芯的 CPU 上，libx264 会使用 LSX 和 LASX 这两种 SIMD 扩展。在这里，我转码一个4K的《守望先锋》游戏剪辑来对比性能。</p>



<figure class="wp-block-image aligncenter size-large"><img decoding="async" width="1024" height="591" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-1-1024x591.png" alt="" class="wp-image-1601" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-1-1024x591.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-1-300x173.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-1-768x443.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-1.png 1230w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>3A6000 和 Zen1 性能大概差不多，在使用所有线程和每个核心只运行一个线程的情况下互有胜负。Zen1 较高的 SMT 收益可能受到其弱的 AVX2 实现的限制。Zen2 和 3A6000 都有 30% 以上的 SMT 收益。因为架构经过了全面改进和有坚实的 AVX-512 实现，AMD 最新的 Zen4 架构在这些测试中遥遥领先。和之前一样，对于一个 2.5GHz 的 CPU 来说，3A6000 的表现还是非常令人钦佩的。</p>



<p>LA664 核心将龙芯从低性能的区域带入了能够与 AMD 和 Intel 的旧型号竞争的行列。Zen1 也和 Haswell 性能差不多，而这两种架构即使在今天也是仍能一战的。接下来让我们来看看是什么样的架构能让龙芯与那些高时钟频率的设计竞争。</p>



<h2 class="wp-block-heading">核心架构</h2>



<p>LA664 是一个 6 发射的乱序核心，拥有丰富的核内资源和较大的乱序窗口。它在这些方面已经可以与较新的 Intel 和 AMD 核心相媲美。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="624" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-2.jpg" alt="" class="wp-image-1613" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-2.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-2-300x244.jpg 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>LA664 在 LA464 的基础上进行了改进。龙芯 3A5000 中的 LA464 核心是一个四发射设计，但在所有方面都相对保守。LA464 总体上是一个经得起考验的核心，没有明显的弱点，并为龙芯在 LA664 的改进提供了坚实的基础。毫不意外地，LA664 继承了 LA464 的总体架构。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="706" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-2-1.jpg" alt="" class="wp-image-1614" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-2-1.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-2-1-300x276.jpg 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>当然，架构图不能覆盖全部的细节。类似分支预测、执行单元延迟和访存性能等因素可能会对性能产生巨大的影响。</p>



<h2 class="wp-block-heading">龙芯3A6000的前端</h2>



<h3 class="wp-block-heading">分支预测</h3>



<p>CPU 分支预测器的责任是指导前端取指，告诉 CPU 分支将会走向哪个方向。这非常重要，因为如果分支预测器预测错误，CPU 的后端将会在错误路径上浪费大量的性能、功耗和时间。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="517" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-2.png" alt="" class="wp-image-1615" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-2.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-2-300x202.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>3A6000 的分支预测器拥有令人印象深刻的模式识别能力，在我们迄今为止看到的中国 CPU 中显然是最优秀的。它与我们在 3A5000 中所见到的大相径庭，并且几乎能够与最新的 Intel 和 AMD 的 CPU 相媲美。但是 AMD 一直在分支预测器能力上投入了大量精力，这使他们的 Zen3 架构仍然领先一些。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="450" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-3.png" alt="" class="wp-image-1620" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-3.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-3-300x176.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>Zen3 通过使用覆盖重定向的预测器来实现这一点：大多数分支行为简单，可被一级预测器迅速处理；仅对具有长期历史依赖的分支，能力更强但速度较慢的二级预测器才需要介入。这种方案使 Zen3 的分支预测器能在不牺牲预测速度的前提下跟踪非常长的分支历史。</p>



<p>即使 3A6000 无法与 AMD 最新的核心匹敌，龙芯在这一领域取得的进步仍值得赞扬。3A5000 的预测器看起来更适合 2000 年代中期到 2010 年代初的高性能核心，而不是近十年的产品。龙芯在分支预测器的改进无疑是 3A6000 改善性能的一大因素。</p>



<h3 class="wp-block-heading">分支预测器速度</h3>



<p>分支预测器要快速且准确，以避免出现供指问题。分支目标缓冲（BTB）缓存分支的目标，让预测器在实际分支指令被从 ICache 中取出并译码之前就能提供一个推测的指令流。LA664 拥有 64 项的 L1 BTB，能够连续（或者说Zero-Bubble地）处理 taken 的分支。BTB 缺失的情况很可能简单地等待 64KB 的 L1i 中的指令被取出和译码，然后才计算分支目标。这在实际效果上相当于 L1i 充当一个 1K-4K 项的 L2 BTB。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="403" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-4.png" alt="" class="wp-image-1623" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-4.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-4-300x157.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>相比之下，AMD 和 Intel 最近的架构都使用解耦的大型 L2 甚至 L3 BTB。只要分支 footprint 能被 BTB 覆盖，从下级 BTB 获取地址可以比从 ICache 获取更快，AMD 的 Zen4 就利用这一点来做到非常低延迟的分支处理。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="406" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-5.png" alt="" class="wp-image-1627" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-5.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-5-300x159.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>此外，从指令缓存解耦 BTB 有助于维护高 IPC，因为预测器得以免受 L1i 缺失延迟的影响。但龙芯决定放弃一个大的、解耦的 BTB 并非独一无二。Tachyum Prodigy 也做出了同样的决定，因为他们发现用标准库达到他们的目标频率将会太贵。如同 Tachyum 的 Prodigy，3A6000 通过使用一个大的 64KB 指令缓存来补偿这一点。如果 L1i 缺失变少，那这种做法弱点就不那么明显了。</p>



<p>3A6000 似乎还有更加积极的 nextline 指令预取。上面的测试中，分支仅仅跳到到下一个 16B 对齐的块，因此 nextline 指令预取器可以很好地工作。相比之下，AMD 的预取完全由分支预测器驱动。一旦超过了 BTB 容量，就无法隐藏 L2 延迟。</p>



<h3 class="wp-block-heading">间接分支预测</h3>



<p>与直接分支相比，间接分支更难预测。间接分支并不是直接编码跳转目标，而是跳转到寄存器中的地址。3A6000 在间接分支预测方面做得非常好。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="542" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-6.png" alt="" class="wp-image-1631" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-6.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-6-300x212.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>3A6000 可以跟踪总共 1024 个间接跳转目标，是 3A5000 的两倍。3A5000 只能跟踪大约 24 个间接分支，而 3A6000 可以轻易跟踪超过 128 个间接分支。相比之下，Zen2 也可以跟踪 1024 个间接跳转目标，因此 3A6000 在这一点上与较新的 x86 CPU 非常接近。</p>



<h3 class="wp-block-heading">返回地址预测</h3>



<p>返回指令作为一种特殊类型的间接分支，经常是以 call-return pair 的形式出现。许多处理器倾向于使用一个专用的返回地址栈。当分支预测器遇到一个调用指令时，它会将一个地址压入这个栈中。当它遇到一个返回指令时，它会从栈中弹出一个地址。有趣的是，3A6000 将返回地址栈的容量从 3A5000 的 32 项减少到了仅仅 16 项。如果核心的两个 SMT 线程都在运行，有效容量甚至可能进一步降低到 8 项。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="346" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-7.png" alt="" class="wp-image-1633" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-7.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-7-300x135.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>将返回栈容量降低到 16 项在某种程度上是有道理的，因为即使是一个较小的返回栈也能覆盖到大多数调用/返回情况。龙芯可能是在返回地址栈溢出示回落到间接分支预测器，因为溢出时 3A6000 只会遭受轻微的性能损失。Intel 采用同样的这种策略。在这种情况下，返回栈地址可以被视为一个功耗和性能优化的措施，而不是性能关键路径上的一部分。</p>



<h3 class="wp-block-heading">分支预测准确率</h3>



<p>7-Zip 和其他压缩程序通常对 CPU 的分支预测器构成巨大的挑战。3A6000 的分支预测器表现出色，与 Zen1 在 MPKI 上持平。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="688" height="798" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-8.png" alt="" class="wp-image-1635" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-8.png 688w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-8-259x300.png 259w" sizes="(max-width: 688px) 100vw, 688px" /></figure>



<p>在准确率方面，3A6000 与 Zen2 不相上下。在跨 ISA 对比时，准确率是一个更好的衡量标准，因为 3A6000 能够以更少的执行指令完成工作负载。MPKI 较高可能是因为龙芯的指令流中分支的比例更大。AMD 最新的 Zen4 架构仍然领先，但龙芯在分支预测器方面取得了值得赞扬的进步，它远远优于3A5000。</p>



<p>libx264 拥有较少并且更好预测的分支，但我们仍然可以看到测试 CPU 之间的差异。在这个测试中，ISA 差异对龙芯不利。3A6000 的 MPKI 较低，但这仅是因为它完成工作负载执行了更多的指令。当有更多的非分支指令要处理时，误预测的影响就变得不那么重要了。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="878" height="1024" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-9-878x1024.png" alt="" class="wp-image-1643" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-9-878x1024.png 878w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-9-257x300.png 257w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-9-768x896.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-9.png 1109w" sizes="(max-width: 878px) 100vw, 878px" /></figure>



<p>3A6000 的分支预测器表现可以与 AMD 的 Zen2 相媲美。龙芯再次显示出他们在设计分支预测器能力上的竞争力。</p>



<h3 class="wp-block-heading">指令获取</h3>



<p>一旦分支预测器决定了指令流，就轮到指令缓存来为核心提供指令。与它的前辈一样，3A6000 拥有一个大型的 64KB 4 路 L1i。Intel 和 AMD 的 CPU 只有 32KB 的 L1i，相比来说 3A6000 有一个相当大的L1i。64KB 的 L1i 向一个 6 宽的译码器供指，这使 3A6000 有比它的前任宽 50% 的前端。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="503" height="1024" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-10-503x1024.png" alt="" class="wp-image-1644" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-10-503x1024.png 503w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-10-147x300.png 147w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-10.png 527w" sizes="(max-width: 503px) 100vw, 503px" /></figure>



<p>AMD 和Intel 的高性能 CPU 使用 uOp Cache，这可以让它们在提供更高吞吐量的同时避免指令译码开销。自 Zen 架构以来，AMD 的 uOp Cache 理论上可以在每周期提供一整行的 8 个 uOp，但是下游的重命名宽度限制了核心的吞吐量。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="509" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-11-1024x509.png" alt="" class="wp-image-1645" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-11-1024x509.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-11-300x149.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-11-768x382.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-11.png 1374w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>当代码溢出 L1i 时，龙芯 3A6000 保持了良好的吞吐量。3A5000 在运行 L2 中的代码时出现了不明原因的指令带宽不足。龙芯在 3A6000 中解决了这个问题，它可以在代码被 L2 覆盖时维持每周期 3 条指令的吞吐。</p>



<p>不幸的是，在指令 footprint 增大到 L3 以后，情况并没有那么乐观。看起来龙芯在 3A6000 上并没有改进他们从 L3 获取指令的能力。这是有些遗憾，因为 Golden Cove 和 Zen3 都可以每周期获取超过三个 4 字节指令，而龙芯的 CPU 连每周期两个都不行。</p>



<h2 class="wp-block-heading">重命名和分配</h2>



<p>指令被解码成 uOp 后，CPU 需要分配后端资源。这些资源追踪指令状态，允许 CPU 尽快执行指令，同时确保正确的程序行为和异常处理。这个阶段还通过寄存器重命名机制打破假依赖，并在执行引擎中实现了其他技巧，以暴露更多的指令级并行性。</p>



<p>LA664 的重命名阶段有 move elimination，对寄存器置零有特殊处理，类似于 x86 CPU 识别原地 XOR 的行为。重命名完全消除了这样的操作，意味着它们不会消耗更下游的执行资源。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>测试项目</td><td>3A6000 IPC</td><td>Zen 4 IPC</td></tr><tr><td>有依赖的move</td><td>2.00</td><td>5.71</td></tr><tr><td>无依赖的move</td><td>3.64</td><td>5.73</td></tr><tr><td>寄存器置零</td><td>5.35 (li.d)</td><td>5.73 (XOR)</td></tr></tbody></table></figure>



<p>龙芯针对寄存器 move 进行了一些优化。有依赖的 move r,r 指令可以每周期执行两条，所以 LA664 有时可以在重命名表中通过指针操作以打破这种依赖。然而，它不能以全速执行move，寄存器 move 仍然需要通过 ALU。</p>



<p>Intel 和 AMD 都有更激进的消除，能够全速消除寄存器 move，无论是否存在依赖。Intel 的 Golden Cove 还可以在重命名阶段消除带小立即数的加法，进一步减轻执行阶段流水的负担。</p>



<h2 class="wp-block-heading">乱序执行</h2>



<p>为了实现乱序执行，重命名和分配阶段需要在必要的队列和缓冲区中找到空位才可以继续。更大的结构允许核心有更大的乱序执行窗口，使其更能隐藏延迟并利用指令级并行性。龙芯 3A6000 拥有非常大型的乱序引擎，这是在 3A5000 基础上的巨大进步。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>结构</td><td>何时需要占用</td><td>LA664 （龙芯 3A6000）</td><td>LA464（龙芯 3A5000）</td><td>Zen3</td><td>Golden Cove</td></tr><tr><td>Reorder Buffer</td><td>&#8211;</td><td>256 项</td><td>128 项</td><td>256 项</td><td>512 项</td></tr><tr><td>整数寄存器堆</td><td>目的寄存器为整数寄存器</td><td>192 项</td><td>128 项</td><td>192 项</td><td>280 项</td></tr><tr><td>向量/浮点寄存器堆</td><td>目的寄存器为向量/浮点寄存器</td><td>256bit 192 项 ，6KB 总容量</td><td>256bit 128 项 ，4KB 总容量</td><td>256bit 160 项 ，5KB 总容量</td><td>512bit 228 项 + 256bit 104 项，16.875KB 总容量</td></tr><tr><td>调度队列</td><td>等待执行</td><td>48 项整数, 48 项浮点, 48 项访存</td><td>32 项整数, 32 项浮点, 32 项访存</td><td>4×24 项整数 ( 其中3个与AGU共享)<br>2×32 项浮点+ 64 项顺序队列</td><td>97 项运算，70 项Load，38 项Store</td></tr><tr><td>Load Queue</td><td>读内存</td><td>80 项</td><td>64 项</td><td>116 项*</td><td>192 项</td></tr><tr><td>Store Queue</td><td>写内存</td><td>64 项</td><td>44 项</td><td>64 项</td><td>114 项</td></tr><tr><td>分支缓冲</td><td>控制流指令</td><td>64 项</td><td>26 项</td><td>48 项 Taken, 117 项 Not Taken</td><td>128 项</td></tr></tbody></table><figcaption class="wp-element-caption">*Zen优化手册显示，Load队列有72项，但核心可以同时处理116个Load。为了与其他架构保持一致，使用了实测的116这一数据。</figcaption></figure>



<p>与 3A5000 相比，诸如寄存器堆和 LSQ 等主要结构至少增加了 25% 的大小。LA464 的分支缓冲看起来太小了，而 LA664 解决了这个问题。LA664 最终拥有与 AMD 的 Zen3 相当的乱序能力。但 Zen3 和 LA664 与 Intel 的 Golden Cove 相比起来仍然小，Golden Cove 有着巨大的 512 项 ROB 和其他更大的结构。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="432" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-12.jpg" alt="" class="wp-image-1659" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-12.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-12-300x169.jpg 300w" sizes="(max-width: 768px) 100vw, 768px" /><figcaption class="wp-element-caption">来自龙芯视频的另一个截图，右侧展示了微结构参数。它们与微基准测试结果一致。</figcaption></figure>



<h3 class="wp-block-heading">SMT实现</h3>



<p>更大的乱序缓冲区对于提高单线程性能至关重要，但会遇到收益边际效应。SMT 通过向操作系统暴露多个逻辑线程，并在这些线程之间分配资源来对抗这种收益递减。因此，一个启用了 SMT 的 CPU 可以保持单线程的高性能，与此同时如果有多个线程活跃，它就像几个较小的核心一样工作。AMD、Intel 和龙芯都决定为每个核心设计两个 SMT 线程。</p>



<p>虽然 SMT 的好处很明显，但实现难度很大。CPU 必须能够动态地在多个线程之间切换。工程师必须决定在两个线程的模式下如何管理各种核心结构。具体来说这种情况下，一个结构的处理方式可以是：</p>



<ul class="wp-block-list">
<li>复制。每个线程获得一份完整的资源。在单线程模式下，第二份资源不使用。这不是一个面积高效的方式，但可能更容易 tuning 和验证。不需要担心线程饥饿，如果第二个线程变得活跃，也不需要清空部分资源。</li>



<li>静态分区。每个线程获得一半的资源。当一个线程处于停止状态时，另一个线程可以使用所有资源，所以这种方法是面积上更高效的。它更难验证，因为当两个线程都工作时，资源需要清空一半并重新配置。但由于将结构切分为两半确保了线程之间某种程度的公平性，所以 tuning 仍然不是太难。</li>



<li>使用 water-mark。在 2T 模式下，一个线程可以占用资源直至 high water-mark。这种做法更灵活、有更多潜在的性能，但 tuning 更难。较高的 water-mark 可能会提高一个线程的性能，但对另一个（饥饿的）线程有严重的影响。</li>



<li>竞争性共享。这就是自由竞争：即使另一个线程活跃，一个线程也可以使用所有的资源。这种情况灵活性和潜在的性能都可以达到最大。例如，如果一个线程运行浮点代码，另一个运行纯整数代码，竞争性共享调度器将允许两个线程用满他们最需要的资源。但 tuning 和验证变得更难。线程饿死可能会更加常见，工程师必须小心避免这种情况。</li>
</ul>



<p>龙芯选择了一个保守的 SMT 实现，其中大多数资源都是静态分区的，包括 ROB、寄存器堆和 LSQ。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="311" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-12.png" alt="" class="wp-image-1662" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-12.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-12-300x121.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>3A6000 的调度器是用 water-mark 的。运算单元调度器的高水位标记似乎在 30 项左右，所以在 3A6000 上运行的线程即使在其 SMT 兄弟线程活跃时也几乎可以使用与在 3A5000 上一样多的调度器大小。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>结构</td><td>3A6000</td><td>Zen 2</td></tr><tr><td>Reorder Buffer</td><td>静态分区<br>每线程约 122 项</td><td>静态分区</td></tr><tr><td>整数寄存器堆</td><td>静态分区<br>每线程约 69 项</td><td>竞争共享（可通过 QoS 等调整）</td></tr><tr><td>向量/浮点寄存器堆</td><td>静态分区<br>每线程约 64 项</td><td>竞争共享（可通过 QoS 等调整）</td></tr><tr><td>Load Queue</td><td>静态分区<br>每线程约 38 项</td><td>竞争共享</td></tr><tr><td>Store Queue</td><td>静态分区<br>每线程约 30 项</td><td>静态分区</td></tr><tr><td>整数调度队列</td><td>Watermarked<br>每线程最大 30 项</td><td>竞争共享</td></tr><tr><td>访存调度队列</td><td>Watermarked<br>每线程最大 36 项</td><td>竞争共享</td></tr><tr><td>浮点调度队列</td><td>Watermarked<br>每线程最大 30 项</td><td>Watermarked<br>每线程最多 64 项 SQ + NSQ （总共 100 项）</td></tr></tbody></table></figure>



<p>AMD Zen2 的重排序缓冲使用静态分区，并对浮点调度队列和非调度队列采用某种水位标记方案。但在其他方面，AMD 采取了非常激进的 SMT 策略。寄存器堆、LSQ 和整数调度队列是竞争性共享的。这可以部分解释Zen2 令人印象深刻的 SMT 收益。</p>



<p>对于龙芯来说，采取一个不那么激进和更容易验证的方法是合理的。3A6000 是他们首个支持 SMT 的 CPU，过于雄心勃勃总是容易导致失败。</p>



<h3 class="wp-block-heading">整数执行</h3>



<p>3A6000 的整数执行单元看起来相比前代变化最少，但调度器容量增加 50% 应该会有更好的 ALU 端口利用率。与 3A5000 一样，3A6000 有四个端口的 ALU 能够执行常见操作。两个端口可以处理分支，两个用于整数乘法。这个配置与 Zen2 大致相似，但龙芯有两个整数乘法单元，而 Zen2 只有一个。Zen2 有更大的总调度容量，但 Zen2 是分布式调度器，与龙芯的统一调度器并不能直接比较。Zen2 上可能会出现其中一个 16 项的队列提前填满，这在重命名阶段将会由于资源不足发生阻塞。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="593" height="404" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-13.png" alt="" class="wp-image-1669" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-13.png 593w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-13-300x204.png 300w" sizes="(max-width: 593px) 100vw, 593px" /></figure>



<p>龙芯在整数除法的吞吐量和延迟上进行了改进，从 3A5000 的每周期 0.11 个指令和 9 个周期的延迟提高到每周期 0.25 个指令和 4 个周期的延迟。除法器的改进是一个奇怪的选择，因为大多数代码会避免使用 DIV 指令，因为它在历史上就非常慢。也许是因为龙芯需要从头开始构建他们的软件生态系统，加速除法操作可能是一个值得的投资。</p>



<h3 class="wp-block-heading">向量和浮点执行</h3>



<p>龙芯的 3A5000 拥有 256 位的向量能力和 LASX 扩展，但实现较为保守，只有两个 256 位执行单元。3A6000 彻底改进了FPU，它现在有四个执行单元。所有四个管道都可以处理 256 位向量加法，这给 3A6000 提供了非常强大的浮点性能。竞品的 x86 CPU 只能每个周期执行两个 256 位向量加法。256 位向量乘法和基础向量整数操作性能与 Zen2 相似。</p>



<p>奇怪的是，标量浮点操作没有得到同样的增强。只有两个单元可以处理标量浮点加法。更奇怪的是，标量浮点乘法似乎和向量乘法使用不同的流水线。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="577" height="526" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-14.png" alt="" class="wp-image-1673" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-14.png 577w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-14-300x273.png 300w" sizes="(max-width: 577px) 100vw, 577px" /></figure>



<p>即使龙芯增加了额外的浮点单元，融合乘加（FMA）的峰值吞吐量还是保持不变。LA464 和 LA664 都可以每个周期执行一个 FMA 操作，它们的 FMA 吞吐量是 AMD 的 Zen2 或 Intel 的 Skylake 的一半。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>指令</td><td>LA664</td><td>LA464</td><td>Zen3</td><td>Golden Cove</td></tr><tr><td>256bit FP64 向量加法吞吐量</td><td>4 per cycle</td><td>2 per cycle</td><td>2 per cycle</td><td>2 per cycle</td></tr><tr><td>256bit FP64 向量加法延迟</td><td>3 cycle latency</td><td>5 cycle latency</td><td>3 cycle latency</td><td>3 cycle latency</td></tr><tr><td>256bit FP64 向量乘法吞吐量</td><td>2 per cycle</td><td>2 per cycle</td><td>2 per cycle</td><td>2 per cycle</td></tr><tr><td>256bit FP64 向量乘法延迟</td><td>5 cycle latency</td><td>5 cycle latency</td><td>3 cycle latency</td><td>4 cycle latency</td></tr><tr><td>256bit FP64 向量 FMA 吞吐量</td><td>1 per cycle</td><td>1 per cycle</td><td>2 per cycle</td><td>2 per cycle</td></tr><tr><td>256bit FP64 向量 FMA 延迟</td><td>5 cycle latency</td><td>5 cycle latency</td><td>4 cycle latency</td><td>4 cycle latency</td></tr></tbody></table></figure>



<p>除了吞吐量外，龙芯改善了执行延迟。浮点加法的延迟为 3 个周期，与 Zen3 相等。然而，AMD 的 Zen3 和 Intel 的 Golden Cove 仍然有更低的浮点执行延迟。特别是 Intel，它可以在 2 个周期延迟内完成浮点加法，而且还是以更高的时钟频率运行。</p>



<p>和整数调度队列一样，浮点调度队列容量增加了 50% 至 48 项。单独就这一点来说可能就已经提供了比增加执行单元更多的浮点性能提升，两者结合使 3A6000 在向量和浮点工作负载中表现强大。</p>



<p>AMD 的 Zen2 也有一个四流水线 FPU，并且也在所有四个单元处理基础向量整数操作。然而，它使用一个聪明的非调度队列，即使它的 36 项调度队列填满，也能让后端保持更多的inflight浮点或向量操作，尽管它不能利用这些指令来找到额外的指令级并行性。</p>



<h3 class="wp-block-heading">地址生成</h3>



<p>龙芯在 LA664 中相比于 LA464 显著强化了地址生成功能。LA464 拥有两个通用的 Load/Store 单元。LA664 将这分为两个 Load 和两个 Store 单元。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="640" height="308" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-15.png" alt="" class="wp-image-1680" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-15.png 640w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-15-300x144.png 300w" sizes="(max-width: 640px) 100vw, 640px" /></figure>



<p>这意味着，LA664 每周期可以处理比 Zen2 更多的标量内存操作，与 Golden Cove 处理的内存操作量相同。Zen3 和 Golden Cove 在灵活性方面稍微领先于 LA664，可以每周期发出 3 个 Load。但 LA664 的 2 Load 每周期已经是从 LA464 的 1 Load 每周期的大幅升级。在 Store 方面，LA664、Zen3 和 Golden Cove 都是每周期 2 次标量 Store，而 LA464 只能每周期处理一个 Store。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td></td><td>LA664</td><td>LA464</td><td>Zen3</td><td>Golden Cove</td></tr><tr><td>每周期标量 Load</td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td>每周期标量 Store</td><td>2</td><td>1</td><td>2</td><td>2</td></tr><tr><td>AGU 数量（标量内存操作发射宽度）</td><td>4</td><td>2</td><td>3</td><td>4</td></tr></tbody></table></figure>



<p>LA664 可以每周期处理两个 256 位向量访存，这些访存可以是任意 Load/Store 组合。</p>



<h3 class="wp-block-heading">内存序</h3>



<p>一旦地址计算完毕，Load/Store 单元必须确保内存访问符合 ISA 的内存模型。Load 可能需要从之前的 Store 中获取结果。与之前的 3A5000 一样，3A6000 可以在大约 7 个周期的延迟中处理 Load 被包含于之前的 Store 的情况。只有部分是重叠的情况会产生 14 个周期的惩罚，可能是因为 Load 被阻塞直到 Store 退休并写回到 L1d。</p>



<p>Zen2 具有相同的 7 周期转发延迟和 14 周期的部分重叠惩罚。但 Zen2 可以以一个额外周期的惩罚处理 64B 缓存行间转发，而龙芯在这种情况下表现不佳。由于 Zen2 的时钟频率更高，所以龙芯 7 周期的转发延迟感觉有些长。Goldmont Plus 的目标频率也是 2.xGHz 接近 3GHz 的范围，它就是 5 周期的 Store-Load 转发延迟以及转发不能处理时的 10 周期延迟。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="493" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1024x493.jpg" alt="" class="wp-image-1684" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1024x493.jpg 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-300x145.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-768x370.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16.jpg 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">使用 Henry Wong 的方法测量的 Store-Load 转发延迟，代码由 Clam 编写。</figcaption></figure>



<p>LA664 的 Store-Load 转发行为看起来与其前代相似。但 LA664 消除了非对齐 Store 的 10 周期惩罚，将其降低到仅 3 周期。这比 Zen2 稍好，Zen2 对于非对齐的 Store 需要 2 到 5 个周期的处理时间。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="496" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1-1024x496.jpg" alt="" class="wp-image-1688" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1-1024x496.jpg 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1-300x145.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1-768x372.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1.jpg 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>跨越 64B 边界的 Store-Load 转发仍然处理不佳，但惩罚从 31 周期降低到了可以接受的 21 周期。</p>



<h2 class="wp-block-heading">缓存和内存访问</h2>



<p>良好的缓存和内存层次结构对于任何现代高性能 CPU 的数据供给至关重要。3A6000 保留了类似的缓存层次结构，但在各个点上有细微的改进。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="250" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1024x250.png" alt="" class="wp-image-1691" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-1024x250.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-300x73.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16-768x188.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-16.png 1146w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>由于龙芯未能提高时钟速度，他们通过减少缓存访问路径中的流水线长度来降低延迟。</p>



<h3 class="wp-block-heading">延迟</h3>



<p>L1d 延迟从四个周期降低到三个周期。我认为低频 CPU 的设计目标应该是 3 周期 L1d 延迟，很高兴看到3A6000 实现了这一点。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="571" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-17-1024x571.png" alt="" class="wp-image-1692" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-17-1024x571.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-17-300x167.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-17-768x428.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-17.png 1093w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>许多现代 CPU 使用 L2 作为中级缓存，使 L1 缺失不需要到相对高延迟的 L3 中查询。3A6000 继续使用256KB L2 缓存，类似于较早的 Intel 架构。最近的 AMD 和 Intel CPU 趋向于使用更大的 L2 缓存。Zen4 已经转向使用 1MB L2 缓存，而 Intel 的 Raptor Lake 选择了巨大的 2MB L2。尽管龙芯未能实现更大的 L2，但他们将延迟从 14 个周期降低到 12 个周期。假设 L1 以同样的速度处理命中和缺失的话，L1 到 L2 的路径上可能只减了一拍。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>内存层级</td><td>3A6000</td><td>3A5000</td><td>Zen 2 (3950X)</td></tr><tr><td>L1d</td><td>64 KB<br>3 周期延迟</td><td>64 KB<br>4 周期延迟</td><td>32 KB<br>4 周期延迟</td></tr><tr><td>L2</td><td>256 KB<br>12 周期延迟</td><td>256 KB<br>14 周期延迟</td><td>512 KB<br>12 周期延迟</td></tr><tr><td>L3</td><td>16 MB<br>38 周期延迟</td><td>16 MB<br>40 周期延迟</td><td>16 MB<br>37 周期延迟</td></tr><tr><td>DRAM</td><td>104.19 ns<br>~263 周期延迟</td><td>144.52 ns<br>~361 周期延迟</td><td>77.25 ns<br>~341 周期延迟</td></tr></tbody></table></figure>



<p>Zen2、3A6000 和 3A5000 都有一个四核共享的大型 16MB L3 缓存。3A6000 减少了几个周期的 L3 延迟，尽管这可能是因为查询 L2 加快了两个周期。</p>



<p>最后，DRAM 延迟从 144ns 改进到了 104ns。3A5000 的 DDR4 控制器很糟糕。它只是恰好被较低的时钟频率“拯救”了，使得它对 IPC 的影响没那么大。3A6000 获得了大大改善后的内存控制器。104ns 还是不够好，但以周期数计的话，它的延迟降低到了比高时钟频率的 3950X 要低。因此 3A6000 至少通过减少 DRAM 访问延迟的周期数来缓解了它的低时钟频率劣势。然而，104ns 对于一个单片 DDR4-2666 的配置来说并不是很好。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="571" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-18-1024x571.png" alt="" class="wp-image-1695" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-18-1024x571.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-18-300x167.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-18-768x428.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-18.png 1093w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>尽管 3A6000 在延迟的周期数上具有竞争力，我们必须考虑实际的延迟，因为龙芯无法将其 CPU 的时钟频率提升到现代 AMD 和 Intel CPU 运行的水平。这并不是对 LA664 有利的情况。在包括 L1 在内的每一缓存它都较慢。这解释了为什么 LA664 在拥有更多乱序能力和更高 IPC 的情况下，仍然稳定地输给 Zen2。</p>



<h3 class="wp-block-heading">带宽</h3>



<p>带宽对于向量化、多线程应用程序尤其重要。3A6000 在很大程度上继承了它前身的内存层次结构，但龙芯再次在各个地方进行了改进。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="639" height="186" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-19.png" alt="" class="wp-image-1697" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-19.png 639w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-19-300x87.png 300w" sizes="(max-width: 639px) 100vw, 639px" /></figure>



<p>3A5000 已经拥有与 Intel 的 Skylake 或 AMD 的 Zen2 相似的每周期 L1d 带宽。3A6000 通过加倍写入带宽进行了改善。基本上 3A6000 的 L1d 可以每周期完成两次 256 位访问，无论是读取还是写入。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="877" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-20-1024x877.png" alt="" class="wp-image-1698" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-20-1024x877.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-20-300x257.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-20-768x657.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-20.png 1063w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>因此，尽管时钟频率较低，3A6000 拥有令人印象深刻的 L1d 写入带宽。LA664 因此成为了和 Golden Cove 一样唯二的两个具有每周期 512 字节存储带宽的消费级 CPU。</p>



<p>LA664 的 256KB L2 表现与其前身基本类似，每周期读带宽为 21-22 字节，写带宽相同。因此，3A6000 的 L2 带宽仍然低于 AMD 或 Intel 最近的任何 CPU。与 Intel 的 CPU 相比差距特别大，后者有每周期 64字节的 L2 带宽。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="447" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-21-1024x447.png" alt="" class="wp-image-1700" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-21-1024x447.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-21-300x131.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-21-768x336.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-21.png 1213w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>在 L3 中，LA664 比其前代增加了 33％ 的带宽。每周期从 L3 获取 18.7 字节的带宽让龙芯可以对抗较老的 Intel CPU，但 AMD 特别强大的 L3 仍然遥遥领先。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="533" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-22-1024x533.png" alt="" class="wp-image-1701" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-22-1024x533.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-22-300x156.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-22-768x400.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-22.png 1134w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>设计共享缓存引入了额外的挑战，因为缓存带宽在服务多个客户时还要增加。当所有硬件线程都活跃时，3A6000 可以给每个核心每周期提供 16.55 字节。这比单个核心在没有其他核心竞争的情况下只少了一点点，是非常好的表现。旧的 3A5000 在所有四个核心加载时只能为每个核心每周期提供 10 字节多一点，与单一核心的 13.7 字节相比。这表明 3A5000 的 L3 结构存在竞争，而 3A6000 在很大程度上解决了这个问题。AMD 在多个核心一起冲击缓存时再次提供了出色的每核心 L3 带宽，每核心每周期超过 24 字节。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="688" height="472" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-24.png" alt="" class="wp-image-1706" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-24.png 688w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-24-300x206.png 300w" sizes="(max-width: 688px) 100vw, 688px" /></figure>



<p>龙芯的 3A5000 有一个极其糟糕的 DDR4 控制器。幸运的是，3A6000 有一个更好的控制器。技术上它支持 DDR4-3200，但我们在使用这个速度运行双通道内存时无法稳定启动。当装配双通道 DDR4-2666 时，3A6000 实现的 DRAM 读带宽大致与 Core i5-6600K 相当。这个酷睿芯片的第一代 DDR4 控制器只能在完全稳定的情况下处理 DDR4-2133 的速度（至少在我的样本上是这样），但仍然比 3A6000 用更快的内存实现更好的 DRAM 读带宽。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="573" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-1024x573.jpg" alt="" class="wp-image-1707" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-1024x573.jpg 1024w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-300x168.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-768x430.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-25.jpg 1149w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>尽管 3A6000 的 DRAM 读取性能一般，但它也有一个小 trick：当检测到缓存行被完全覆写而且没有对其先前内容的依赖时，可能会避免获取这个缓存行的所有权。这可能有助于某些访问模式，但好处可能会受限，因为程序通常有更多的读取而不是写入操作。</p>



<h2 class="wp-block-heading">核心到核心延迟</h2>



<p>这个测试使用原子比较和交换操作在核心之间传递值。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="356" height="198" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-23.png" alt="" class="wp-image-1702" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-23.png 356w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-23-300x167.png 300w" sizes="(max-width: 356px) 100vw, 356px" /></figure>



<p>龙芯的性能没有带来任何惊喜（惊吓），这是一件好事。</p>



<h2 class="wp-block-heading">结语</h2>



<p>龙芯的工程师有许多值得骄傲的地方。设计与 Zen2 相当的分支预测器并不容易，考虑到龙芯在 3A5000 中的表现，这一成就更显卓越。同样地，SMT 非常难以实现正确。龙芯在大幅扩展 3A5000 的乱序引擎和修复其 DDR4 控制器的同时，设法完成了这两件事。这些巨大的性能提升使 LA664 与 Zen1 在单核性能上相当。</p>



<p>3A6000 是中国为使其经济减少对外国 CPU 依赖所做努力的一部分。在这方面，3A6000 是向前迈出的一步。Zen1 在今天仍然非常可用，所以中国消费者可能会发现 3A6000 的性能对于轻量级日常任务已经是可以接受的了。龙芯的软件生态系统比性能更影响芯片的可用性。</p>



<p>但龙芯还有一个成为世界级 CPU 制造商的次级目标，与西方公司如 Intel 和 AMD 并肩。在这方面，他们还有很长的路要走。Zen1 级别的单线程性能值得赞扬。但我们必须记住，Zen1 之所以能够从 Intel 那里夺取市场份额，是因为它将便宜的的 6 核和 8 核产品带入了消费者平台，而不是因为它能够以核心对核心的方式赢得 Skylake。3A6000 只是一个四核产品，因此缺少了 Zen1 最大的优势。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="779" height="518" src="https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-1.jpg" alt="" class="wp-image-1711" srcset="https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-1.jpg 779w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-1-300x199.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2024/03/image-25-1-768x511.jpg 768w" sizes="(max-width: 779px) 100vw, 779px" /><figcaption class="wp-element-caption">来自&nbsp;<a href="https://mp.weixin.qq.com/s/dMXDPrsCS4rOj7AOPyYOcg">https://mp.weixin.qq.com/s/dMXDPrsCS4rOj7AOPyYOcg</a></figcaption></figure>



<p>龙芯还将 3A6000 与 Intel 的 Core i3-10100 相比较——一个带有 6MB 缓存和 4.3 GHz 睿频时钟的四核 Skylake 产品。虽然从名义上看是第 10 代产品，i3-10100 更类似于 2015 年的 Core i7-6700K。Intel 的第 10 代更为人所知的是将 10 核 CPU 带入消费者阵容，而 6 核和 8 核构成中端产品。除了更多核心，像 i5-10600K 和 i7-10700K 这样的部件还享有更高的睿频频率。3A6000 将无法与这些产品竞争。针对同一时代的 Zen2 产品，龙芯也将遇到挑战。我们已经看到，3950X 在相同核心数量的测试中轻松超过 3A6000。当 Zen2 的更多核心开始发挥作用时，这一差距将只会变得更大。</p>



<p>今天，Intel 的 Golden Cove 衍生产品和 AMD 的 Zen4 在 3A6000 之上还有更大的领先优势。四核产品在 AMD 和 Intel 的消费者产品阵容中已经几乎消失。龙芯的 3A6000 可能是我们从中国看到的最有前途的 CPU，远比围绕 A72 进行的笨拙尝试更加令人兴奋。但龙芯的工程师们仍有大量工作要做。我们期待看到他们接下来能够实现什么。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文翻译自 <a href="https://chipsandcheese.com/" target="_blank" rel="noopener" title="">ChipsAndCheese</a>，原文为英文，原文链接：<a href="https://chipsandcheese.com/2024/03/13/loongson-3a6000-a-star-among-chinese-cpus/" target="_blank" rel="noopener" title="">https://chipsandcheese.com/2024/03/13/loongson-3a6000-a-star-among-chinese-cpus/</a> 原作者：clamchowder</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文中文翻译首发自我的博客 <a href="https://blog.eastonman.com/blog/2024/03/oongson-3a6000-a-star-among-chinese-cpus/" target="_blank" rel="noopener" title=""></a><a href="https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/" target="_blank" rel="noopener" title="">https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/</a> ，翻译已取得 ChipsAndCheese 编辑/原作者授权。因英文原文无公开的授权协议，本译文也禁止转载，如需转载请先联系我或 ChipsAndCheese。</p>
</blockquote>The post <a href="https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/">龙芯3A6000：国产CPU中的一颗明星</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2024/03/loongson-3a6000-a-star-among-chinese-cpus/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>HotChips 2023: Ventana 不寻常的 Veyron V1</title>
		<link>https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/</link>
					<comments>https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Sat, 10 Feb 2024 15:25:09 +0000</pubDate>
				<category><![CDATA[ChipsAndCheese 翻译]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[RISC-V]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=1454</guid>

					<description><![CDATA[<p>近年来 RISC-V 正在变得越来越火热。CPU IP 设计商中 SiFive 已经进入到高性能的范围中了。阿里巴巴的平头哥一直在开发 RISC-V 芯片，以期发展成为一条可行的本土服务器 CPU 产品线。现在，Ventana 也加入了这场派对，带来了他们的 Veyron V1 核心。这篇文章将分析 Veyron V1 的设计，并特别关注一些 Veyron V1 的不寻常特性。</p>
The post <a href="https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/">HotChips 2023: Ventana 不寻常的 Veyron V1</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 24 分钟</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文大部分翻译自 <a href="https://chipsandcheese.com/" target="_blank" rel="noopener" title="">ChipsAndCheese</a>，添加有个人理解和资料补充 ，原文为英文，原文链接：<a href="https://chipsandcheese.com/2023/09/01/hot-chips-2023-ventanas-unconventional-veyron-v1/" target="_blank" rel="noopener" title="">https://chipsandcheese.com/2023/09/01/hot-chips-2023-ventanas-unconventional-veyron-v1/</a> 原作者：<a href="http://chlamchowder" target="_blank" rel="noopener" title="">chlamchowder</a></p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文中文翻译首发自我的博客 <a href="https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/" target="_blank" rel="noopener" title="">https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/</a> ，翻译已取得 ChipsAndCheese 编辑/原作者授权。因英文原文无公开的授权协议，本译文也禁止转载，如需转载请先联系我或 ChipsAndCheese。</p>
</blockquote>



<h2 class="wp-block-heading">总体设计</h2>



<p>Veyron V1 是一个八发射的乱序核心（这里一般指重命名/分派宽度）。分支误预测惩罚是 15 周期，乱序调度队列容量十分可观。与 ARM 和 SiFive 的设计类似，Veyron V1 以适中的时钟频率换取较低的面积和功率，这使它能扩展到非常多的核心。Ventana 给 Veyron V1 的目标频率是 3.6GHz，但 Veyron V1 还可以降频以减少功耗，例如在 2.4GHz 时，核心的功耗少于 0.9W。</p>



<p>译者：总体来说 8 宽度的重命名可以做到 3.6GHz 的最高频率，2.4GHz 下功耗小于 0.9W，这个设计能力并不算最好，但鉴于 RISC-V 各家都没有太多的基础，在较短时间内能迭代到这样的效果也属于不错了。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="761" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-1024x761.png" alt="" class="wp-image-1463" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-1024x761.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-300x223.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-768x571.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image.png 1130w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">根据 Vetana 演讲中信息绘制的 Veyron V1 的框图</figcaption></figure>



<h2 class="wp-block-heading">流水线设计</h2>



<p>乍一看，Veyron V1 的流水线深度可能会显得比较深（15 周期的分支误预测惩罚）。作为比较，AMD Zen4 的最小分支误预测惩罚是 11 周期，ARM 的 Neoverse N2 的流水线深度是 10 周期（我们假设 ARM 在之前的 HotChips 演讲中提到的流水线深度就是指最小分支误预测惩罚）。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="575" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-1-1024x575.png" alt="" class="wp-image-1465" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-1-1024x575.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-1-300x169.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-1-768x431.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-1.png 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Ventana 在 HotChips 2023 中的演示，此处是 V1 的流水线</figcaption></figure>



<p>Ventana 表示他们新版的 V2 架构改善了分支误预测惩罚，在他们的演示中也提到了一点。在整数执行阶段1（IX1）检测到的误预测会触发在RPS（Restart Pipe Start）阶段的流水，和 IX1 重叠。这个周期离标记为 PNI 的预测流水有两个周期的距离。PNI大概率表示 Predict Next IP，意思是生成 Next Instruction Pointer 的地方。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="121" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-2-1024x121.png" alt="" class="wp-image-1466" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-2-1024x121.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-2-300x36.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-2-768x91.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-2.png 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">添加了分支误预测重定向通路的流水线</figcaption></figure>



<p>通过比较实际的分支方向与预测的方向或比较跳转的目标，可以检测出分支误预测。如果直接将真实的目标地址或正确方向反馈到分支预测器中，应该能让流水线在 PNI 之后立即重新启动，将误预测惩罚减少两个周期（降至13个周期）。</p>



<h2 class="wp-block-heading">分支预测器：单级 BTB</h2>



<p>像其他一些现代 CPU 一样，Veyron V1 使用了解耦前端（分离式前端）。同时，解耦前端的分支预测结果也充当了准确的指令预取器。为了最大限度地利用这种技术，预测器必须能够同时且准确地跟踪多个分支目标。因此大的分支目标缓冲（BTB）对于分支预测器而言至关重要，这可以让分支预测器无需从指令缓存中获取指令后才能解码出跳转的目标，降低了延迟。</p>



<p>但是无论是存储分支目标还是直接存储指令数据，大而快的缓存都难以制造。因此现代 CPU 一般选择多级 BTB。通常快速的一级 BTB 能够背靠背地预测跳转的分支，而较慢的下级 BTB 则有助于保持在较大分支足迹下的良好性能。像 Intel 就有 3-4 级的 BTB 结构。</p>



<p>尽管在速度、容量和复杂性之间通常需要权衡，Ventana 说“我全都要”。V1 有一个 12K 项的巨大单级 BTB，和一个“单周期 next-line 预测器”。与 Berkerly 的 BOOM 相似，“next-line 预测器”包括有 BTB 查询，因此如果按照 Ventana 演示的说法，12K 项的 BTB 是可以做到背靠背地预测跳转分支的。这是相当了不起的成就，这让 V1 能够以最小的吞吐量损失处理非常大的分支足迹。对比之下，以 Intel 的 Golden Cove 为例，它也有一个 12K 条目的 Last-level BTB，但是延迟为三个周期。</p>



<p>译者：这可能和 Intel 的目标频率较高有关系，但是 Ventana 的单级 BTB 设计还是不太寻常，也许和尚未完全挖掘工艺制程的潜力有关。我猜测 Ventana 使用这样的设计仅是因为没有做更好的设计，巨大的单级 BTB 还会带来比较大的功耗开销，毕竟每周期都需要开启。结合 Ventana 提供的功耗数据，我觉得他们的低功耗设计还有很大的空间。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="556" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-3-1024x556.png" alt="" class="wp-image-1484" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-3-1024x556.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-3-300x163.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-3-768x417.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-3.png 1279w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Ventana 在 HotChips 2023 上的演示文稿</figcaption></figure>



<p>如果 BTB 未提供正确的目标，V1 声称能做到“three-cycle redirect on mispredict”。这可能意味着静态预测器（预译码）距离指令取指有三个周期，或者在解码的第一周期，因为 V1 的指令缓存延迟为两个周期。在有这么大的 12K 项 BTB 的情况下，这样的静态预测器（预译码）重定向应该会比较罕见。</p>



<h2 class="wp-block-heading">前端：512KB L1/L2指令缓存</h2>



<p>除了 BTB 的层级，Ventana 也减少了指令缓存层级。Veyron V1 有一个大的 512KB 指令缓存，充当 L1/L2 指令缓存的角色。一个小型循环缓冲区位于指令缓存的上方，但这主要是对密集循环的 Power 优化。V1 预计大部分时间都会从 512KB 的 L1/L2 缓存中获取指令。</p>



<p>Veyron V1 的前端设计适合于大指令足迹的软件，这些足迹溢出了我们通常看到的 32KB 或 64KB 的指令缓存。在这种情况下，其他 CPU 会浪费功耗和时间在检查 L1i 上，并需要在两级缓存之间交换指令数据。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="330" height="311" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-4.png" alt="" class="wp-image-1492" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-4.png 330w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-4-300x283.png 300w" sizes="(max-width: 330px) 100vw, 330px" /><figcaption class="wp-element-caption">Veyron V1 的 floorplan，红框是指令和数据 L2 缓存</figcaption></figure>



<p>V1 的设计使用强制性的 L2 分区，牺牲了我们在其他 CPU 上看到的缓存容量灵活性。如果程序是少量代码在大量数据上循环，一个统一的 1MB L2 可以几乎使用其所有容量来存储数据。或者，如果一个应用的数据足迹很小，但代码足迹很大，则可以为指令缓存分配更多容量。</p>



<p>如果 Ventana 尝试使用一个统一的 L2 缓存，取指的流量可能会与数据侧的 L2 访问引起冲突。CPU 的前端是缓存带宽的重要消费者，因为每条指令都必须经由前端访问下级缓存系统来获得，但不是每条指令都会有数据侧的内存访问。此外，不完美的分支预测会导致在一个误预测分支之后的指令有多于一个的前端内存访问（因为第一次尝试取错了东西）。假设一个统一的 1MB 缓存充当 L1/L2 指令缓存和 L2 数据缓存，可能会遇到指令和数据访问之间的冲突。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="518" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-5-1024x518.png" alt="" class="wp-image-1497" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-5-1024x518.png 1024w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-5-300x152.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-5-768x388.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-5.png 1313w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">即使是一个不怎么高性能的 CPU 也需要大量的供指带宽</figcaption></figure>



<p>因此，Veyron V1 的 L2i 和 L2d 缓存各自需要足够的容量，以便在广泛的工作负载中都能充当有效的中层缓存。一个 512+512KB 的 L2 设置无疑会提供比统一的 512KB L2（如 AMD 的 Zen1 到 Zen3 中的）更好的缓存容量，但与 AMD 的 Zen4（以及一堆 ARM 的设计）上的统一 1MB L2 相比，可能会比较差。</p>



<p>译者：这样的设计也比较激进，这种设计的思路来源是依靠 BTB 存储信息的密度优势，减少靠近流水线部分的 memory 数量，然后依靠预取来掩盖较远的 L2 的延迟。但是这样设计也有很多不好解决的问题，例如下面提到的非对齐取指。</p>



<h2 class="wp-block-heading">非对齐取指支持</h2>



<p>如果你仔细看 Ventana 的演示文稿，他们宣称支持&#8221;每周期最多完整 64B 的的非对齐取指&#8221;。通常来说前端缓存访问是对齐的，也就是说向指令缓存发送的地址中最低的几位是零。例如，一个 32B 对齐的取指会隐式地将五个最低地址位归零。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="1025" height="279" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-6.png" alt="" class="wp-image-1500" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-6.png 1025w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-6-300x82.png 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-6-768x209.png 768w" sizes="(max-width: 1025px) 100vw, 1025px" /><figcaption class="wp-element-caption">非对齐的前端取指是什么，为什么可以提高供指带宽</figcaption></figure>



<p>支持非对齐的取指是挺不错的，因为即使分支跳转到一个缓存行的中间，也可以维持高取指带宽。但不知道 Ventana 是如何实现这一点的。通常，非对齐访问是通过在底层进行两次访问并合并输出的数据来处理的。数据缓存通常有足够的 SRAM 带宽，可以每个周期这样做。但是一个指令缓存能够做到，尤其是一个512KB的缓存，感觉有点过分。</p>



<p>译者：这里就发现 Ventana 这种设计的问题了，目前各家 CPU 支持非对齐的取指都是通过较细粒度的分 bank 来实现，比如 Apple 和 AMD。然而 L2 通常需要追求高密度，做太细粒度的分 bank 不太合适。观察 Ventana 放出的 floorplan，他们 L2 的分 bank 粒度也较粗，猜测可能就是 64B 或者更大。这样会带来新的功耗问题，因为每周期都需要访问两个这么大的 SRAM，还需要在前端处理 128B 的裁切，这个开销可不小。这个其实也和 Ventana 给出的功耗数据相印证，说明他们的功耗还有很大的优化空间。</p>



<h2 class="wp-block-heading">巨大的单级 TLB </h2>



<p>接着减少缓存层级的主题，Ventana 选择了单级指令和数据 TLB。每个都有 3K 项，与其他家的大 L2 TLB 容量相匹配。作为比较，AMD 的 Zen4 有一个3072 项的 L2 dTLB，而英特尔的 Golden Cove 有一个 2048 项的 L2 TLB。 TLB（地址翻译缓冲器）是虚拟地址到物理地址转换的缓存。在你的电脑上运行的程序通过虚拟地址访问内存，这些地址通过操作系统管理的页表映射到物理地址。通常，每个内存访问都需要一个地址转换，因此 CPU 有一个小但高度优化的L1 dTLB，它记住了最常访问页面的转换。</p>



<p>在指令方面，512KB 的 L1/L2 指令缓存的访问需要同时访问 L1 iTLB。这意味着大多数指令访问将会引起大 iTLB 的查找。因为代码通常顺序执行直到遇到分支，所以一个单读口的 TLB 应该足以满足多条指令。</p>



<p>在数据方面，Ventana 删除了L1 dTLB，并且采用了虚拟地址 L1 DCache。主 TLB 在通向 L2 的路上被访问，使其不需要像传统L1 dTLB那样对延迟和功耗敏感。</p>



<p>译者：这个其实也很不利于功耗的控制，Ventana 看起来就和香山的开发团队一样，首先追求性能，然后才是功耗面积等。这也不难理解，毕竟现在在 PPT 上性能最吸引人。</p>



<h2 class="wp-block-heading">VIVT 的 L1 数据缓存</h2>



<p>Veyron V1 有一个 64KB 的数据缓存。它是虚拟地址索引和虚拟地址标记的（VIVT），与其他CPU中使用的虚拟索引、物理标记的（VIPT）缓存不太一样。VIPT 缓存的重要之处在于它的行为和纯粹的物理地址缓存是一样的。传统上，虚拟地址的缓存有很多缺点：它们会在上下文切换或 TLB 维护操作时被刷新，因为更改虚拟地址到物理地址的映射意味着一个虚拟地址的缓存的内容不再是最新的。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="431" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-7.png" alt="" class="wp-image-1503" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-7.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-7-300x168.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>然而，Ventana 尝试通过在 L1d 标签中跟踪 ASID 来避免这些缺点。这让他们可以避免在上下文切换时刷新 L1 数据缓存。此外，L1d 可以处理多个虚拟地址映射到同一个物理地址的情况。也许 V1 在 L2 TLB 中记录一定数量的 VA 到 PA 别名，并在 snoop 请求出现时一次性使这些别名失效。</p>



<p>V1 的 L1d 仍然有四个周期的延迟，与 AMD 和 ARM 近期的 CPU 非常相似。他们没有从使用虚拟地址的 L1d 中获得任何延迟优势，但他们避免了维护 L1 dTLB 的功耗和面积开销。Ventana 在使用 VIVT 缓存上面临不少质疑，但我认为他们会没问题的。他们显然已经努力避免了虚拟地址缓存带来的最大问题。即使这种做法最终的缺失率高于同等大小的 VIPT L1d，那么他们 L2 的命中延迟其实也相对较低，只有 11-12 个周期，不会有太大的问题。</p>



<p>译者：这个 VIVT 缓存肯定是引起很多质疑的，因为历史上使用 VIVT 缓存的设计好像都活得不久。现在的操作系统似乎也很少认为修改页表会引起数据缓存上的开销。更别说这种设计还需要新的一致性处理机制，我猜测他们的验证成本会比较高。</p>



<p>译者：我还是很难理解 Ventana 这个减少各种 memory 的层级的设计，感觉并没有给他们带来什么可观的好处。如果他们的 L1d 延迟能进一步降低（降低一周期），我觉得用 VIVT 缓存 + 单级 TLB 就可以说是值得的。但是他们大费周章最终并没有体现在性能上，这就让我感觉不太值得。也许他们这个结构还有降低一拍的潜力，只是还未解决工程上的问题。 </p>



<h2 class="wp-block-heading">乱序执行引擎</h2>



<p>通常 CPU 会有多种执行端口。这是因为不同的执行单元专门用于不同的操作，而且足够的执行端口必须为 CPU 可能遇到的不同指令组合提供服务。Ventana 选择使用更少、更灵活（功能更多）的端口。尽管重命名宽度是8，Veyron V1 只有四个整数和访存共用的执行端口。直觉上，核心的吞吐量会受到缺乏执行资源的限制。</p>



<p>然而，即使前端的宽度很宽，执行资源很少被持续完全利用，因为在今天的高性能 CPU 中，吞吐量通常受到内存延迟的限制。Sandy Bridge 是一个 Intel 的例子，它的端口数量也相对较少。即便有时会遇到端口争用，也是因为某些工作负载压迫了特定端口后面的执行单元，而不是CPU整体的执行端口数量不足。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="688" height="355" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-8.png" alt="" class="wp-image-1518" style="width:850px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-8.png 688w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-8-300x155.png 300w" sizes="(max-width: 688px) 100vw, 688px" /></figure>



<p>因此，Veyron 出于减少功耗和面积的考虑，选择使用四个被充分利用的整数执行端口，而不是更多利用率较低的执行端口。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="768" height="113" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-9.png" alt="" class="wp-image-1519" style="width:1020px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-9.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-9-300x44.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>虽然 Veyron 的执行端口布局在 HotChips 上肯定引起了一些关注，但我不认为它有什么不寻常的。其他 CPU 通过让端口服务不同的指令类别也减少了执行端口的数量。英特尔长期以来将浮点和整数执行单元放在同一个端口后面。富士通的 A64FX 也有两个端口同时服务 AGU 和 ALU。</p>



<p>这种方法的好处也很明显。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>（Zen4 的向量寄存器堆）自寄存器堆的面积主要受读写端口的宽度和数量限制，而不是存储的限制以来，仅有很小的增长。 </p>
<cite>Kai Troester 在 HotChips 2023 上，关于 Zen4 的 AVX-512 实现</cite></blockquote>



<p>更多的端口意味着要么有更多的调度队列，要么使用更复杂的调度器，可以每周期选择多个操作。寄存器堆的面积是另一个考虑因素，更多的执行单元/发射端口就需要更多的寄存器堆端口来满足它们。避免这种面积增大是一个明智的举动。</p>



<h2 class="wp-block-heading">面积</h2>



<p>面积对 Ventana 来说也是重要的，因为他们的目标是高核心数量，减少面积有助于降低成本。Veyron V1 的面积小于 AMD 的 Zen4c 和 ARM 的 Neoverse V2。Ventana 平衡面积的设计决策在此起了作用，但 Veyron V1 为了减少面积使用也牺牲了性能：它不支持向量扩展，仅有一个标量浮点流水。相比之下，AMD 的 Zen4c 具有非常高的向量和标量浮点吞吐量。与 Neoverse V2 相比，Veyron V1 的 L2缓存容量较小。Ventana 无疑实现了良好的核心面积效率，但其非传统的设计决策使它至少在面积方面不是 Game Changer。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="768" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-10.jpg" alt="" class="wp-image-1521" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-10.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-10-300x300.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-10-150x150.jpg 150w" sizes="(max-width: 768px) 100vw, 768px" /><figcaption class="wp-element-caption">与其他在 HotChips 2023 上展示的 CPU 核的对比</figcaption></figure>



<p>ARM 的 Neoverse N2 是另一个有趣的比较。它不如 Veyron V1 宽，具有较小的 BTB 和 TLB。V1 可能在整数应用上表现得更好，特别是如果那些代码具有较大的指令侧内存占用。但 N2 也有优势，比如一个非常基础的 2×128b 向量流水配置。</p>



<h2 class="wp-block-heading">Cluster 面积</h2>



<p>核心面积只是一部分，最近的手机和服务器 CPU 在互连和缓存这样的外围逻辑上花费了大量的面积。AMD 的 Bergamo 可以作为一个有趣的比较，因为 Ventana 计划使用类似 AMD 的 hub-spoke chiplet 配置来扩展到大量的核心。也就是说，Veyron V1 和 Zen4c 都使用计算模块连接到中心 IO 模块的这种封装形式。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="431" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-10.png" alt="" class="wp-image-1527" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-10.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-10-300x168.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>Ventana 在他们的 HotChips 演讲中提供了一个 16 核心 Veyron V1 Cluster 的图片，以及在 TSMC 5nm 工艺上的面积数据。AMD 也在他们的演讲中提供了一个 Zen4c 16 核心 CCD 的照片。在这里使用了 Fritzchens Fritz 的 69.49 平方毫米的数据作为 Zen4 CCD 的大小用于面积比较，并假设 Zen4c 的 CCD 占用类似的面积。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="431" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-11.jpg" alt="" class="wp-image-1528" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-11.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-11-300x168.jpg 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>乍一看，具有缓存和支持互连逻辑的 16 个 Veyron V1 核心相对于 AMD 的 Bergamo 有轻微的面积优势。然而，仔细查看 die shot 显示，Ventana 没有在他们的图片中包含芯片到芯片的接口。这种接口将占用大量的面积。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="438" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-11-1.jpg" alt="" class="wp-image-1530" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-11-1.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-11-1-300x171.jpg 300w" sizes="(max-width: 768px) 100vw, 768px" /><figcaption class="wp-element-caption">通过 GIMP 将 CPU 核，L3 slice 和互联部分合并到一起</figcaption></figure>



<p>因此，Veyron V1 的计算模块大小可能与 AMD Bergamo 的不相上下。Veyron V1 每核享有更多的 L3 缓存容量：每核3MB，相比之下 AMD 是每核2MB。Ventana 的 Cluster 内互连还覆盖了 16 个核心及其关联的 L3 Slice，为任意核心提供 48MB 的 L3 容量。对 AMD 来说，他们的 Zen4c 核心拥有更灵活的统一 L2 缓存和更强大的浮点和向量执行能力。</p>



<h2 class="wp-block-heading">Cluster 内互连</h2>



<p>Ventana 使用类环形的互连。他们在演示上说有两个环，但在 2.5GHz 频率下 160GB/s 的对分带宽意味着每周期 64B。如果环每周期可以传32B，那么可能就只有一个环，然后环两侧都在传输。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="432" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-11.png" alt="" class="wp-image-1532" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-11.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-11-300x169.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>也许它是双环配置，然后每周期只能移动 16B。我不完全确定 Ventana 的互连在这里是什么样子，但基于环的互联往往能提供相当好的 L3 性能。希望Ventana 在这里做得很好，因为他们的分离的 512+512KB 的 L2 设计可能意味着 L3 看到的流量比具有 1MB 统一 L2 的设计更多。</p>



<h2 class="wp-block-heading">写在最后</h2>



<p>Veyron V1 有一套非常独特的设计决策。偏离常规是有风险的，因为历史上工程师已经弄清楚了在 CPU 设计上什么有效以及什么无效。这就是为什么成功的 CPU 有很多共同的设计特点。Ventana 似乎做得相当不错，他们没有落入任何明显的漏洞。他们通过硬件别名处理和 ASID 过滤缓解了 VIVT 缓存带来的问题。他们的分离 L2 设计在指令和数据两侧都有足够的缓存容量。他们大型的单级 BTB 和 TLB 要么具有足够低的延迟不会阻碍性能，要么不在性能关键路径上。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="768" height="432" src="https://blog.eastonman.com/wp-content/uploads/2024/02/image-12.png" alt="" class="wp-image-1533" srcset="https://blog.eastonman.com/wp-content/uploads/2024/02/image-12.png 768w, https://blog.eastonman.com/wp-content/uploads/2024/02/image-12-300x169.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>尽管 Veyron V1 是 Ventana 的首款设计，它有望成为在标量整数应用领域的强力竞争对手。但 Ventana 在处理吞吐量密集型应用时将遇到更大的挑战。由于 V1 缺乏向量单元，即便 V2 搭载了向量单元，RISC-V 生态系统的成熟程度仍旧落后于 ARM，这就需要软件来支持 RISC-V 的向量扩展功能。许多吞吐量受限的软件如视频编码器、渲染器和图像处理应用是由社区维护的，而不同社区的对支持新指令集的优先程度不同，这使得对新指令集扩展的支持通常进展缓慢。ARM在努力成为x86可行替代品的过程中已经遇到了软件支持的难题，而RISC-V在这方面面临的挑战则更为严峻。</p>



<p>译者：Ventana 这个核心从开发的角度上来说感觉与香山处在类似的阶段，但是商业化上比香山做得更早更好。但是不知道是不是因为不寻常的设计决策拉长了验证周期，感觉 Ventana 的 IP 在进入硅的速度上比 SiFive 慢一点。</p>



<p>我们要感谢 Ventana 为他们的 Veyron V1 核心整理了非常好的演讲，并期待看到该公司在即将推出的 V2 设计上做了些什么。</p>



<p></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文大部分翻译自 <a href="https://chipsandcheese.com/" target="_blank" rel="noopener" title="">ChipsAndCheese</a>，添加有个人理解和资料补充 ，原文为英文，原文链接：<a href="https://chipsandcheese.com/2023/09/01/hot-chips-2023-ventanas-unconventional-veyron-v1/" target="_blank" rel="noopener" title="">https://chipsandcheese.com/2023/09/01/hot-chips-2023-ventanas-unconventional-veyron-v1/</a> 原作者：<a href="http://chlamchowder" target="_blank" rel="noopener" title="">chlamchowder</a></p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>本文中文翻译首发自我的博客 <a href="https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/" target="_blank" rel="noopener" title="">https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/</a> ，翻译已取得 ChipsAndCheese 编辑/原作者授权。因英文原文无公开的授权协议，本译文也禁止转载，如需转载请先联系我或 ChipsAndCheese。</p>
</blockquote>The post <a href="https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/">HotChips 2023: Ventana 不寻常的 Veyron V1</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2024/02/hot-chips-2023-ventanas-unconventional-veyron-v1/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>现代分支预测：从学术界到工业界</title>
		<link>https://blog.eastonman.com/blog/2023/12/modern-branch-prediction-from-academy-to-industry/</link>
					<comments>https://blog.eastonman.com/blog/2023/12/modern-branch-prediction-from-academy-to-industry/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Sun, 31 Dec 2023 12:51:26 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[计算机系统]]></category>
		<category><![CDATA[Branch Prediction]]></category>
		<category><![CDATA[CPU]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=1282</guid>

					<description><![CDATA[<p>预计阅读时间： 24 分钟 Branch Prediction Is Not A Solved Problem [&#8230;]</p>
The post <a href="https://blog.eastonman.com/blog/2023/12/modern-branch-prediction-from-academy-to-industry/">现代分支预测：从学术界到工业界</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 24 分钟</p>
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Branch Prediction Is Not A Solved Problem</p>
<cite>&#8211;Intel</cite></blockquote>



<p>我从 2022 年底开始在<a href="https://github.com/OpenXiangShan" target="_blank" rel="noopener" title="香山开源高性能处理器">香山开源高性能处理器</a>团队里实习，到现在已满一年了，一直主要负责前端取指模块中分支预测单元的改进。到了年底了，略有一点空闲时间来写一下目前为止在分支预测方面的见闻。</p>



<p>本文假定读者对数字电路设计和高性能 CPU 设计都有一定了解，知道什么是“分支预测”以及“分支预测”对于高性能 CPU 来说的重要意义，如果你完全不了解这部分内容，但又感兴趣的话，建议先去看一些相关的基础书籍和文章。</p>



<p>另外本文基本上仅探讨条件分支的预测，也即只进行 T/NT 的方向预测，其他类型的分支如果大家感兴趣我以后再写。</p>



<p>希望本文能被一些刚参加完龙芯杯，或者通过一生一芯学习的同学阅读。如果想要尝试做分支预测器方面的工作，或者到香山前端团队来实习，那么在雄心壮志开始准备大战预测算法提高准确率之前，请先停下来了解一下真正的高性能处理器设计对分支预测的需求。</p>



<h2 class="wp-block-heading">分支预测发展历史</h2>



<p>最古老的分支预测方法就是饱和计数器，这里假设读者已经充分了解什么是饱和计数器和用饱和计数器来预测分支的方法了。</p>



<p>饱和计数器只能作出偏向性的预测，并不能充分利用单个分支内部和分支之间的关联信息，因此很快就发展出了能够利用分支历史信息的预测器，例如 GShare 预测器:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="567" height="309" src="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.01.12.png" alt="" class="wp-image-1293" srcset="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.01.12.png 567w, https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.01.12-300x163.png 300w" sizes="(max-width: 567px) 100vw, 567px" /><figcaption class="wp-element-caption">GShare预测器示意图<br>来自 <a href="https://courses.cs.washington.edu/courses/cse548/05wi/files/Patt-An-Analysis-of-Correlation-and-Predictability.pdf" target="_blank" rel="noopener" title="">An Analysis of Correlation and Predictability:<br>What Makes Two-Level Branch Predictors Work</a></figcaption></figure>



<p>GShare 预测器将分支的 PC 与一个全局历史寄存器 XOR 以后作为 index 来选择 PHT 中的一个饱和计数器，然后使用这个计数器给出预测方向，更新时同样也通过同样的方式索引到同一个饱和计数器并更新。</p>



<p>这种方式将不同全局分支历史下的同一条分支的预测分给了不同的饱和计数器，使得 GShare 预测器能够区分不同的历史。这样做带来的第一个好处是能够正确预测循环退出了。</p>



<p>另一个好处是，分支之间的相关性可以得到有效利用，下图是常见的分支间相关性的例子</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="549" height="423" src="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.05.05-edited.png" alt="" class="wp-image-1297" srcset="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.05.05-edited.png 549w, https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.05.05-edited-300x231.png 300w" sizes="(max-width: 549px) 100vw, 549px" /><figcaption class="wp-element-caption">分支间相关的例子，来源同上面的图</figcaption></figure>



<p>以上的两个例子恰好对应局部分支历史和全局分支历史。循环退出的预测只需要单个 PC 的历史就可以解决，而分支间相关的例子则必须要全局的分支历史。可见不同的分支需要的历史是不同的，由此发展出了锦标赛预测器，由局部历史和全局历史的两个预测器分别独立预测，然后再由一个饱和计数器构成的选择结构选择使用哪个子预测器提供的方向预测。著名的 Alpha 21264 处理器就是使用了锦标赛预测器。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="704" height="414" src="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.24.28.png" alt="" class="wp-image-1300" srcset="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.24.28.png 704w, https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.24.28-300x176.png 300w" sizes="(max-width: 704px) 100vw, 704px" /><figcaption class="wp-element-caption">Alpha 21264 的分支预测器，来自其论文</figcaption></figure>



<p>Alpha 21264 是经典的超标量乱序处理器，它的论文值得大家一看 <a href="https://ieeexplore.ieee.org/document/755465" target="_blank" rel="noopener" title="">The Alpha 21264 microprocessor</a></p>



<h2 class="wp-block-heading">现代分支预测算法</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Branch prediction research is basically about separating easier and more difficult branches, and using a simple predictor for the easy branches and a complex predictor for the difficult ones.</p>
<cite>&#8212; Onur Mutlu (Computer Architecture and Digital Design, ETHZ)</cite></blockquote>



<p>现代的分支预测器算法只有一种——TAGE，几乎所有的学术研究和商业高性能处理器都使用 TAGE 或者 TAGE 的变种。TAGE 预测器是最早在 2006 年由 Andre Seznec 提出的，提出当时即获得当届分支预测锦标赛冠军。然后接下来直到 2016 年的所有分支预测锦标赛都由TAGE预测器的变种获得。</p>



<p>我在 2022 年参加龙芯杯比赛复现 TAGE 预测器的时候，对 Mutlu 教授在课程中的这句话感受颇深。分支本身的预测难度不尽相同， 如果使用复杂的分支预测器预测简单的分支，那么大概率会浪费存储空间。TAGE 预测器的设计和这一个思想不谋而合，它使用多个不同历史长度的子预测器，并且使用一套机制自动地在这些子预测器中分配表项。这样能够做到使用短历史预测简单分支，长历史预测复杂分支，同时也能够避免存储空间的浪费。</p>



<p>以下是 TAGE 预测器的框图</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="810" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-09.44.12-1024x810.png" alt="" class="wp-image-1316" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-09.44.12-1024x810.png 1024w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-09.44.12-300x237.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-09.44.12-768x607.png 768w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-09.44.12-1536x1215.png 1536w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-09.44.12.png 1808w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>对预测机制细节感兴趣的读者可以参考原论文 <a href="https://jilp.org/vol8/v8paper1.pdf" target="_blank" rel="noopener" title="">A case for (partially) <strong>TA</strong>gged <strong>GE</strong>ometric history length branch prediction</a></p>



<h2 class="wp-block-heading">解耦合前端与覆盖重定向</h2>



<p>像 TAG E这种复杂程度的预测器是没有可能当拍就能得到预测结果的，本文撰写的时期（2023年）附近的半导体工艺对于 SRAM 微缩已经遇到了困难，目前 SRAM 访问就需要接近一个时钟周期或者一个完整的时钟周期了。另外考虑到每个 PC 都访问复杂预测器在功耗上的开销，商业处理器也很少做非常激进的省拍策略（如提前流水等）。</p>



<p>故 TAGE 预测器的返回结果到下一次的预测之间就出现了空拍，这是难以接受的，对性能的影响非常大。所以很自然的就出现多级预测器——覆盖重定向这种策略。这种设计一般使用简单的预测器进行 zero-bubble 的预测，然后再使用复杂预测器对简单预测器的结果进行验证，如果预测不一致，就会发出一个前端重定向，重新开始一个预测流水。这样的做法可以在简单预测器准确率较高的情况下，省去复杂预测器的访问延迟。</p>



<p>下图是一个 SiFive P870 的设计，可以明显得看出具有 Cascading BP 的的设计。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="406" src="https://blog.eastonman.com/wp-content/uploads/2023/12/image-1024x406.png" alt="" class="wp-image-1318" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/image-1024x406.png 1024w, https://blog.eastonman.com/wp-content/uploads/2023/12/image-300x119.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/image-768x305.png 768w, https://blog.eastonman.com/wp-content/uploads/2023/12/image-1536x610.png 1536w, https://blog.eastonman.com/wp-content/uploads/2023/12/image.png 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>覆盖重定向的设计带来了新的问题——预测流水和取指流水，也即 ICache 流水，的速度不匹配。预测流水出现前端重定向的时候，ICache 可能仍能取指，ICache 出现缺失的时候，BP 就处于阻塞状态。为了避免相互影响，现代高性能处理器普遍采用了解耦合前端（或称为分离式前端），只有少量的处理器设计采用传统的设计。</p>



<p>下图是高通在 HPCA 2019 上发表的论文 <em><a href="https://ieeexplore.ieee.org/document/8675212" target="_blank" rel="noopener" title="Elastic Instruction Fetching">Elastic Instruction Fetching</a></em> 中的配图</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="521" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-10.18.35-1024x521.png" alt="" class="wp-image-1323" style="width:694px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-10.18.35-1024x521.png 1024w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-10.18.35-300x153.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-10.18.35-768x390.png 768w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-10.18.35-1536x781.png 1536w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-10.18.35.png 1546w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">学术与产业落地</h2>



<p>我曾经一度认为商业公司秘密发展了新的分支预测算法，而且不予公开，并且感到十分不高兴。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="524" height="128" src="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.32.07.png" alt="" class="wp-image-1305" srcset="https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.32.07.png 524w, https://blog.eastonman.com/wp-content/uploads/2023/10/Screenshot-2023-10-10-at-20.32.07-300x73.png 300w" sizes="(max-width: 524px) 100vw, 524px" /><figcaption class="wp-element-caption">年轻的我.jpg</figcaption></figure>



<p>后来我很快认识到工业界中使用的分支预测算法非常的保守，甚至大部分的商业处理器都只使用了 TAGE 预测器或者 TAGE-L 等价的预测器。</p>



<p>香山处理器的分支预测器也从雁栖湖的 TAGE-SC-L 变成了南湖和昆明湖的 TAGE-SC，然后甚至正在考虑去掉 SC。</p>



<p>这样的情况可能和很多人的认识相悖，尤其是刚参加完龙芯杯或者一生一芯学习的同学。也和一些刚毕业的研究生或者长时间从事学术研究的人的认识有冲突。分支预测准确率对于高性能处理器的影响是非常巨大的，为什么这些在相同存储下能够取得更好预测准确率的算法没有被工业界采用？</p>



<p>接下来我们就讨论一下工业界对于分支预测算法更关注什么？</p>



<h2 class="wp-block-heading">工业界关注什么？</h2>



<p>首先，工业界和学术界的区别在哪里？学术界相信大家都很好理解，只要一个算法在相同的存储空间下能够取得更好的准确率，就可以认为这是一个更好的算法。但是在商业高性能处理器的设计中，还必须要考虑实际的延迟，以及性能、功耗、面积的平衡。</p>



<h3 class="wp-block-heading">更新延迟</h3>



<p>首先是一个非常明显的问题，部分学术界的研究也已经涉及到了，那就是在真实处理器中，分支预测器的更新只能在后端执行完毕以后才能拿到准确的分支执行情况。现代的预测算法都依赖分支的历史，如果历史不准确，将会极大地影响预测的准确率。然而下一条分支的预测开始时，上一条分支还远没有执行完毕，现代处理器中 in-flight 的分支数量普遍可达数十条。那么下一条分支如何拿到前一条分支的方向用于历史的计算？</p>



<p>目前普遍采用的做法是推测更新历史，也就是在预测后立即更新分支历史供下一条分支使用，然后在分支误预测发生时对历史进行恢复。这个做法能够较好的解决历史不及时的问题，但是 Pattern History Table（PHT）的更新依然可以是可以探讨的设计。是在分支执行完就更新？还是等到 commit 以后才更新？总体来说越早更新 warmup 越快，越晚更新更新得越准确。</p>



<p>推测更新历史的做法就引起了下一个问题——为什么现代处理器的分支历史都是全局历史？前文刚刚讨论了局部历史和全局历史对分支预测有不同的贡献，给任何预测器加入局部历史几乎都可以进一步提高预测准确率，那么为什么没有人用？只有部分处理器使用了专门的 Loop 预测器（Loop iteration count 就是一种特殊的局部历史）。这就涉及局部历史推测更新后的恢复困难问题。</p>



<h3 class="wp-block-heading">历史维护</h3>



<p>局部历史维护的困难可以参考 MICRO 2019&#8217;的论文 <em><a href="https://dl.acm.org/doi/10.1145/3352460.3358315" target="_blank" rel="noopener" title="Towards the adoption of Local Branch Predictors in Modern Out-of-Order Superscalar Processors ">Towards the adoption of Local Branch Predictors in Modern Out-of-Order Superscalar Processors </a></em>。</p>



<p>因为全局历史整个处理器只需要维护一份（或少量几份用于推测更新-恢复用），一般都直接使用寄存器来存储全局历史。但是局部历史是需要按照分支来维护，即便是只维护部分活跃的分支，也需要较大的表，因此几乎必须使用 SRAM 来存储。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="1020" height="892" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-11.02.03.png" alt="" class="wp-image-1334" style="width:464px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-11.02.03.png 1020w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-11.02.03-300x262.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-11.02.03-768x672.png 768w" sizes="(max-width: 1020px) 100vw, 1020px" /></figure>



<p>上图是按照分支预测锦标赛中软件实现的 Loop 预测器设计的对应硬件的框图，可以看到需要一个 Branch History Table（BHT）用于记录目前的循环计数。</p>



<p>使用 SRAM 存储带来的问题就是，恢复历史时无法在一拍内恢复或者甚至无法在短时间内恢复。目前已有的几种解决办法是</p>



<ul class="wp-block-list">
<li>维护 in-flight queue，预测时同时查询 queue 获得新的历史，SRAM 内仅存储 commit 后的分支信息，不需要恢复 SRAM，对预测时序和功耗影响大。</li>



<li>维护 recover queue，SRAM 内推测更新历史，恢复时通过 walk queue 来恢复 SRAM 内的信息，对恢复速度要求高。</li>



<li>设计机制，让 SRAM 内历史不准确的时候不参与预测，可以较慢恢复 SRAM 内的信息。</li>
</ul>



<p>更多设计细节的评估和对比可以参考上面的论文。</p>



<p>这种维护的困难使得局部历史在现代处理器的分支预测器中很少见到。</p>



<h3 class="wp-block-heading">Path History</h3>



<p>无论是哪一种分支历史，由于推测更新的维护方式，都必须先出现在 BTB 中，才能进入历史。然而，正常程序中会出现大量从不跳转的分支（如 debug 或错误处理），没有人愿意为了存储这些分支耗费多几倍的 BTB 存储开销。现代处理器普遍只有曾经跳转过的分支才会存储进入 BTB 中，这给维护 Branch History 带来了困难，如果有新分支突然 taken，那么它附近的分支由于历史的变化，基本需要重新 warmup。有的处理器为了让有限的历史寄存器放入更多有意义的信息，可能会对分支做进一步的过滤，只有 T/NT 均出现至少一次的分支才会进入历史，这样需要重新 warmup 的概率也就上升了。</p>



<p>因此工业界更倾向于使用 Path History。Path History就是将跳转的 Target 经过哈希计算进入历史中，这种历史天然地不受不跳转的分支影响。通过论文观察到 ARM 和 Intel 都使用了 Path History 来代替 Branch History。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="818" height="136" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.55.28.png" alt="" class="wp-image-1339" style="width:517px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.55.28.png 818w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.55.28-300x50.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.55.28-768x128.png 768w" sizes="(max-width: 818px) 100vw, 818px" /><figcaption class="wp-element-caption">ARM的PHR</figcaption></figure>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="852" height="246" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.17.png" alt="" class="wp-image-1340" style="width:513px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.17.png 852w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.17-300x87.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.17-768x222.png 768w" sizes="(max-width: 852px) 100vw, 852px" /><figcaption class="wp-element-caption">Intel PHR 中 footprint 的哈希</figcaption></figure>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="826" height="224" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.22.png" alt="" class="wp-image-1341" style="width:546px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.22.png 826w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.22-300x81.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-16.56.22-768x208.png 768w" sizes="(max-width: 826px) 100vw, 826px" /><figcaption class="wp-element-caption">Intel 的 PHR 历史维护</figcaption></figure>



<h3 class="wp-block-heading">主频 &amp; 预测延迟</h3>



<p>如果说前面的几个方面只是增加了复杂分支预测算法落地的难度，那么预测延迟就是阻碍复杂算法落地的直接障碍。举一个例子，TAGE-SC-L 预测器中的 Statistical Corrector（SC）是为了纠正 TAGE 对于一些偏向性大的分支预测不好的部件，同时 SC 还可以作为 TAGE 的补充，如果 TAGE 由于哈希或者别的 corner case 出现失效，那么 SC 可以在一定程度上挽救问题。</p>



<p>但是 SC 需要使用 TAGE 的结果作为输入，还需要做所有表项的加法规约，因此在现代处理器的主频下，延迟比 TAGE 多1-2拍。这样多出来的延迟可能就把将存储分给 SC 的优势给抵消了，如果 SRAM 时序允许的话，给 TAGE 增加额外的 10% 的空间可能能取得更好的效果。SC 增加的延迟在学术上是欠评估的，因为 SC 提出的场景是分支预测锦标赛，该比赛并不要求模拟实际的预测延迟，因此复杂的预测算法更有可能因为合理的存储空间分配而获得更好的效果。</p>



<p>根据我的观察，SC 目前在大的商业处理器上应用很少，可能是加入这个部件增加的功耗、面积、验证成本等已经超过了它带来的误预测率降低的好处了。</p>



<h3 class="wp-block-heading">Multi-branch ahead</h3>



<p>根据统计，桌面应用的平均基本块长度约在5条指令附近（4-10），因此在设计宽译码、宽发射的高性能处理器时，如果每周期只预测一条分支，那么供指能力是不足的。现代高性能处理器普遍具备每周期预测两条指令的能力，但是根据设计能力和目标的不同，一般对两条指令的性质会有一些限制。最常见的是要求本周期的第一条指令不能跳转，这种限制大大降低了设计难度，因为在这样的限制下，两个基本块是连续的，每周期只会产生一个新的 PC，不需要做大量额外的设计就可以支持。</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="551" src="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-17.21.24-1024x551.png" alt="" class="wp-image-1344" style="width:552px;height:auto" srcset="https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-17.21.24-1024x551.png 1024w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-17.21.24-300x161.png 300w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-17.21.24-768x413.png 768w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-17.21.24-1536x827.png 1536w, https://blog.eastonman.com/wp-content/uploads/2023/12/Screenshot-2023-12-31-at-17.21.24.png 1758w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">平均基本块长度和平均跳转长度的对比（注意单位是Bytes）</figcaption></figure>



<p>从上图中可以看到，基本上仅支持这种限制较严格的 2 Branch per cycle 就可以提升每周期的平均供指数量。香山处理器昆明湖目前也仅支持这种情况，实际在 SPEC CPU 2006 上可以做到平均每周期6-7条指令的供指能力。</p>



<p>近年来也有处理器能够支持 2-Taken per cycle的情况，如 ARM 和 Intel 的最新产品，但同时也有对两条分支的类型或者支持的数量做限制。</p>



<h3 class="wp-block-heading">BTB设计</h3>



<p>BTB 设计也是重要的一环，目前的设计方向有以下几个：</p>



<ul class="wp-block-list">
<li>多级 BTB</li>



<li>下级 BTB 与 Cache 共用存储</li>



<li>BTB 压缩</li>



<li>L0 BTB / 快速预测器的设计</li>



<li>L1 BTB 的类型</li>
</ul>



<p>其中除了 L1 BTB 的类型，其他的都比较好理解。那么为什么需要单独讨论 L1 BTB 的类型呢？因为 L0 BTB 大多使用寄存器，排布灵活，L2 BTB 有压缩等设计，类型也可以灵活。唯独 L1 BTB 在预测的关键路径上，不能使用复杂排布，又需要足够大的容量，因此需要使用 SRAM，对读写有较为严格的限制。因此 L1 BTB 的表项设计和索引方式是前端设计中的重点，这里的设计也会大幅影响 TAGE 预测器和整个前端的设计。</p>



<p>今年的 MICRO 2023&#8217;恰好有一篇BTB的类似综述文的文章 <em><a href="https://dl.acm.org/doi/10.1145/3613424.3623774" target="_blank" rel="noopener" title="Branch Target Buffer Organizations">Branch Target Buffer Organizations</a></em> ，感兴趣的读者可以找来看看。其中有对 L1 BTB 的三种常见类型的描述和对比</p>



<ul class="wp-block-list">
<li>I-BTB：紧耦合前端，使用 ICache+预译码充当大容量 BTB。采用这样设计的有苹果和 SiFive。这种设计的优点是不需要为 BTB 使用额外的存储，缺点是紧耦合前端供指能力稍弱，另外 ICache 存储分支的效率是较低的，超过 ICache 容量之后，将无法使用分支信息指导指令预取。</li>



<li>R-BTB：使用减少的空间来存储一定范围内的分支（如 64B），在范围内支持一定数量（如8条）分支，BTB 使用对齐的 PC 索引，通过 offset 来确定具体的分支。优点是使用独立的 BTB 和解耦前端后，前端对分支的处理能力大幅加强，IBM 的 z15 处理器设计中，BTB 的总容量甚至可以 cover L2 Cache 的大小。缺点是当分支密度高的时候无法全部覆盖。</li>



<li>B-BTB：每个表项内存储一个基本块（或一个 Fetch Stream）的信息（下称取指块），取指块内可以有多个分支，有最长的取指块大小，使用 PC 低位索引，高位做 Tag。这种做法解决了 R-BTB 对分支密度的限制，能够处理绝大多数的情况。缺点是一个分支可能出现在多个表项内，会浪费一定的存储空间。</li>
</ul>



<p>香山南湖和昆明湖的设计都是使用 B-BTB 的设计，取指块中允许两个 Branch Slot，第一个仅允许条件分支，第二个允许所有类型的分支。然后预测时如果第一个分支预测不跳转，那么可以按照离第二个分支的距离来供指。</p>



<h3 class="wp-block-heading">功耗</h3>



<p>功耗这一块其实我知之甚少，因为香山还没有开始做低功耗设计（狗头保命），但其实有许多大方向是低功耗设计共性的。首先，因为分支具有难度的差别，还有类型的差别，复杂的预测器和 ITTAGE 这类的间接跳转预测器是没有必要每个PC每个周期都访问的。另外 TAGE 中，每个历史表对于程序片段中的有用程度也是不一样的，所以通过统计的方法，是可以在保留大部分性能的情况下省去很多 SRAM 的功耗。</p>



<p>另外 SRAM 与 SRAM 天差地别，如果设计时给 SRAM 留了时序裕度，选用稍慢的 SRAM 可能可以大幅降低功耗。</p>



<h2 class="wp-block-heading">一定要预测吗？</h2>



<p>最后想谈一下从体系结构的角度，分支一定要预测吗？近年来分支预测算法发展陷入停滞，原因是 TAGE 预测器的算法已经足够优秀，能够在存储耗尽之前充分地挖掘一条分支的“可预测性”，后续的改进也基本针对 corner case 的优化和存储替换算法、置信度等的一些小优化。</p>



<p>那么，剩下的分支误预测怎么办，还有办法吗？此时就需要跳出固有的思维框架，不再仅思考纯硬件的做法，就会发现有新的天地。实际计算机的设计是一个系统工程，需要每一层紧密配合才能达到最好的性能。此处我们介绍两个已经实用的方法，有没有别的新的方法留待读者探究。</p>



<h3 class="wp-block-heading">谓词化指令</h3>



<p>谓词化指令其实就是赫赫有名的条件执行指令，例如 cmov 指令就是著名的谓词化指令。设计这种指令的初衷是很多难预测的分支是和程序数据紧密相关的，甚至直接依赖于数据，那么如果将这些分支转化为 cmov 或类似的条件转送/执行指令，就可以只出现数据流的变化，而不出现控制流的变化。</p>



<p>谓词化指令在 x86 体系和 ARM 体系中都有大量的使用，RISC-V 在 2023 年也通过了 Zicond 扩展。</p>



<h3 class="wp-block-heading">软硬结合</h3>



<p>这里讲的软硬结合大致是指 hint 类型的指令，苹果已有专利说明其设计的处理器中，有针对 macOS/iOS 进程间调用返回的长跳转做 hint 和指令预取的优化，具体的优化只能翻专利了。</p>



<h2 class="wp-block-heading">结语</h2>



<p>其实还有更多的具体设计细节，这里由于篇幅考虑（<s>实际是我偷懒</s>）就不详细展开了，在这里提一个列表共大家思考</p>



<ul class="wp-block-list">
<li>单端口 SRAM 的读写冲突处理</li>



<li>预测器 ctr 降低更新延迟的影响，加快 warmup 的方法</li>



<li>B-BTB 中的多个分支对于方向预测器容量需求不对称的问题</li>



<li>预测器中 SRAM 分 bank 的方法</li>



<li>TAGE 折叠历史的维护方法</li>



<li>各种预测器和 BTB 的索引哈希</li>
</ul>



<p>现在看了这么多设计考虑，你还认为预测准确率是分支预测器唯一的评价指标吗？</p>



<h2 class="wp-block-heading">参考文献</h2>



<ul class="wp-block-list">
<li><a href="https://ieeexplore.ieee.org/document/755465" target="_blank" rel="noopener" title="">The Alpha 21264 microprocessor</a></li>



<li><a href="https://jilp.org/vol8/v8paper1.pdf" target="_blank" rel="noopener" title="">A case for (partially) <strong>TA</strong>gged <strong>GE</strong>ometric history length branch prediction</a></li>



<li><a href="https://ieeexplore.ieee.org/document/8675212" target="_blank" rel="noopener" title="Elastic Instruction Fetching">Elastic Instruction Fetching</a></li>



<li><a href="https://dl.acm.org/doi/10.1145/3352460.3358315" target="_blank" rel="noopener" title="Towards the adoption of Local Branch Predictors in Modern Out-of-Order Superscalar Processors ">Towards the adoption of Local Branch Predictors in Modern Out-of-Order Superscalar Processors</a></li>



<li>Branch Target Buffer Organizations</li>



<li>Half&amp;Half: Demystifying Intel’s Directional Branch Predictors for Fast, Secure Partitioned Execution</li>



<li>Re-establishing Fetch-Directed Instruction Prefetching: An Industry Perspective</li>



<li>Rebalancing the core front-end through HPC code analysis</li>



<li><a href="https://xiangshan-doc.readthedocs.io/zh-cn/latest/frontend/bp/" target="_blank" rel="noopener" title="香山处理器分支预测文档">香山处理器分支预测文档</a></li>
</ul>The post <a href="https://blog.eastonman.com/blog/2023/12/modern-branch-prediction-from-academy-to-industry/">现代分支预测：从学术界到工业界</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2023/12/modern-branch-prediction-from-academy-to-industry/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>NSCSCC2022 龙芯杯参赛总结</title>
		<link>https://blog.eastonman.com/blog/2022/08/nscscc2022/</link>
					<comments>https://blog.eastonman.com/blog/2022/08/nscscc2022/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Thu, 25 Aug 2022 03:32:37 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[计算机系统]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[NSCSCC]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=1081</guid>

					<description><![CDATA[<p>我参加2022年龙芯杯的参赛总结</p>
The post <a href="https://blog.eastonman.com/blog/2022/08/nscscc2022/">NSCSCC2022 龙芯杯参赛总结</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 21 分钟</p>
<p>一转眼已经是八月下旬，暑假只剩一个星期了，<s>当然去年也是这样</s>。上一次写内容还是在年初，基本上半年以来的课外时间基本都投入在龙芯杯和相关的事情上了。龙芯杯就如字面上的意思是由龙芯中科公司和高等学校计算机教育研究委员会共同主办的一个赛事，赛事的主要内容是使用RTL设计一个完整的CPU，并在FPGA上进行验证和展示。2022年是龙芯杯的第六年，今年的比赛也和以往有所不同，新增了LoongArch挑战赛，我就是来吃螃蟹的人（<s>踩大坑</s>）</p>



<h2 class="wp-block-heading">新增赛道</h2>



<p>如上面所讲，2022年龙芯杯最大的不同就是除了往年的MIPS指令集的个人赛和团体赛，新增了LoongArch挑战赛赛道。这个新的赛道的赛制，日程和评价方式都和MIPS的赛道有相当大的区别，区别体现在：</p>



<ul class="wp-block-list"><li><strong>指令集区别</strong>：这个当然是最主要的区别，LoongArch挑战赛使用的是LA32 Reduced指令集，而以往的个人赛和团体赛都使用MIPS指令集。</li></ul>



<ul class="wp-block-list"><li><strong>日程区别</strong>：团体赛和个人赛分为初赛和决赛，2022年初赛在8月5日截止，决赛提交在8月19日截止，线上答题在8月19日，决赛答辩在8月20日。而LoongArch赛道没有区分初赛和决赛（<s>因为本来就没几支队伍</s>），性能测试的提交截止日期是8月14日，没有答辩，ppt展示视频的提交时间是8月18日.</li><li><strong>决赛门槛区别</strong>：MIPS团体赛的决赛门槛是性能分数，决赛队伍数量一般是较为恒定的，只要性能排名能够达到一定水平，就可以进入决赛。而LoongArch挑战赛虽然没有实质性的决赛，但是仍有一个门槛，就是<strong>启动Linux</strong>。</li><li><strong>评价方式区别</strong>：MIPS的团体赛最终评分是50%性能，50%系统展示和答辩，LoongArch则是70%性能，30%系统展示和答辩。</li><li><strong>官方提供的开发工具区别</strong>：MIPS团队赛只有提供功能测试、系统测试等一系列测试，而LoongArch提供了差分测试框架和随机验证的指令流，相比而言LoongArch的仿真验证手段更为先进。</li><li>一些细节要求的区别：比如LoongArch挑战赛性能测试样例不同，性能测试不允许修改SoC，也不允许使用自己编译的Linux。以及LoongArch允许研究生参赛。</li></ul>



<p>当然基本上参加比赛要完成内容是相近的，无非就是设计一个CPU，大部分本科生队伍都是遵循传统的5级流水线，在此基础之上增加流水级、增加顺序双发射、提高频率等等。能力强的队伍或者往届学长很强的队伍可以依照已有的框架进行部分的微架构创新，基本就是这样。</p>



<p>LoongArch指令集恰好也是在2022年大踏步进入开源社区，Linux、QEMU、GCC等诸多重要开源软件都在2022年加入了LoongArch的支持。于是接下来讲一讲龙芯这个LoongArch指令集与MIPS有什么不同。</p>



<p>首先是最明显的（<s>大快人心</s>）的变化就是去除了<strong>延迟槽</strong>。延迟槽是MIPS设计时对那个年代普遍具有的5级流水微架构做的<strong>过度设计</strong>，这种与微架构绑定的设计显然没有跟上飞速发展的硬件设计水平，在2022年看来，延迟槽这个东西属于无用且严重增加设计复杂度的指令集设计。LoongArch没有沿用这个诟病已久的特性我觉得是理所当然的。</p>



<p>其次是删除了协处理器这个概念，特权资源与RISC-V类似使用特权寄存器（CSR）来提供支持，以及浮点运算也不在由协处理器完成。虽然参加龙芯杯不需要实现硬件浮点部件，且在大部分RISC的实现中，浮点部件因为面积巨大，逻辑复杂，通常也是由专门的一个深流水部件完成，与主流水线是解耦的关系，但是我觉得这个仍然是一个好的设计，同样是去除了MIPS中不必要的微架构绑定，为更复杂的设计提供了预留的空间。</p>



<p>再次是对MIPS中的立即数编码做了修改，由统一16位修改为长20/短12的分离的立即数格式。这样做的好处在RISC-V上就能得到充分的体现，常用指令使用短立即数增大了指令的编码空间，长跳、立即数加载等指令使用专门的20位立即数，避免了使用短立即数带来的代码体积增加的问题。</p>



<p>除了上面这些与MIPS相比的优点，LoongArch与RISC-V相比也有相当大的不同。首先是LoongArch设计之初就考虑了大量扩展，相比于RISC-V的极简主义极致模块化，每个扩展缓慢发展，LA的设计更加偏向于商用和实用主义，其中不乏大量的tradeoff和edge case，指令格式的类型也比RISC-V要多很多。</p>



<h2 class="wp-block-heading">队伍情况</h2>



<p>原本以为LoongArch这么先进的基础设施和先进的指令集，以往的强校都会将有实力的队伍投入LA挑战赛中，结果实际上虽然LA有实力强劲的队伍，也有拿出了复杂的乱序设计的队伍，但是传统强校依然基本将重心放在MIPS的团队赛。我猜测（盲猜，不对请大佬指正）是因为这些学校都有传承了数届的基础设施，MIPS的开发流程也能够做到十分先进高效，往年也有优秀的学长的设计可以参考，做到什么样的程度能拿到什么样的名次都十分清楚，因此不太愿意放弃成熟的基础设施和开发流程转而来新的赛道踩坑。</p>



<p>相比之下虽然我校也曾经在龙芯杯取得过一等奖第二名这样的成绩，然而我校却严重缺乏MIPS的基础设施和学长的经验传承，体现在以下几个方面：</p>



<ul class="wp-block-list"><li>没有差分测试框架。参加过多年的强校都有往届学长移植的差分测试框架，甚至有完善的CI自动化测试基础设施，这些基础设施我校一概没有。</li><li>没有学长成功启动过Linux。由于MIPS赛道的决赛门槛并不是支持操作系统，决赛评分中支持Linux也只是保证了一个高的起评分，因此在没有差分测试的情况下，我校的往届学长往往选择做出一个不错的性能，然后靠较为高级的外设和互动来拿到系统展示的分数。没有适配系统的经验对于想要启动Linux来说是非常致命的。</li><li>没有成熟的SoC和外设适配代码和流程。在操作系统上适配外设除了CPU以外SoC上也需要做很多的工作，包括地址空间分配，中断号分配，时钟分配等等。因此我校的系统展示基本仅限于裸机程序的展示，而且都没有使用中断，而采用轮询的方法避开SoC的工作。</li></ul>



<p>今年我们的队伍是临时组建的，也就是说组队前基本互不认识（起码我都不认识），所以并不是什么深谙嵌入式开发/熟悉CPU设计的人。我们所有人，包括我都是在组队完成以后才开始接触入门CPU设计的。</p>



<p>比赛结束以后我翻了一下团队赛的演示视频（是的，我也不知道为什么出现在b站上但不是官号）。2022年第六届龙芯杯了，就我个人的感觉而言，我觉得仍然没有队伍所做的工作能够超越哈利橙他们2019年第三届龙芯杯的工作。的确清华大学今年的参赛队伍设计了乱序的CPU性能能够超过nontrivials-mips，但是按照IPC的比例我觉得其实并没有很好的发挥出乱序处理器应有的能力，而他们的系统适配基本沿用2019年的工作。<s>这令我相信是神去了清华而不是清华培养出了神。</s></p>



<p>我们队伍今年的性能大约只有哈利橙他们的60%（因为没法直接对比），是因为流水cache出现了小概率bug，最后都没有调试出这个问题，因此只能提交主频低，IPC也不高的一个版本。如果流水dcache能够使用的话，我们的性能大约能够小幅超过他们。</p>



<h2 class="wp-block-heading">参赛过程</h2>



<p>以下是回顾参赛的过程，希望对我校或者其他参加龙芯杯的同学或者有志于从事CPU设计的同学有参考价值。</p>



<h3 class="wp-block-heading">语言选择</h3>



<p>我们队伍选择的开发语言是SystemVerilog，主要是考虑到我校的数字逻辑和组成原理都使用的是Verilog，而实际上2005年开始在IEEE的标准里Verilog其实就是SystemVerilog。相比于Verilog，SV主要的优点是有<strong>结构体</strong>和<strong>多维数组</strong>，在总线等信号很多但是较为固定的地方SV也有提供<strong>接口</strong>这一个概念供简化代码。</p>



<p>我校的其他队伍和龙芯杯参赛的其他队伍也都有使用一些其他的HDL，例如SpinalHDL和Chisel等。这些高级的硬件描述语言的工作方式通常是由编译器将高级语言编译成为Verilog然后再交由仿真或综合工具生成网表等。此类高级语言通常都能够解决Verilog系的接线和端口声明冗长的问题，有的语言还有更多的语法糖可以实现OOP等更加高级的代码精简方法。</p>



<p>对于参加龙芯杯使用的HDL的选择，其实并没有一个谁好谁坏，我把一些关键的区别总结在下面共有需要的人参考：</p>



<p>Verilog系语言的优势：</p>



<ul class="wp-block-list"><li>有完整原生的全套工具链支持，包括仿真、综合、实现、编辑&amp;代码补全等，虽然并不一定很好用但是一定是有的。</li><li>直接了当，生成的电路与代码描述的一致。</li><li>相当多开源项目/开源IP核都使用Verilog。</li></ul>



<p>Verilog系语言的劣势：</p>



<ul class="wp-block-list"><li>代码冗余，开发效率可能因此降低，开发者不能讲精力集中在功能设计上。</li><li>二义性语法多，如果出现未定义的语法，不同的工具可能行为不一致，非常影响开发。</li><li>测试testbench编写麻烦。</li></ul>



<p>Chisel类高级语言的优势：</p>



<ul class="wp-block-list"><li>代码精简，开发效率高。</li><li>生成的Verilog保证没有二义性。</li><li>可以使用高级语言编写测试样例，较为方便</li></ul>



<p>Chisel类高级语言的劣势：</p>



<ul class="wp-block-list"><li>通常较新，工具链支持可能不足。</li><li>可能存在编译器bug，而出现问题的时候由于生成的Verilog可读性较差，debug难度较大。</li><li>需要学习一门新的语言。</li></ul>



<p>当然有些优势/劣势并不一定符合你的实际情况，例如你熟悉java/scala语法，那可能Chisel等语言也很容易上手，所以对于语言的选择只要符合自己/自己队伍的情况就可以了，并没有必然的好坏。</p>



<h3 class="wp-block-heading">前期准备</h3>



<p>我校的龙芯杯宣传和备赛都开始得很早，在2021年秋季的数字逻辑设计实验课结束以后就开始了宣传和备赛，我猜测应该在大部分学校里面属于比较早的。我校2022年龙芯杯备赛的时候一开始来了11支队伍，30几个个人参赛，是的你没看错，我也觉得难以置信，而且往年也就是两支队伍+几个人。当然最后提交作品的大概就剩下3支mips+2两支la+8个个人左右（个人赛不太清楚）。</p>



<p>前期的话主要是以学习为主，切忌好高骛远，有的同学在开始动手之前先看了《超标量处理器设计》，然后打算先干一个乱序CPU，然后就没有然后了。我们队伍基本上到2022年3月都是在摸鱼/跟着《自己动手写CPU》抄代码，其他东西的准备也是几乎没有。直到我3月3号提交了ASC初赛的proposal，大家才开始干活（<s>才开始大力push队友</s>）。</p>



<p>4月初我们就分好了工，我负责分支预测，其他队友分别负责主流水线，DCache和总线。于是4月份我就在调研和复现，最终在4月底的时候完成了TAGE的RTL复现。五一假期的时候我们申请了一个教师做队伍内部交流（<s>首次面基</s>），那个时候DCache是说有bug还没有对接，axi已经有一个可用版本，主流水线的单发射版本已经可以进行功能测试了，双发射才开始改。</p>



<p>5月份我主要和主流水线的队友一起适配chiplab和修bug，<s>其他队友可能在摸鱼</s>。5月底的时候就基本完成了双发射CPU的代码编写，<s>这个大约就算是完成了前期的工作吧</s>。</p>



<p>接下来就是<strong>debug</strong>，<strong>debug</strong>和<strong>debug</strong>。</p>



<h3 class="wp-block-heading">工作量和难点</h3>



<p>对于参加LA赛道的工作量，我觉得最大的其实是适配系统。</p>



<p>虽然说支持Linux操作系统并不是两眼一抓瞎从头做起，官方的框架chiplab里面提供了一个十分完善的单发射五级流水的实例CPU，能够稳定启动Linux，因此理论上只要对着那个CPU增量开发就能够保证适配Linux。但是那个CPU充满了assign式的电路级别写法，十分晦涩难懂，所以我们选择了从头开始编写。然而对于完全没有CPU设计和系统适配经验的队伍来说，就算是有完整的实现可以参考，但是这个部分依然是工作量很大的部分。因为系统软件大量依赖特权资源，而这些特权资源其实并没有一个很好的仿真验证负载来测试，因此对于特权部分的调试我们队伍基本上只能依赖在仿真启动Linux，当然这个耗时久比较的久了。另外的是启动Linux操作系统有上亿条指令（没错，是真的上亿），这个对CPU的正确性要求很高，即便是有了6000万条的随机验证序列，chiplab的仿真SoC中也做了各种随机因素的引入，但是有的问题依然不能够在除了Linux以外的负载上复现。</p>



<p>其次就是DCache的一致性问题。LoongArch其实是一个软件维护一致性的ISA，相比于x86这种硬件维护的已经是大幅降低了硬件的设计难度，但是由于实际操作系统中访存指令的类型混杂，访存和一致性维护指令的混杂，使得DCache数据一致性的设计尤为重要但又十分难调试。我们队伍最终就是栽在了流水DCache的一致性维护上，导致Linux的启动并不稳定，有较低概率出现问题，只好提交一个旧的并不是最优的一个版本。</p>



<p>接下来才是CPU微架构的设计，或者其他的特性的设计。哪怕是设计一个乱序四发射的CPU，我相信除了访存部分，其他部分从设计到编写代码到调试完成，基本都可以在两个人月内完成。而访存部分的调试可能需要花同样多的时间。我们的队伍是以启动系统作为最高优先级，因此微架构上的设计可以说十分简陋和落后，仅仅是一个10级静态顺序双发射。</p>



<h3 class="wp-block-heading">队伍时间节点</h3>



<p>以下是我们队伍2022年参加龙芯杯的一些重要时间节点：</p>



<ul class="wp-block-list"><li>2021年12月：组队</li><li>2022年1月-3月：学习阶段，大家各自完成大部分的《自己动手写CPU》的内容</li><li>2022年4月：分工完成，我负责分支预测和前端，我完成TAGE的论文复现</li><li>2022年5月：完成主流水线并接入chiplab进行功能测试</li><li>2022年6月11日：首次仿真启动Linux，无Cache</li><li>2022年6月下旬：根据随机验证修复bug</li><li>2022年7月初：遇到Verilog二义性问题，花费全队一个星期debug</li><li>2022年7月12日：首次板上启动Linux，有ICache</li><li>2022年7月底：前端分支预测、写直通法dcache完成</li><li>2022年8月6日：前端freeze，首个可以提交的版本完成</li><li>2022年8月7-14日：流水写回dcache调试，未成功</li><li>2022年8月14日：提交旧版无bug版本</li><li>2022年8月18日：最终提交系统展示和文档ppt等</li></ul>



<h2 class="wp-block-heading">感想</h2>



<p>其实原本我校还有另一支队伍也参加了LA赛道，他们使用了Chisel作为开发语言，开始准备的时间比我们还要早很多，寒假结束就已经有一个五级流水的核了。而且他们的微结构比我们先进很多，也使用了记分板的乱序方法（没有重命名），但是他们最终没有调试出Linux，而无缘决赛。说到这个，不得不提我们最终的作品是一个静态10级流水的顺序双发射CPU，对于这个10级静态，我们队里的人和学长/老师都觉得十分浪费。访存所需的逻辑很多，导致基本上需要4-5级的流水来完成，但是作为一个静态的流水线，除了访存以外的指令在后面的访存流水中完全没有事情做，非常的浪费。而且我们的load to use问题解决的也不是很好，采取了保守的方式，而本可以采用更激进而高效的方法。但是其实我认为吧，LA这个赛道如果是第一年参赛，首要目标还是启动Linux吧，这也是我们队伍从一开始的原则。当然这导致我们的微架构没有充足的时间设计，只能草草沿用传统的静态长流水。</p>



<p>今年的龙芯杯也算是最终落下帷幕了，已经得知有乱序队伍今年Linux启动不稳定，打算明年继续了。</p>



<h2 class="wp-block-heading">成绩</h2>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/08/Screen-Shot-2022-08-21-at-10.18.55-1024x578.png" alt="" class="wp-image-1117" width="637" height="360" srcset="https://blog.eastonman.com/wp-content/uploads/2022/08/Screen-Shot-2022-08-21-at-10.18.55-1024x578.png 1024w, https://blog.eastonman.com/wp-content/uploads/2022/08/Screen-Shot-2022-08-21-at-10.18.55-300x169.png 300w, https://blog.eastonman.com/wp-content/uploads/2022/08/Screen-Shot-2022-08-21-at-10.18.55-768x433.png 768w, https://blog.eastonman.com/wp-content/uploads/2022/08/Screen-Shot-2022-08-21-at-10.18.55.png 1193w" sizes="(max-width: 637px) 100vw, 637px" /></figure>



<p>最终我们拿到了一等奖。对于这个成绩我还是十分满意的（<s>好像没有更满意的成绩了</s>）。其实也算有点意料之中吧，因为提交前看到LA群里的其他队伍的情况，很多队伍都还在调试Linux，估计是没有时间好好调性能了，我们虽然最终的流水DCache版本没有敢提交，但是前端是有仔细调优过的，性能也算中规中矩吧。</p>



<h2 class="wp-block-heading">后续</h2>



<p>项目代码整理完毕以后应该会开源出来。</p>



<p>接下来我也会撰写几篇文章描述我们的前端设计和TAGE预测器的详细复现内容，可以关注本博客或者订阅RSS。</p>The post <a href="https://blog.eastonman.com/blog/2022/08/nscscc2022/">NSCSCC2022 龙芯杯参赛总结</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2022/08/nscscc2022/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>2021年终总结：你所热爱的，就是你的生活</title>
		<link>https://blog.eastonman.com/blog/2022/01/end-of-year/</link>
					<comments>https://blog.eastonman.com/blog/2022/01/end-of-year/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Mon, 03 Jan 2022 14:27:04 +0000</pubDate>
				<category><![CDATA[生活]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=994</guid>

					<description><![CDATA[<p>预计阅读时间： 10 分钟 跟风写一个年终总结，迟了几天，属于错峰写文了。 引言：身份的焦虑 此段是我看到@m [&#8230;]</p>
The post <a href="https://blog.eastonman.com/blog/2022/01/end-of-year/">2021年终总结：你所热爱的，就是你的生活</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 10 分钟</p>
<p>跟风写一个年终总结，迟了几天，属于错峰写文了。</p>



<h2 class="wp-block-heading">引言：身份的焦虑</h2>



<p>此段是我看到<a href="https://milkice.me/" title="https://milkice.me/" target="_blank" rel="noreferrer noopener">@milkice</a>的<a href="https://milkice.me/2022/01/01/2021-yearly-summanry/" target="_blank" rel="noreferrer noopener" title="https://milkice.me/2022/01/01/2021-yearly-summanry/">2021年终总结</a>关于评价能力指标的问题，有感而发，再加上之前读了一本书叫《身份的焦虑》，觉得这本书有回答这个问题。这本书的主要论点如下：</p>



<ul class="wp-block-list"><li>一个人焦虑是一个人的问题，所有人都焦虑就是社会问题</li><li>现代社会的阶级流动性好，与历史上任何的时期相比都是</li><li>大家都认同“世上无难事，只要肯登攀”这样的理念</li><li>潜台词就是“如果生活不好/社会地位不高，原因就是不够努力”</li><li>人总是喜欢与一些和自己地位相近而又略高的人比较</li><li>因此大家越来越焦虑</li><li>但是其实这并不合理，因为社会地位影响因素太多了，而大部分并不是努力能够改变的</li></ul>



<p>这本书其实还有提出一些解决办法，但是我个人觉得实在是有点玄学，就不在此列出，感兴趣的可以去看。</p>



<p>我出生在并不富裕但也并不贫穷的家庭，可以说我的家庭比相当多的人家庭条件还是要好上一些的，然后家长也比较重视教育，使得我能够享受到很好的教育资源，从小学中学直到大学。因此毫无疑问的是我已经比绝大多数人，所谓的“芸芸众生”要好上那么一些，然而我并不能摆脱这些“焦虑”源泉，经常能在各种地方看到各种各样的大佬，每一个都可以顶礼膜拜好多天。</p>



<p>所以如果一定要量化一个评价标准的话，我个人认为应该将一个人的各种“评分”Normalize到一个相同的平均值和标准差，再来评价一个人所谓的“努力程度”。</p>



<p>我所在的计算机系已经属于努力和成绩比较接近线性的学科了，我觉得我应该为此感到庆幸。</p>



<h2 class="wp-block-heading">回顾</h2>



<p>上一次写年终总结已经是2018年了，那个时候我是高二，现在是大二，三年过去了。2018年的时候我在用typecho写博客，还维护一个我自己重构的主题，2020年高考完重建博客的时候换用了Wordpress。2018年的时候想考去中科大少创班，想着2019年6月考完就解放了，<s>还可以去USTCLUG</s>，结果造化弄人，6月考完得知落榜的第二天，我就收到了清华的一本优惠，然后造化继续弄人，2020年自主招生就取消了。然后我就到了哈工大深圳。现在想来，也不算是遗憾了，命里有时终须有，我在计算机系能够找到我热爱的事情，希望以后能一直热爱下去。</p>



<h2 class="wp-block-heading">博客</h2>



<p>首先是2021年本博客的一些数据和统计，全年访客共5889人，访问页面数11042。2018年的时候因为维护主题，也有4000多的访客量，但是跳出率很高，也就是大多数人都只看一个页面就走了。（以及下面这张图的跳出率是错的）</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM.png" alt="" class="wp-image-1000" width="978" height="588" srcset="https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM.png 2286w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM-300x181.png 300w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM-1024x616.png 1024w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM-768x462.png 768w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM-1536x925.png 1536w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.14.49-PM-2048x1233.png 2048w" sizes="(max-width: 978px) 100vw, 978px" /><figcaption>访客统计图</figcaption></figure></div>



<p>数据统计用的是<a href="https://umami.is/" target="_blank" rel="noreferrer noopener" title="https://umami.is/">umami</a>，符合GDPR。显然5月份写的《<a href="https://blog.eastonman.com/blog/2021/05/modern-processor/" target="_blank" rel="noreferrer noopener" title="https://blog.eastonman.com/blog/2021/05/modern-processor/">现代处理器</a>》因为在V2EX上宣传了一下，带来了很多的访客。全年访问最多的也是它，有2.09k的访问量。Referer最多的是Google，有3.15k的访问量。</p>



<p>2021年的博文一共写了36703个字，写的还是《<a href="https://blog.eastonman.com/blog/2021/05/modern-processor/" target="_blank" rel="noreferrer noopener" title="https://blog.eastonman.com/blog/2021/05/modern-processor/">现代处理器</a>》，有9075字。到目前为止，博客一共发布了46k个字。</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.34.51-PM-279x1024.png" alt="" class="wp-image-1008" width="136" height="499" srcset="https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.34.51-PM-279x1024.png 279w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.34.51-PM-82x300.png 82w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-03-at-8.34.51-PM.png 414w" sizes="(max-width: 136px) 100vw, 136px" /></figure></div>



<h2 class="wp-block-heading">野鸡运维</h2>



<p>2021年维护了一堆又一堆的机器，有自己的，也有学校的</p>



<p>最长的uptime是一个代理机器，有一年24天，前两天重启了，截个图纪念一下</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-02-at-10.03.27-AM.png" alt="" class="wp-image-1002" width="576" height="198" srcset="https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-02-at-10.03.27-AM.png 1370w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-02-at-10.03.27-AM-300x103.png 300w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-02-at-10.03.27-AM-1024x351.png 1024w, https://blog.eastonman.com/wp-content/uploads/2022/01/Screen-Shot-2022-01-02-at-10.03.27-AM-768x263.png 768w" sizes="(max-width: 576px) 100vw, 576px" /><figcaption>tuptime</figcaption></figure></div>



<p>2021年持续运行的服务有：</p>



<ul class="wp-block-list"><li><strong>Gitea</strong> 自建的Git服务</li><li><strong>RSSHub</strong> 将网页转化为RSS</li></ul>



<p>申请了一个ASN号，<s>成为了HostUS受害者</s>，但是目前为止基本处于荒废状态，DN42里也注册了ASN，但是至今也没有Peer。2022年希望能有时间玩这个吧。</p>



<h2 class="wp-block-heading">GitHub</h2>



<p>2021年代码量前4的Repo</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_repos-1-1024x973.png" alt="" class="wp-image-1018" width="344" height="327" srcset="https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_repos-1-1024x973.png 1024w, https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_repos-1-300x285.png 300w, https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_repos-1-768x730.png 768w, https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_repos-1.png 1200w" sizes="(max-width: 344px) 100vw, 344px" /><figcaption>GitHub Repo Wrapped</figcaption></figure></div>



<p>2021年使用最多的语言，看来我写了很多Go。C++主要是那个ASC练习题，Zig主要就是Zesty-Core这个玩具操作系统。</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_langs-1-1024x973.png" alt="" class="wp-image-1019" width="343" height="326" srcset="https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_langs-1-1024x973.png 1024w, https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_langs-1-300x285.png 300w, https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_langs-1-768x730.png 768w, https://blog.eastonman.com/wp-content/uploads/2022/01/eastonman_langs-1.png 1200w" sizes="(max-width: 343px) 100vw, 343px" /><figcaption>GitHub Language Wrapped</figcaption></figure></div>



<p>2021往acme.sh交了PR，还往podman交了PR，虽然只是一个小改动。</p>



<h2 class="wp-block-heading">实习</h2>



<p>暑假出去实习了，<s>因为暑假没事干很无聊</s>，因为老师建议尽早实习感受一下。当时投递的是高性能计算岗，先是笔试考了一些简单知识，还有几道算法，然后是电话面试，基本上是问简历上写的项目和几道算法，我感觉我答得也不算好，因为我本来不搞OI，算法设计这门课也还没上，所以好多算法都不知道或者写错了。</p>



<p>最后还有一个实际项目的测试，是五一的前两天做的，要求写一个矩阵乘法，还有一个“生产者-消费者”模型。这个应该是我第一次写SIMD的intrinsic，最后矩阵乘法的效率应该也没有多高，好像大概是10%左右吧，当时没有分析达到了峰值性能的多少，还被导师指点了一下。后来我也有参考别人的做法自己写，能够达到50%左右的效率，和OpenBLAS差不多了，MKL在当时那台机上能达到70%，真不知道英特尔是怎么写的。“生产者-消费者”模型就是要求用Redis的队列实现，Redis我直接用了个C++的库，大概写的差不多就交了。</p>



<p>最终还真过了，然后7月初开始就去实习了，一直到8月20号左右。在公司做的是JPEG实时解压这个方案的调研，结果是花了6核的CPU才能做到30帧，毕竟要解压JPEG成YUV420，然后再通过一个已有SIMD的库变换成为特定视角的图，我通过每帧流水线式的并行解码达到了30帧，但是当时leader觉得这个方案计算量还是太大，不知道最后有没有用上。</p>



<h2 class="wp-block-heading">竞赛</h2>



<p>2021年其实参加了三个竞赛，如果把1月截止的ASC20-21也算上就算是四个竞赛了。3-4月份跟着某位大佬去华为的软件挑战赛，结果卷不过别人，拿了个三等奖。然后就到了暑假，暑假一边实习，一边做CPC的初赛，还有IKCEST的初赛和复赛。IKCEST我们两个人都是不是做CV的，比赛还有性能要求，只好现学现做，甚至搞上了TensorRT这种东西。</p>



<p>CPC那边得益于两个给力的队友，成功去了现场的决赛，还拿到了银奖。初赛的时候两个队友在上小学期的CPU设计与实践，我主要抄论文写汇编计算核，队友写分块保证正确性。复赛的时候变成了大项目，其实时间也不是很多，基本上是刚刚好把能做的初步优化都做了，然后就到了决赛了。有些队伍做的算子融合我们都一个都没做，不过这次比赛让我知道了先做Profile再做优化的重要性，还有接下来的ASC可能要引入自动化测试，免得像CPC那样决赛前一天晚上大家通宵解决正确性问题。</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/REX_2590-1024x601.jpg" alt="" class="wp-image-1029" width="504" height="296" srcset="https://blog.eastonman.com/wp-content/uploads/2022/01/REX_2590-1024x601.jpg 1024w, https://blog.eastonman.com/wp-content/uploads/2022/01/REX_2590-300x176.jpg 300w, https://blog.eastonman.com/wp-content/uploads/2022/01/REX_2590-768x450.jpg 768w, https://blog.eastonman.com/wp-content/uploads/2022/01/REX_2590-1536x901.jpg 1536w, https://blog.eastonman.com/wp-content/uploads/2022/01/REX_2590.jpg 1920w" sizes="(max-width: 504px) 100vw, 504px" /><figcaption>入场照</figcaption></figure></div>



<h2 class="wp-block-heading">社团</h2>



<p>2021年春天我有想法做一个镜像站，问了几个老师和同学，就变成了组织一个Linux用户协会这种类型的社团，可惜我一无建立社团的经验，二无宣传拉人的经验，所以镜像站建起来一个雏形以后就没有接下来的动作。直到秋天21级的本科生进来了，突然有了很多志同道合的小伙伴，于是就很快有很多的人，还搞了两场Talk，虽然都是<a href="https://origami404.github.io/" target="_blank" rel="noreferrer noopener" title="https://origami404.github.io/">@萝佬</a>讲的。希望LUG能越办越好，毕竟大家都卷AI在我看来也不是什么好事。</p>



<p>放一个LUG徽标在这里</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><a href="https://wiki.hitsz.org/" target="_blank" rel="noopener"><img loading="lazy" decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2022/01/lug-01.svg" alt="" class="wp-image-1028" width="270" height="270"/></a><figcaption>HITSZ LUG</figcaption></figure></div>



<h2 class="wp-block-heading">2022展望</h2>



<p>首先是希望ASC22能进决赛（<s>小小的愿望都不可以满足吗</s>），今年队友都很好，学校也是给了很大的硬件支持，自己的水平经过一年也有所提升，希望今年能圆梦ASC吧。</p>



<p>然后希望ASC的工作能像清华他们那样写一篇论文吧。</p>



<p>另外秋季学期的课程学得不是很认真，希望春季能在兼顾竞赛的同时学好课程吧，能把学分绩卷上去一点，<s>俺也要保研名额！</s></p>



<p></p>



<p></p>



<p>题图来自<a href="https://unsplash.com/@mrthetrain" target="_blank" rel="noreferrer noopener">@mrthetrain</a>，感谢</p>The post <a href="https://blog.eastonman.com/blog/2022/01/end-of-year/">2021年终总结：你所热爱的，就是你的生活</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2022/01/end-of-year/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>最优停止理论与导师选择</title>
		<link>https://blog.eastonman.com/blog/2021/12/optimal-stop/</link>
					<comments>https://blog.eastonman.com/blog/2021/12/optimal-stop/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Tue, 07 Dec 2021 16:17:14 +0000</pubDate>
				<category><![CDATA[生活]]></category>
		<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=936</guid>

					<description><![CDATA[<p>预计阅读时间： 10 分钟 这是我的概率论小论文，是经由《如何选择心仪对象》启发，加入了一些我自己的想法和论文 [&#8230;]</p>
The post <a href="https://blog.eastonman.com/blog/2021/12/optimal-stop/">最优停止理论与导师选择</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 10 分钟</p>
<p>这是我的概率论小论文，是经由《<a href="https://blog.lhp-pku.top/2021/06/19/%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E5%BF%83%E4%BB%AA%E5%AF%B9%E8%B1%A1/" target="_blank" rel="noreferrer noopener" title="https://blog.lhp-pku.top/2021/06/19/%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E5%BF%83%E4%BB%AA%E5%AF%B9%E8%B1%A1/">如何选择心仪对象</a>》启发，加入了一些我自己的想法和论文查询，经过重新排版以后发布到博客上。如果你希望看由Latex生成的PDF，可以在<a href="https://blog.eastonman.com/wp-content/uploads/2021/12/概率论论文.pdf" title="https://blog.eastonman.com/wp-content/uploads/2021/12/概率论论文.pdf">这里</a>找到。</p>



<h2 class="wp-block-heading">问题背景</h2>



<p>最近我阅读到了一篇<a href="https://blog.lhp-pku.top/2021/06/19/%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E5%BF%83%E4%BB%AA%E5%AF%B9%E8%B1%A1/" target="_blank" rel="noreferrer noopener" title="https://blog.lhp-pku.top/2021/06/19/%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E5%BF%83%E4%BB%AA%E5%AF%B9%E8%B1%A1/">文章</a>，描述最优停止理论在研究生导师选取上的应用。文章使用了经典麦穗问题的结论，我认为对于这个场景，有更合适的最优停止策略。</p>



<p>想象有以下情景：某同学在参加保研面试。面试之前他与这些老师互不了解，聊完之后他对老师们有了自己的评价；但出乎他意料的是，每个老师在面试结束时都表达出对某同学的满意，当即给出offer，并要求他现场决定是否接受。某同学只有一次选择机会，offer接受后不能再反悔拒绝，拒绝后也不能再反悔接受。在这样的情境下，这位同学该如何在他们中选出评价最高（即最喜欢）的一位呢？</p>



<p>此类问题其实是非常古老的问题，被人们研究了很多年了，策略通常为“观望-选择”，因主要研究最佳的停止观望时间，此类问题称为最优停止问题。最优停止问题又称麦穗问题、秘书问题、博士结婚问题等，大家应该对以下的故事有所耳闻：</p>



<p>在很久之前，苏格拉底的三个弟子向他请教，问他怎样才能找到自己理想的伴侣。他什么话都没说，直接把三个弟子带到一片麦田里，让弟子们每人挑一根最大的麦穗。做这一切的前提是，不能走回头路，也就意味着他们只有一次选择的机会。</p>



<p>这三个弟子呢，颇有意思，因为他们的选择大不相同。</p>



<p>第一个弟子刚走进去没多久，就摘了一根自以为很大的麦穗，心里不禁洋洋得意。但他越走越发现自己做错了选择，因为后面的麦穗远大于他手里的麦穗。</p>



<p>第二个弟子不同于第一个弟子，他恰好相反，他一直往前走，总觉得最大的麦穗都在前面，所以他看到比较大的麦穗早就错过了。</p>



<p>第三个弟子，也是三个人里比较聪明的一个了，他不慌不忙地边走边思考，然后把麦田分成了三份，走第一段路时，他只看不摘；继续往前走时，他会将现在的麦穗与之前看到过的麦穗进行比对。最后他挑选了一根他认为最大的麦穗。当然，他也是这三人中拿到最大麦穗的人。</p>



<p>对于经典的麦穗问题，最受公认的最优停止时间是麦穗总数N的1/e，也即N/e。以下我们将会讨论这个结论的来源和我们“导师选取”问题的结论。</p>



<h2 class="wp-block-heading">经典结论</h2>



<p>我们将这个问题用数学语言描述：考虑一组N个独立服从[0,1]上均匀分布的随机变量X_1,X_2…X_N然后我们按编号从小到大遍历这些变量，对于每个变量，我们只能做出“保留”或者“丢弃”两种选择，并且我们只有一次保留机会。那么应该如何制定策咯，使得我们选取到最大变量的概率最大？</p>



<p>我们主要关注于一种简单的策略，就是通过某种方法设定一个阈值c，在选取过程中一旦出现值超过阈值c的变量，那么立即选取。很自然地有这样一个策略：对于前m个变量，我们一律不选，对于从m+1号开始的变量，一旦有比前面更大的变量，我们就立即选取。这种先观望，后选取的策略，以下称为m策略。</p>



<p>现在讨论如何选取m的值。</p>



<p>对于某个特定的m，如果最大值出现在第i个位置，m策略选取到最大值的概率为</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
P(m,i)=
&amp; 0 &amp; {i \leq m} \\
&amp; \frac{m}{i-1} &amp; {i&gt;m}
\end{aligned}</pre></div>



<p><br>所以如果我们使用m策略，成功选取到最大值的概率为</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
P(m) &amp;= \sum_{i=m+1}^{n} \frac{1}{n} P(m,i) \\
&amp;= \frac{m}{n} \sum_{i=m+1}^{n} \frac{1}{i-1}
\end{aligned}</pre></div>



<p><br>当n和m都较大的时候，令r= m/n，可将上式右侧的调和级数近似为ln(m/n)，可得：</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
P(m) &amp; \approx \frac{m}{n} \ln\frac{n}{m} \\ \Rightarrow P(r) &amp; = -r\ln r
\end{aligned}</pre></div>



<p>求导得：</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
P'(r) = -1-\ln r
\end{aligned}</pre></div>



<p><br>当r=1/e时，P'(r)=0，P(r)达到最大值1/e</p>



<p>所以对于麦穗问题的m策略，m的最优值就是N/e（取整数）。</p>



<p>然而，选取研究生导师和麦穗问题还有不同之处：1) 我们并不需要选到最好的导师，只是希望选取到好的导师。2) 我们不能允许我们糟糕的选择策略使得我们没有导师！</p>



<h2 class="wp-block-heading">导师选取问题</h2>



<p>首先用数学语言描述“导师选取”问题：对于N个导师，我们有主观评价分X_1,X_2,…,X_N，然后我们希望通过某种策略选取导师。这种策略应当使选取的导师的评价分的期望尽可能高，同时也能保证我们能够选到老师（有学上）。除此之外，在现实中，评价导师并不能使用量化的分数，而只能通过比较，认为某导师较好。因此，我们设定虽然每位导师都有评价分X_i，但我们选择的时候并不能得知具体的分X_i，而是只能得到导师评价分之间的关系。</p>



<p>因此我们无法使用任何动态阈值或使用不同于任何导师评价分的阈值。我们只能使用类似m策略的方式，为了区分，下称t策略。t策略是指通过遍历前m个导师确定一个阈值t=max{(X_1,X_2,…,X_m)}然后从第m+1开始，只要某导师的评价高于t，那么立即选取该导师，如果直到第N-1位导师都没有找到评价大于t的，那么选取最后一位导师。</p>



<p>我们希望选取合适的m使得最终我们选取的导师的评价分的期望尽量的大。</p>



<p>首先我们计算阈值t的分布函数</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
F(t) &amp;= F_{max}(t) \\
&amp;= P(\max(X_i) \leq t) \\
&amp;= \prod P_i(X \leq t) \\
&amp;= t^m
\end{aligned}</pre></div>



<p><br>由此可以得到阈值t的概率密度函数</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
f(t) &amp;= F'(t) \\
&amp;= m t^{m-1}
\end{aligned}</pre></div>



<p>对于确定的阈值t，有两种情况：1) 我们成功选到了心仪的导师。 2) 我们只好选了最后一位导师。</p>



<p>分别有概率</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
P_2 &amp;= t^{N-m-1} \\
P_1 &amp;= 1 - P_2 \\
&amp;= 1 - t^{N-m-1}
\end{aligned}</pre></div>



<p>而这两种情况对应的评价分的期望为E_1 = (1+t)/2，E_2 = 1/2</p>



<p>于是我们可以计算出对于确定的m，选取导师的评价分的期望为</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\begin{aligned}
E(x) &amp;= \int_{0}^{1} f(t) (P_1 E_1 + P_2 E_2) dt \\
&amp;= \int_{0}^{1} mt^{m-1} \left[ \frac{1}{2} t^{N-m-1} + (1-t^{N-m-1})(\frac{1+t}{2}) \right] dt \\
&amp;= \frac{2m+1}{2(m+1)} - \frac{m}{2N}
\end{aligned}</pre></div>



<p>当期望E(x)最大时，有</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>\frac{\partial E}{\partial m} =\frac{1}{2(m+1)^2} - \frac{1}{2N} = 0</pre></div>



<p>解得</p>



<div class="wp-block-katex-display-block katex-eq" data-katex-display="true"><pre>m = \sqrt{N} -1</pre></div>



<p>可见如果我们希望以期望为指标，那么我们的最优停止时间将是\sqrt{N}-1。也就是从第\sqrt{N}位导师开始，只要出现比前面评价分高的导师，我们就立即选取。</p>



<h2 class="wp-block-heading">讨论</h2>



<p>我们发现，麦穗问题和“导师选取”问题的结论其实有很大不同，分别为m=N/e和m=\sqrt{N}-1。这里显然有一个增长速率上的差异。当N足够大时，按照\sqrt{N}-1进行的策略需要观察的元素数量远小于按照N/e的策略。</p>



<p>这是因为选择到最大元素X_k要求在[1,k-1]的元素中，我们选定的阈值c既是[1,m]中最大的，也是[1,k-1]中最大的，这样的情况下要求我们选定的阈值较高，才更有可能选取到最大元素。而在按照期望为指标的策略中，我们并不需要选取最大的元素，而是期望较大就可以了，因此需要的观望次数也较少。</p>



<p>现实生活中有许多可以应用最优停止理论的地方，其中有相当一部分并不完全符合经典麦穗问题类的前提，而是与本文提出的修正情况相符合，因此本文得出的结论具有一定的现实意义。</p>



<h2 class="wp-block-heading">结语</h2>



<p>当然以上提出的情景和我没有任何关系，因为我在写概率论论文，没有导师给我发offer。希望我能够有应用这个结论的场景，因为这意味着我至少不会失学了。同时也祝愿所有同学都不失学，并且能找到对象。</p>



<h2 class="wp-block-heading">参考</h2>



<ul class="wp-block-list"><li><a href="https://blog.lhp-pku.top/2021/06/19/%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E5%BF%83%E4%BB%AA%E5%AF%B9%E8%B1%A1/" target="_blank" rel="noreferrer noopener" title="https://blog.lhp-pku.top/2021/06/19/%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E5%BF%83%E4%BB%AA%E5%AF%B9%E8%B1%A1/">如何选择心仪对象</a></li><li><a href="https://arxiv.org/abs/1810.11557" target="_blank" rel="noreferrer noopener" title="https://arxiv.org/abs/1810.11557">The Duration of Optimal Stopping Problems</a></li><li><a href="https://zhuanlan.zhihu.com/p/113593894" target="_blank" rel="noreferrer noopener">麦穗理论|不求最好，但求合适</a></li></ul>The post <a href="https://blog.eastonman.com/blog/2021/12/optimal-stop/">最优停止理论与导师选择</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2021/12/optimal-stop/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>全球大内网与Zerotier</title>
		<link>https://blog.eastonman.com/blog/2021/12/zerotier/</link>
					<comments>https://blog.eastonman.com/blog/2021/12/zerotier/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Mon, 06 Dec 2021 03:32:53 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[Zerotier]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=910</guid>

					<description><![CDATA[<p>预计阅读时间： 6 分钟 前段时间把笔记本换成了MacBook Pro（真香），为了数据安全，自然是需要把Ti [&#8230;]</p>
The post <a href="https://blog.eastonman.com/blog/2021/12/zerotier/">全球大内网与Zerotier</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 6 分钟</p>
<p>前段时间把笔记本换成了MacBook Pro（<s>真香</s>），为了数据安全，自然是需要把Time Machine利用起来。而Time Machine只能备份到本地的硬盘或者AFP或者SMB或者Time Capsule。显然我没有多余的硬盘，也没有rich到来一个Time Capsule，但是我手头上恰好有一个2T HDD的大盘鸡（<s>远在天边</s>），所以想把Time Machine的备份放到这个大盘鸡里。</p>



<p>那么现在问题来了，SMB这个玩意儿的安全问题相信大家也都有所耳闻，我实在是不想把它暴露在公网上，这就需要一个VPN。正好我前几天黑五搞了一些BGP Player友好的鸡，甚至还有公网的ASN号，因此就想干脆组一个全球大内网吧。</p>



<h2 class="wp-block-heading">为什么选择Zerotier</h2>



<p>其实我一开始的想法是Wireguard，因为这玩意儿大家都在吹，也在内核主线里，可以说相当的轻量，也很flexible。但是Wireguard的问题在于它是点对点的，也就是如果没有一个管理的途径的话，每条边都需要手动操作一次，非常麻烦。有的大佬用ansible解决，<s>然而我并不会这么复杂的ansible</s>。</p>



<p>我总结了一下我对于全球大内网最主要的需求：</p>



<ul class="wp-block-list"><li><strong>无中心节点</strong>，或者说流量不能由中心节点转发</li><li><strong>轻量高效</strong>（OpenVPN什么的就免了），因为有的节点之间可以跑到千兆，如果不够轻量的话会有CPU瓶颈。</li><li><strong>配置简易</strong>，超过Wireguard的就免了</li><li><strong>确保安全</strong>，必须加密传输，稍弱一些如aes128的也可以，但是不能接受明文传输，所以VXLAN和GRE等也不满足了。</li></ul>



<p>除此之外还有一些加分项：</p>



<ul class="wp-block-list"><li>multipath，我们的VPS服务商提供的网络接入质量不尽相同，如果有一种方式可以让两个线路较差的VPS通过一个线路较好的VPS进行转发其实可以得到更好的质量。不过目前除了iBGP+自定义cost似乎没有别的方式可以达到这样的效果。</li><li>中心化的路由配置，这一项Zerotier恰好可以满足，如果我们需要桥接别的“真正的”内网，这个功能还是很实用的。</li><li>跨平台客户端，虽然目前的方案并不需要这个功能，但是如果我们以后希望将大内网进一步扩大，可能会需要这个功能。</li></ul>



<h2 class="wp-block-heading">自建VS官方</h2>



<p><s>有这么多鸡不自建也是浪费</s></p>



<p>zerotier有network controller，planet，moon等很多概念/角色，我也没搞懂，有懂的可以指点一下。自建network controller的好处在于可以避免官方的同一个网络50个客户端限制。自建moon的好处在于对称nat的情况下可以有较好的转发质量。</p>



<p>由于我们不需要考虑nat，所以就只自建network controller。</p>



<h2 class="wp-block-heading">自建方案选型</h2>



<p>由于<s>好看是第一生产力</s>手动添加网络和批准加入十分繁琐，所以需要一个GUI在网络控制器上，而不是按照官方的API使用curl来管理网络。</p>



<p>ztncui（<a href="https://github.com/key-networks/ztncui" target="_blank" rel="noreferrer noopener" title="https://github.com/key-networks/ztncui">Github</a>和<a href="https://key-networks.com/ztncui/" target="_blank" rel="noreferrer noopener" title="https://key-networks.com/ztncui/">官网</a>）是一个基于zerotier-one API的前端，提供类似于官方my.zerotier.com的管理操作，可以添加网络，管理网络和路由等。</p>



<p>其他服务器自然是使用正常的zerotier-one作为节点加入。zerotier还提供有各个嵌入式平台和路由器平台的支持，可以说十分的跨平台了。</p>



<p>还有一点，这次我希望安装的服务器是一台buyvm/frantech的512MB内存的服务器，计划之后还要在上面跑<s>鸟</s>bird，如果要收全表的话，512MB的内存可谓是捉襟见肘，所以首先排除使用docker的安装方式，尽量使用overhead比较小的安装。</p>



<h2 class="wp-block-heading">安装过程</h2>



<h3 class="wp-block-heading">Zerotier</h3>



<p>zerotier在我使用的Debian系发行版上只需要一行命令，注意需要gpg已安装，未安装的话会提示需要安装。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -s https://install.zerotier.com | sudo bash</pre>



<p>如果你是安全nerd，也可以使用gpg验证脚本以后再执行，仍然是一行。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -s 'https://raw.githubusercontent.com/zerotier/ZeroTierOne/master/doc/contact%40zerotier.com.gpg' | gpg --import &amp;&amp; \
if z=$(curl -s 'https://install.zerotier.com/' | gpg); then echo "$z" | sudo bash; fi</pre>



<p>安装完成以后zerotier的源就会被加入apt中，以后只需要使用apt来管理和升级即可。</p>



<h3 class="wp-block-heading">Ztncui</h3>



<p>根据其<meta charset="utf-8"><a href="https://key-networks.com/ztncui/" target="_blank" rel="noreferrer noopener" title="https://key-networks.com/ztncui/">官网</a>的说明，是采用deb打包的方式安装，需要首先下载deb的安装包，然后再安装。</p>



<p>注意！此处具有实效性，建议按照官网安装最新的包！</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -O https://s3-us-west-1.amazonaws.com/key-networks/deb/ztncui/1/x86_64/ztncui_0.7.1_amd64.deb
sudo apt-get install ./ztncui_0.7.1_amd64.deb</pre>



<p>由于ztncui是使用nodejs开发的，因此还需设置一下production mode</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">sudo sh -c "echo 'NODE_ENV=production' >> /opt/key-networks/ztncui/.env"</pre>



<p>ztncui默认监听127.0.0.1:3000，可以使用以下命令转发到本地访问</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ssh -f user@network.controller.machine -L 3333:localhost:3000 -N</pre>



<p>也可以使用以下命令打开一个https端口，没有配置的话使用的是self-signed证书，如果需要配置证书的话可以参考<a href="https://key-networks.com/ztncui/#installation" target="_blank" rel="noreferrer noopener" title="https://key-networks.com/ztncui/#installation">官方教程</a>。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">sudo sh -c "echo 'HTTPS_PORT=3443' > /opt/key-networks/ztncui/.env"</pre>



<p>最后重启ztncui是配置生效。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">sudo systemctl restart ztncui</pre>



<h2 class="wp-block-heading">网络配置</h2>



<p>这部分的配置其实比较简单，按照提示来就可以了，此处放一些截图供调研方案和选型的读者参考。</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="529" height="255" src="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.12-AM.png" alt="" class="wp-image-926" srcset="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.12-AM.png 529w, https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.12-AM-300x145.png 300w" sizes="(max-width: 529px) 100vw, 529px" /><figcaption>创建网络</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="763" height="311" src="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.29-AM.png" alt="" class="wp-image-927" srcset="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.29-AM.png 763w, https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.29-AM-300x122.png 300w" sizes="(max-width: 763px) 100vw, 763px" /><figcaption>创建后结果</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="428" height="500" src="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.45-AM.png" alt="" class="wp-image-928" srcset="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.45-AM.png 428w, https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-11.21.45-AM-257x300.png 257w" sizes="(max-width: 428px) 100vw, 428px" /><figcaption>Easy setup中添加cidr</figcaption></figure></div>



<h2 class="wp-block-heading">最终效果</h2>



<p><s>辣鸡nodejs果然占内存</s> 安装完成以后大概内存占用是111MB，ztncui自己占了60MB，相对来说还可以接受。</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="940" height="139" src="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.35.04-AM.png" alt="" class="wp-image-917" srcset="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.35.04-AM.png 940w, https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.35.04-AM-300x44.png 300w, https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.35.04-AM-768x114.png 768w" sizes="(max-width: 940px) 100vw, 940px" /><figcaption>mtr</figcaption></figure></div>



<p>可以看到，一天的丢包率只有0.1%，我认为是可以接受的。</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="653" height="263" src="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.36.31-AM.png" alt="" class="wp-image-918" srcset="https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.36.31-AM.png 653w, https://blog.eastonman.com/wp-content/uploads/2021/12/Screen-Shot-2021-12-06-at-10.36.31-AM-300x121.png 300w" sizes="(max-width: 653px) 100vw, 653px" /><figcaption>iperf3</figcaption></figure></div>



<p>我的两台vps之间也能跑到比较高的速率。</p>The post <a href="https://blog.eastonman.com/blog/2021/12/zerotier/">全球大内网与Zerotier</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2021/12/zerotier/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>论大学之义</title>
		<link>https://blog.eastonman.com/blog/2021/08/meaning-of-university/</link>
					<comments>https://blog.eastonman.com/blog/2021/08/meaning-of-university/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Sat, 07 Aug 2021 14:31:14 +0000</pubDate>
				<category><![CDATA[生活]]></category>
		<category><![CDATA[Original]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=847</guid>

					<description><![CDATA[<p>每个备战高考的学子都曾想过：为什么上大学？对这个问题的回答将直接指引青年的人生，指引他们在进入大学生活后的行动。</p>
The post <a href="https://blog.eastonman.com/blog/2021/08/meaning-of-university/">论大学之义</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 16 分钟</p>
<p>此演讲稿经授权首发于本站，任何转载或引用需署名并保留原文链接。</p>



<p>强调大学之义有其现实因素。青年兴则国家兴，青年强则国家强。提高青年学习和奋斗的自觉性是谈论大学之义的原因之一。大学前奋发图强，大学后碌碌无为，乃当今大学生的常态。我所属大学在广东乃至全国的分数都不低，工科亦属中国第二，世界第四；但是身边许多大学生仍没有自己的人生目标，入大学如出狱般无拘无束。我们学院衡水中学进来的十数名学生，以其分数若在广东，实是清北之材；但他们的大学成绩排在倒数，只因大学后早上旷课，晚上喝酒，学业荒废。“衡水模式”之弊在于，过度注重时间管理和考试，以至学生没有空余时间来思考人生真正的意义，发掘自己真正的兴趣所在，让学生变成了在高压管理制度之下随波逐流的机器。但这样的惯性在大学无以为继，因为上大学后，没有衡水的老师家长推着学生学习。只有想通了上大学的意义，在大学才有动力去奋斗。</p>



<p>谈论大学之义的必要性之二，在中国经济发展对大学生的专业选择提出了新要求。现在某些职校生比普通大学生更容易就业。这主要是因为经过改革开放，社会经济条件产生巨变。四十年前“唯学历论”盛行，因为彼时大学生很少，能上大学的往往在智商上，自觉性上，更胜他人；现今大学生很多，除“物以稀为贵”的影响外，当下中国不再是四十年前敢闯敢拼就能干出一番事业的社会。生产力的提升与社会分工的日渐细化互为因果，而分工细化导致在大学综合水平相差无几的情况下，选择一个好的专业比一所名气更大的大学，对人生的影响要更为正面。转行在当下显得不那么容易。一个学化学的学生如果从来没有接触过计算机，完全不知道计算机的体系架构和组成原理，那么他将无法满足企业的需求。倘若他想对计算机基本原理了解个大概，则需要许多时间；若要精通则又要数年时间。企业几乎不可能聘用一个不熟悉业务甚至可能带来损失的人，因为当下有诸多熟悉业务的大学生可供选择。分工细化和私营企业的增多，私有制经济的蓬勃发展，导致社会“唯学历论”向“唯能力论”的转变。甚至于国企也开始进行整合重组，加强对内部员工的能力要求。现今中国社会需要的是干实事的人，需要的是某领域的专才。以名气为主的大学选择方式日渐式微，而只有想清楚自己上大学的真正原因，青年学生才能正确选择专业，发展特长，过属于自己的人生。</p>



<p>“大学之道，在明明德，在亲民，在止于至善”。私以为今日之大学，也应承大学之道。中国大学是青年学生提高知识水平的地方，是青年学生培养专业能力的地方，也是青年学生提高思想觉悟的地方。欧洲最初的大学是一帮知识分子为了摆脱教会的束缚，为获取学术自由，为讨论学术观点聚集而成的组织，后来逐渐演化为教育机构。故当今大学担负两个责任：一是教书育人，即培养本科生和研究生；二是研究考察，即探索世界，发掘知识。二者之间，孰轻孰重？从大学的发源上讲，研究考察为重；从大学承担的社会功能来看，教书育人为重。教书育人一是传授知识，而是建设思想。传授知识必非纸上谈兵，空有理论；而是理论与实践相结合，以提高学生的动手能力为首要。目前中国大学有延续高中填鸭式教育的现象，而缺失技能的培养。直接证明即中国大学普遍不重视实验，没有像美国大学那样的课程大作业。所谓“大作业”就是实践过程，要求学生利用学到的知识做出实际的东西，包括但不限于社会调查研究（社会类学科课程）、可运行程序（计算机类）、对某个数理化问题的探究结论（数理化课程）等。这些大作业不只是对创新性的培养，也是对技能的培养。如果不上机写程序，C语言课教的永远只是理论上的C语言；如果不做问题探究，不做实验室里的实验，化学永远只是纸上的化学，生物课也不能教会学生怎么制造生物试剂。除传授知识和培育技能外，大学的目的既然是培养中国之栋梁，非外国之栋梁，那么仍需要对学生进行思想建设，培养家国情怀和社会责任。综合而论，传递知识、技能、思想这三点共同构成了大学在青年学生的人生中扮演的角色。</p>



<p>“见贤思齐焉，见不贤而内自省也”。很多人知道在大学能见天下贤士，拓展自身见识，即大学相比高中能为青年学生提供更加广阔的视野和资源。人们觉得这就是上清北的好处，即能够见识到全国各地最顶尖的学生。这样的说法正确在于北京的确有最顶尖的人才和学生，错误在于认为其他大学没有顶尖的学生。此种认识的根源在于“唯分数论”，在于单一的人才评定标准。其谬误在于以高考成绩作为评价学生是否优秀的唯一标准，而在当今中国“唯学历论”向“唯能力论”转化的情势之下，显然是站不住脚的。鼓励上清北的真正原因在于清北作为首都学校，拥有全国最丰富的资源。这一点从每年国家的经费拨款就能看出来。但资源丰富不代表任何人都能享有，在人类社会中存在资源向少数集中的趋势，大多数勉强上清北的学生会被边缘化。我自己在理学院的成绩排名第四，但如果放在计算机系则排不上号。正因如此我在理学院给辅导员和教师提的意见大部分都得到采纳；而我在计算机课上提的意见基本均被无视。做机器学习需要专门的显卡，我作为理学院的学生向老师申请就能拿到6张；而在计算机学院则根本找不到这样的渠道。同时，资源也包含教师资源——这体现在专业选择上。对要学人工智能和机器学习的学生，最优秀的选择是南京大学，因为南京大学有全国最顶级的教授周志华，南京大学的人工智能学院也是他亲手筹办，课程设置和培养体系可谓是全国典范。所以要知道具体上哪所大学，必先了解该大学具备的资源，包括硬件资源和教师资源，而不可人云亦云。即专业比学校更重要。</p>



<p>专业选好了能保证学生的知识水平和在社会上的被需要程度，但这只是大学里第三重要的。第二重要的是能力的培养。这里能力广泛地指在学科领域的能力，在人际交往上的能力，在自我管理上的能力，在深入思考社会问题上的能力等。由于“唯能力论”的当下社会要求，只知学习与工业需求脱节的大学课程是行不通的。这也是当下这么多人选择读研的原因。读研热潮的根本在于本科无法锻炼业界真正需要的能力，只能把出来工作的时间往后拖延，期望读研时能够学到更多实践能力。其实这纯粹是空想。在中国，知识的传授方式沿袭苏联的灌输式传统。研究生唯一的优势就是项目和实验做得多，即动手实践机会多。那么为何不在本科就更多地进行实践活动？“纸上得来终觉浅，绝知此事要躬行”。能力是在实践中培养的，真正的知识也是在实践中获取的。比如在学微积分这件事情上，如果一个人终其一生均是平凡的屠夫，那么学微积分对他无半点大用。只有成为工程师，成为数学家，成为物理学家，把不论是所谓思维方式还是微积分本身在生活中真正地实践出来，才算是真正懂得了微积分，微积分才算是真正地影响了他的人生。但实践需要平台和资源，所以在大学要跟对导师，要主动跟导师做项目。导师的作用就是给学生实践的机会。同时实践并不仅限于学科领域的实践，也涉及到人际交往的实践，社会活动意义上的实践如义工活动等。这一点每个青年学生须自行发掘其中方法，但首要是形成这样的认识，即提升自我能力的迫切需要，而非践行“大学享乐主义”。</p>



<p>思想是行动的指南，在大学中最重要的正是青年学生的思想。为什么人当为国奉献？如果想不通这个问题，青年学生必不能发自真心地为人民服务。从人类物质与精神追求的关系来看，物质富足的人必然要寻求自我价值的实现，否则内心就会感到空虚。为人民服务正是青年实现自我价值的最好方法。当个人在群体中奉献自我，他才会感受到社会认同。人生的意义只存在于人类群体当中，而不存在于个人；或者说只有当一个人置身于社会之中时，他所做的除开谋生之外的一切事情，才能够说是有意义的，因为人对意义的感知正是在群体当中发源的。古代无数文人志士为民请命，正是此种奉献精神的体现。从个人成就的来源上讲，每个人生活在社会中，他们所取得任何成就都不仅来源于他们自己。现代互联网大亨的成功更多是国家政策的扶持，是当今中国的经济条件给了他们机会，而这个条件是全体人民共同劳动的结果。如果没有大家的共同劳动，社会生产力就不能得到提升，互联网就没有市场需求，他们就不会成为富翁。同样，学生享有的教育资源是所有老师和学生共同努力建设的结果，也是国家政策扶持，社会群众支持的结果。学校的钱来自于国家，国家的钱来自于纳税人，而纳税人就是全体劳动人民。因此人不回馈社会则与“不孝子孙”同，任何人特别是青年学生，应当以服务社会为己任。这在学生的身份上落实正是服务老师，服务学生。看见学校的课程办得不合理，青年应当勇于提出建议；看见其他学生对专业的就业方向感到迷茫，有能力的可以主动站出来为其他人开设讲座，解除疑惑；没打过竞赛的学生在赛前感到无所适从，有经验的学生可以主动请缨传授自己的方法，也可以像学院老师建议请业界工程师前来开讲；学校某些政策制定不得人心，青年学生也应当站出来维护公理。为人民服务的方法除此之外仍有许多，但以形成其意识为首要。</p>



<p>“路漫漫其修远兮，吾将上下而求索”。认识到位了，更重要的是行动。所谓“知行合一”，知而不行则不为真知。“行”是唯一“知”，实践出真知。在繁忙的高三学业中抽出时间来思考自己的人生价值，思考自己的大学选择，是非常必要的。</p>



<p><strong>作者简介：</strong>徐兆彬，2019年广东实验中学南山一班毕业，现就读于哈尔滨工业大学（深圳）理学院，目前成绩排学院第四，曾获BDCI（CCF大数据与计算智能大赛）全国第七名，第九届泰迪杯数据挖掘挑战赛全国二等奖，CCPC（中国大学生程序设计竞赛）威海站银牌，全国大学生数学竞赛黑龙江省省级一等奖，Kaggle平台上两场比赛的第一名。大一入学即进实验室跟随户保田教授做自然语言处理的文本生成领域，目前已向人工智能领域顶级会议ICLR投稿一篇论文。曾任微软俱乐部主席，为学生开展多次竞赛讲座。同时在理学院建立竞赛群体分享竞赛经验，帮学生提升自身能力，明了专业未来发展方向；也曾多次向学院提出课程改革建议并得到采纳。</p>



<p>此演讲稿是经大佬授权首发于本站，希望能够使更多的人得到启发，任何转载或引用需署名并保留原文链接。</p>The post <a href="https://blog.eastonman.com/blog/2021/08/meaning-of-university/">论大学之义</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2021/08/meaning-of-university/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Bash History的使用技巧</title>
		<link>https://blog.eastonman.com/blog/2021/06/bash-history-tricks/</link>
					<comments>https://blog.eastonman.com/blog/2021/06/bash-history-tricks/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Thu, 10 Jun 2021 14:51:35 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Bash]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=792</guid>

					<description><![CDATA[<p>Bash是常见的shell环境，大多数Linux发行版也都带有Bash，因此高效地利用history可以节省手动输入重复命令的时间。本文分享一下几个我自己使用的技巧和配置。</p>
The post <a href="https://blog.eastonman.com/blog/2021/06/bash-history-tricks/">Bash History的使用技巧</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 3 分钟</p>
<p>Bash是常见的shell环境，大多数Linux发行版也都带有Bash，因此高效地利用history可以节省手动输入重复命令的时间。本文分享一下几个我自己使用的技巧和配置。</p>



<h3 class="wp-block-heading">搜索</h3>



<p>这个技巧大家应该多少都知道，就是在输入界面按<code>Ctrl-R</code>就可以进行搜索了，返回的结果是最近一个匹配的命令，然后按回车就可以执行。</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="526" height="220" src="https://blog.eastonman.com/wp-content/uploads/2021/06/image.png" alt="" class="wp-image-794" srcset="https://blog.eastonman.com/wp-content/uploads/2021/06/image.png 526w, https://blog.eastonman.com/wp-content/uploads/2021/06/image-300x125.png 300w" sizes="(max-width: 526px) 100vw, 526px" /><figcaption>Ctrl-R</figcaption></figure></div>



<p>但是有的时候搜索结果只是与我想要的很相近，但又不完全一样，我希望能够在执行之前对命令做小的修改，那怎么办?其实此时按Esc就可以退出并且编辑。</p>



<p>还有一个方式是利用<code>:p</code>，<code>:p</code>在Bash中表示打印但不执行，因此如果我们想要查看某个前缀的命令，就可以使用<code>!prefix:p</code>来打印。</p>



<h3 class="wp-block-heading">HISTIGNORE</h3>



<p>HISTIGNORE是一个环境变量，它指示Bash什么样的命令不要加入history中，例如这样的一个变量。</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">export HISTIGNORE='pwd:exit:fg:bg:top:clear:history:ls:uptime:df'</pre>



<p>就可以把无意义的交互命令从history中去除了。如果你经常使用<code>ll</code>或者<code>la</code>这样的简写，那么把他们加入HISTIGNORE中或许是一个不错的选择。</p>



<h3 class="wp-block-heading">HISTCONTROL</h3>



<p>HISTCONTROL也是一个环境变量，它有几个选择</p>



<ul class="wp-block-list"><li>ignorespace：以空格开头的所有命令都不会加入history中</li><li>ignoredups：重复的命令不会加入history中</li><li>ignoreboth：打开以上的两个特性</li></ul>



<p>通常一些发行版已经带有ignoredups的选项，如果你想要避免某些特定的命令计入history中，那么可以打开ignorespace选项。</p>



<h3 class="wp-block-heading">并行的窗口之间的history问题</h3>



<p>这个问题相信很多同学也会有，在远程管理服务器的时候，通常为了某些监视任务或者耗时长的任务不阻塞人的操作，会使用各种窗口复用工具，比如<code>tmux</code>或者<code>screen</code>，那么由于Bash在关闭的时候才会将history写入<code>.bash_history</code>文件，那么大多数时候当我们关闭这些窗口以后就只有最后一个关闭的窗口的History被记录了下来。</p>



<p>我们可以使用</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">shopt -s histappend</pre>



<p>来指示Bash讲history附加到原本的history上而不是覆盖。</p>



<h3 class="wp-block-heading">使用!来执行history中的命令</h3>



<p>!可以用来执行命令，比如!10就是执行history中的第10条命令。当然也可以执行最近的第几条命令，比如!-3就是倒数第三条命令。</p>



<p>这里还可以结合<code>:p</code>使用，以避免直接执行带来的风险。</p>



<p>如果你一定要避免所有的风险，每次都希望检查命令再执行，而又不想输入<code>:p</code>，那么Bash还有一个选项可以做到这一点：<code>shopt -s histverify</code>。这个选项会使得所有用!执行的命令都需要按Enter键确认才会执行。</p>



<h3 class="wp-block-heading">使用上一个命令的参数</h3>



<p>!$和!*分别会将上一个命令的最后一个参数和所有参数填补到这个位置，例如</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ mv list.txt items.txt
$ vim !$
vim items.txt
$ cp !$ shopping.txt
cp items.txt shopping.txt</pre>



<p></p>The post <a href="https://blog.eastonman.com/blog/2021/06/bash-history-tricks/">Bash History的使用技巧</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2021/06/bash-history-tricks/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>现代处理器结构</title>
		<link>https://blog.eastonman.com/blog/2021/05/modern-processor/</link>
					<comments>https://blog.eastonman.com/blog/2021/05/modern-processor/#comments</comments>
		
		<dc:creator><![CDATA[Easton Man]]></dc:creator>
		<pubDate>Mon, 17 May 2021 15:10:04 +0000</pubDate>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[计算机系统]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[体系结构]]></category>
		<category><![CDATA[处理器]]></category>
		<guid isPermaLink="false">https://blog.eastonman.com/?p=639</guid>

					<description><![CDATA[<p>一个简短的、直接的现代处理器微架构设计介绍。</p>
The post <a href="https://blog.eastonman.com/blog/2021/05/modern-processor/">现代处理器结构</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></description>
										<content:encoded><![CDATA[<p class="wpwc-reading-time">预计阅读时间： 36 分钟</p>
<p>一个简短的、直接的现代处理器微架构设计介绍。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>当今的机器实在是非常原始，它们只能理解区区几个简单的指令，比如“向左”、“向右”和“生产汽车”。</p><p><em>Today&#8217;s robots are very primitive, capable of understanding only a</em> <em>few simple instructions such as &#8216;go left&#8217;, &#8216;go right&#8217; and &#8216;build car&#8217;.</em></p><cite><a href="https://www.azquotes.com/quote/1403947" target="_blank" rel="noreferrer noopener">John Thomas Sladek</a></cite></blockquote>



<p><strong>警告：</strong>本文旨在以非正式和风趣的语言讲述严肃的科学。</p>



<p><strong>警告2：</strong>长文！预计阅读时间36分钟。</p>



<p>本文主要向计算机专业的低年级学生和对现代处理器结构感兴趣的读者介绍有关处理器微架构的一些概念。具体来说，有以下几个方面：</p>



<ul class="wp-block-list"><li>流水线（超标量执行、乱序执行、超长字指令、分支预测）</li><li>多核和超线程（同步超线程 SMT）</li><li>SIMD指令集（SSE、AVS、NEON、SVE）</li><li>缓存和缓存机制</li></ul>



<p>听起来内容很深奥，<strong>但是，不要害怕！</strong>这篇文章将带你快速地了解这些看似只有处理器设计从业者或者是体系结构专家才能了解的东西。也许你很快就可以和你的同学/朋友吹牛了<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f923.png" alt="🤣" class="wp-smiley" style="height: 1em; max-height: 1em;" />。</p>



<h2 class="wp-block-heading">超10G！</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>超10G！全人类感谢你！</p><cite>——某up</cite></blockquote>



<p>主频越高，CPU性能越好，这似乎是很多人的误区（不包括以上引用的up主），但是，从上古时期开始，CPU的性能和主频就没有直接关系。那么这个刻板印象是从哪里来的呢？<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>让我们来看看一些上古（上世纪90年代末）的处理器数据&#8230;</p>



<figure id="megahertztable" class="wp-block-table is-style-stripes"><table><tbody><tr><td><strong>主频</strong></td><td><strong>型号</strong></td><td><strong>SPECint95</strong></td><td><strong>SPECfp95</strong></td></tr><tr><td>195 MHz</td><td>MIPS R10000</td><td>11.0</td><td>17.0</td></tr><tr><td>400 MHz</td><td>Alpha 21164</td><td>12.3</td><td>17.2</td></tr><tr><td>300 MHz</td><td>UltraSPARC</td><td>12.1</td><td>15.5</td></tr><tr><td>300 MHz</td><td>Pentium II</td><td>11.6</td><td>8.8</td></tr><tr><td>300 MHz</td><td>PowerPC G3</td><td>14.8</td><td>11.4</td></tr><tr><td>135 MHz</td><td>POWER2</td><td>6.2</td><td>17.6</td></tr></tbody></table><figcaption>1997年的处理器性能</figcaption></figure>



<p>SPEC是一个当年常用的性能测试工具，乔布斯在宣布苹果的Macbook由IBM PowerPC平台转向Intel的酷睿平台的时候就在发布会上展示了SPEC的性能提升。</p>



<p>从表中可以看到，为什么300MHz的处理器有这么不同的性能差异？为什么低主频的CPU反而吊打高主频的？</p>



<p>什么？你说这都是上古的数据？那就来一个最近的：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="903" height="340" src="https://blog.eastonman.com/wp-content/uploads/2021/05/image-1.png" alt="" class="wp-image-651" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/image-1.png 903w, https://blog.eastonman.com/wp-content/uploads/2021/05/image-1-300x113.png 300w, https://blog.eastonman.com/wp-content/uploads/2021/05/image-1-768x289.png 768w" sizes="(max-width: 903px) 100vw, 903px" /></figure>



<p>超到一半人类感谢你（5GHz）的Intel i9-9900K居然被M1吊打？</p>



<p>是的，你没有看错，这就说明显然除了主频以外还有一些什么东西，那就是——</p>



<h2 class="wp-block-heading">流水线和指令级并行</h2>



<p>指令在处理器中是一个接一个的执行的，对吗？不完全对。这样的说法可能是直观的，但是并不是事实，实际上，从80年代开始，CPU就不再是完全顺序执行每个指令了。现代处理器可以同时执行不同指令的不同阶段，甚至有的处理器也可以完全同时地执行多个指令。</p>



<p>让我们来看一看一个简单的四级流水线是怎么构成的。指令被分成四个部分：<strong>取指、译码、执行和写回</strong>。</p>



<p>如果CPU完全顺序执行，那么每条指令需要花费4个周期才能执行完毕，IPC=0.25（Instruction per cycle）。当然，古老一点的时期更喜欢使用CPI，因为当时的处理器普遍不能做到每周期执行一条指令。但是现在时代变了，你能接触到的任何一个桌面级处理器都可以在一个周期内执行一条、两条甚至是三条指令。</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="384" height="145" src="https://blog.eastonman.com/wp-content/uploads/2021/05/sequential2.png" alt="" class="wp-image-654" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/sequential2.png 384w, https://blog.eastonman.com/wp-content/uploads/2021/05/sequential2-300x113.png 300w" sizes="(max-width: 384px) 100vw, 384px" /><figcaption>顺序执行的处理器</figcaption></figure></div>



<p>正如你所看到的，实际上CPU内负责运算的组件（ALU）十分的悠闲，甚至只有25%的时间在干活。什么？怎么压榨ALU？我看你很有资本家的天赋嘛&#8230;</p>



<p>好吧，现代处理器确实有手段压榨这些ALU（对，现代处理器也不止一个ALU）。一个很符合直觉的想法就是既然大部分的阶段CPU都不是完全占用的，那么将这些阶段重叠起来就好了。确实，现代处理器就是这么干的。</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="384" height="145" src="https://blog.eastonman.com/wp-content/uploads/2021/05/pipelined2.png" alt="" class="wp-image-660" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/pipelined2.png 384w, https://blog.eastonman.com/wp-content/uploads/2021/05/pipelined2-300x113.png 300w" sizes="(max-width: 384px) 100vw, 384px" /><figcaption>流水线执行的处理器</figcaption></figure></div>



<p>现在我们的处理器大多数时候一个周期可以执行一条指令了，看起来不错！这已经是在没有增加主频的情况下达到四倍的加速了。</p>



<p>从硬件的角度来看，每级流水线都是由该级的逻辑模块构成的，CPU时钟就像一个水泵，每次把信号（或者也可以说是数据）从一级泵到下一级，就像这样：</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="380" height="96" src="https://blog.eastonman.com/wp-content/uploads/2021/05/pipelinedmicroarch2.png" alt="" class="wp-image-661" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/pipelinedmicroarch2.png 380w, https://blog.eastonman.com/wp-content/uploads/2021/05/pipelinedmicroarch2-300x76.png 300w" sizes="(max-width: 380px) 100vw, 380px" /><figcaption>流水线微架构</figcaption></figure></div>



<p>事实上，现代处理器除了以上这样简单的结构，首先还有很多额外的ALU，比如整数乘法、加法、位运算、浮点数的各种运算等等，几乎每种常用的运算都有至少一个ALU。其次，如果前一条指令的结果就是下一条指令的操作数，那么为什么还要把数据写回寄存器呢？因此就出现了Bypass（前递）通路，用于在这种情况下直接将数据重新送到运算器的输入端口。综合起来，详细一点的流水线微架构应该长这样：</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="390" height="92" src="https://blog.eastonman.com/wp-content/uploads/2021/05/pipelinedfunctionalunits2.png" alt="" class="wp-image-665" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/pipelinedfunctionalunits2.png 390w, https://blog.eastonman.com/wp-content/uploads/2021/05/pipelinedfunctionalunits2-300x71.png 300w" sizes="(max-width: 390px) 100vw, 390px" /><figcaption>详细的流水线微架构</figcaption></figure></div>



<h2 class="wp-block-heading">更深的流水线——超级流水线！</h2>



<p>自从CPU主频由于某种原因（某种神秘力量？<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f608.png" alt="😈" class="wp-smiley" style="height: 1em; max-height: 1em;" />）很多年没有大的进步以来（对的，超频榜第一还是AMD的推土机架构CPU），流水线的设计几乎成为了CPU厂商的竞赛主场。加深的流水线首先可以继续增大实际的IPC（理论上限仍是1），其次可以避免流水线对时序的影响。这与晶体管的特性有关，感兴趣的读者可以上网搜一搜多级流水线结构为什么会影响时序和最终综合出的主频。</p>



<p>在2000-2010年间，这种竞赛达到了最高峰，那时候的处理器甚至可以有高达31级的流水线。但是超深的流水线带来的是结构上的复杂和显著增大的动态调度模块设计难度，因此，从那以后就没有再出现过使用这么多级流水线的CPU了。作为对比，目前（2021年）的处理器多半视应用场景的不同采用10-20级不等的流水线。</p>



<p>x86和其它CISC处理器通常有着更深的流水线，因为他们在取指和译码阶段有数倍的任务要做，所以通常使用更深的流水线来避免这一阶段带来的性能损耗。</p>



<h2 class="wp-block-heading">多发射——超标量处理器</h2>



<p>既然整数的运算器和浮点数的运算器以及其它的的一些ALU互相之间都是没有依赖的，自己做自己的事情，那为什么不进一步压榨它们，让他们尽可能地一起忙起来呢？这就出现了多发射和超标量处理器。多发射的意思是处理器每个周期可以“发射”多于一条的指令，比如浮点运算和整数运算的指令就可以同时执行且互不干扰。为了完成这一点，取指和译码阶段的逻辑必须加强，这就出现了一个叫做<strong>调度器</strong>或者<strong>分发器</strong>的结构，就像这样：</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="437" height="335" src="https://blog.eastonman.com/wp-content/uploads/2021/05/superscalarmicroarch2.png" alt="" class="wp-image-685" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/superscalarmicroarch2.png 437w, https://blog.eastonman.com/wp-content/uploads/2021/05/superscalarmicroarch2-300x230.png 300w" sizes="(max-width: 437px) 100vw, 437px" /><figcaption>超标量处理器微架构</figcaption></figure></div>



<p>或者我们来看一张实际的Intel Skylake架构的调度器，图中红圈的就是负责每周期“发射”指令的调度器。</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2021/05/image-2.png" alt="" class="wp-image-684" width="-127" height="-87" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/image-2.png 792w, https://blog.eastonman.com/wp-content/uploads/2021/05/image-2-300x207.png 300w, https://blog.eastonman.com/wp-content/uploads/2021/05/image-2-768x529.png 768w" sizes="(max-width: 792px) 100vw, 792px" /><figcaption>Skylake 调度器</figcaption></figure></div>



<p>当然，现在不同的运算有了不同的“数据通路”，经过的运算器也不同。因为不同的运算器内部可能也分不同的执行阶段，于是不同的指令也就有了不同的流水线深度：简单的指令执行得快一些，复杂的指令执行得慢一些，这样可以降低简单指令的<strong>延迟</strong>（我们很快就会涉及到）。某些指令（比如除法）可能相当耗时，可能需要数十个周期才能返回，因此在编译器设计中，这些因素就变得格外重要了。有兴趣的读者可以思考<strong>梅森素数</strong>在这里的妙用。</p>



<p>超标量处理器中指令流可能是这个样子的：</p>



<figure class="wp-block-gallery alignwide columns-2 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img loading="lazy" decoding="async" width="384" height="145" src="https://blog.eastonman.com/wp-content/uploads/2021/05/superscalar2.png" alt="" data-id="690" data-full-url="https://blog.eastonman.com/wp-content/uploads/2021/05/superscalar2.png" data-link="https://blog.eastonman.com/?attachment_id=690" class="wp-image-690" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/superscalar2.png 384w, https://blog.eastonman.com/wp-content/uploads/2021/05/superscalar2-300x113.png 300w" sizes="(max-width: 384px) 100vw, 384px" /></figure></li><li class="blocks-gallery-item"><figure><img loading="lazy" decoding="async" width="384" height="145" src="https://blog.eastonman.com/wp-content/uploads/2021/05/superpipelinedsuperscalar2.png" alt="" data-id="689" data-full-url="https://blog.eastonman.com/wp-content/uploads/2021/05/superpipelinedsuperscalar2.png" data-link="https://blog.eastonman.com/?attachment_id=689" class="wp-image-689" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/superpipelinedsuperscalar2.png 384w, https://blog.eastonman.com/wp-content/uploads/2021/05/superpipelinedsuperscalar2-300x113.png 300w" sizes="(max-width: 384px) 100vw, 384px" /></figure></li></ul></figure>



<p>现代处理器一般都有相当多的发射端口，比如上面提到的Intel Skylake是八发射的结构，苹果的M1也是八发射的，ARM最新发布的N1则是16发射的处理器。</p>



<h2 class="wp-block-heading">显式并行——超长指令集</h2>



<p>当兼容性不成问题的时候（很不幸，很少有这种时候），我们可以设计一种指令集，显式地指出某些指令是可以被并行执行的，这样就可以避免在译码时进行繁复的依赖检验。这样理论上可以使处理器的硬件设计变得更加简单、小巧，也更容易取得更高的主频。</p>



<p>这种类型的指令集中，“指令”实际上是“一组子指令”，这使得它们拥有非常多的指令，进而每个指令都很长，例如128bits，这就是<strong>超长指令集（VLIW）</strong>这个名字的来源。</p>



<p>超长指令集处理器的指令流和超标量处理器的指令流十分的类似，只是省去了繁杂的取指和译码阶段，像这样：</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="384" height="145" src="https://blog.eastonman.com/wp-content/uploads/2021/05/vliw2.png" alt="" class="wp-image-697" srcset="https://blog.eastonman.com/wp-content/uploads/2021/05/vliw2.png 384w, https://blog.eastonman.com/wp-content/uploads/2021/05/vliw2-300x113.png 300w" sizes="(max-width: 384px) 100vw, 384px" /><figcaption>VLIW指令流</figcaption></figure></div>



<p>除了硬件结构，超长指令集处理器和超标量处理器十分的相似，尤其是从编译器的角度来看（我们很快也会谈到）。</p>



<p>但是，超长指令集处理器通常被设计成<strong>不检查依赖</strong>的，这就使得它们必须依赖编译器的魔法才能保证结果的正确，而且，如果发生了缓存缺失，那它们不得不整个处理器都停下来，而不是仅仅停止遇到缓存缺失问题的那一条指令。编译器会在指令之间插入“nops”（no operations）——即空指令，以保证有数据依赖的指令能够正确地执行。这无疑增加了编译器的设计难度和编译所需的时间，但是这同时节省了宝贵的处理器片上资源，通常也能有略好的性能。</p>



<p>现在仍在生产的现代处理器中<strong>并没有</strong>采用VLIW指令集的处理器。Intel曾经大力推行过的IA-64架构就是一个超长指令集（VLIW）架构，由此设计的“Itanium”系列处理器在当时也被认为是x86的继承者，但是由于市场对这个新架构并不感冒，所以最终这个系列没有发展下去。现代硬件加速最火热的方向是GPU，其实GPU也可以看作是一种VLIW架构的的处理器，只不过它将VLIW架构更进一步，使用“核函数”代替指令，大大增加了这种体系结构的可扩展性，有兴趣的读者也可以了解相关方面的内容。</p>



<h2 class="wp-block-heading">数据依赖和延迟</h2>



<p>我们在流水线和多发射这条路上能走多远？既然多发射和多级流水线这么好，那为什么不做出50级流水线、30发射的处理器？我们来讨论以下两条指令：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="c" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">a = b * c;
d = a + 1;</pre>



<p>第二条指令<strong>依赖于</strong>第一条指令——处理器在完成前一条指令之前无法执行下一条指令。这是一个很严重的问题，这样一来，多发射就没有用武之地了，因为无论你制造出了多少发射的处理器，这两条指令还是只能顺序地执行（除去取指等部分）。有关依赖和消除的问题我们会在后面讨论。</p>



<p>如果第一条指令是一个简单的加法指令，那么加法器在执行完毕后可以通过Bypass通路（前递）将数据传回ALU的输入端口并继续计算，这样流水线才可以正常工作。但是很不幸，第一条指令是一个需要多周期才能完成的乘法（目前的大多数CPU没有使用单周期乘法，因为复杂的逻辑通常会损害主频），这样的话，处理器为了等待第一条指令完成就不得不往流水线中加入若干“气泡”也就是类似于“nops”的指令来保证运算的正确性。</p>



<p>一条指令到达运算器的输入端口和执行结果可用之间需要耗费的CPU周期称为<strong>指令的延迟</strong>。流水线越深，指令的延迟就越高，所以更深的流水线如果无法有效地填满，那么结果只能是很高的指令延迟而无益于处理器的性能。</p>



<p>从编译器的角度（考虑了Bypass，硬件工程师口中的延迟通常不包括Bypass），现代处理器的指令延迟通常是：整数乘加和位操作1周期，浮点数乘加2-6周期不等，sincos这种复杂指令10+周期，最后是可能长达30-50周期的除法。</p>



<p>访存操作的延迟也是一个很麻烦的问题，因为它们通常是每条指令最开始执行的步骤，这使得它们造成的延迟很难用别的方式补偿。除此以外，他们的延迟也很难预测，因为延迟很大程度上取决于缓存是否命中，而缓存是动态调度的（我们很快也会讲到）。</p>



<h2 class="wp-block-heading">分支和分支预测</h2>



<p>另外一个流水线的重要问题就是分支，我们来看一看接下来的一段程序：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="c" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">if (a > 7) {
    b = c;
} else {
    b = d;
}</pre>



<p>编译成的汇编程序将会是这样：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">    cmp a, 7    ; a > 7 ?
    ble L1
    mov c, b    ; b = c
    br L2
L1: mov d, b    ; b = d
L2: ...</pre>



<p>现在想象一个流水线处理器来执行这一段程序。当处理器执行到第二行，也就是第二行的跳转命令到达处理器的执行器的时候，它肯定已经把后面的所有指令都提前从内存中取出存并完成了译码工作了。但是，究竟跳转的是哪一条指令？是3，4行还是第5行？在跳转命令到达执行器之前我们并不知道应该跳转到哪里。在一个深流水线的处理器中，似乎不得不停下来等待这个跳转命令，再重新往流水线中填入新的指令。这当然是不可接受的，程序中，尤其是循环时，分支跳转的命令占比很大，如果每次都等待这条命令的完成，那么我们的流水线就不得不经常地暂停，而我们通过流水线取得的性能提升也将不复存在。</p>



<p><strong>于是现代处理器会做出猜测</strong>。什么？处理器竟然靠猜，我还以为发达的处理器设计行业能给出更好的解决方案呢！先不要着急，实际上程序中分支的跳转是有规律的，现代处理器分支预测的准确度通常能达到99%以上（虽然分支预测也是Intel的spectre和meltdown漏洞的来源<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f47f.png" alt="👿" class="wp-smiley" style="height: 1em; max-height: 1em;" />）。</p>



<p><strong>处理器会沿着预测的分支执行下去</strong>，这样一来，我们的处理器就可以保持流水线和运算器的占用，并高速地执行下去。当然，执行的结果还不能作为最终的结果，只有在分支跳转命令的结果出来以后，预测正确的结果才会被写回（commit或retire）。那猜错了怎么办？那处理器也没有好的办法，只能重新从另一个分支开始进入流水线，在高度流水化的现代处理器里，分支预测错误的代价（<strong>分支预测的错误惩罚</strong>）是相当高昂的，通常会达到数十个CPU周期。</p>



<p>这里的关键在于，<strong>处理器如何做出预测</strong>。通常而言，分支预测分为静态和动态两种。</p>



<p><strong>静态分支预测即处理器做出的猜测与运行时的状态无关</strong>，而对跳转的优化由编译器完成。静态预测通常有一律跳或者往后跳预测不跳，往前跳转则预测跳。后者通常效果更好，原因是循环中一般会有大量向前的跳转指令。</p>



<p><strong>动态分支预测则是根据跳转指令的历史决定是否跳转</strong>。一个最简单的动态分支预测器就是<strong>2位饱和计数器</strong>，它是一个四个状态的状态机，特点是只有连续两次预测错误才会更改预测方向。它已经能在大部分场合下取得90%以上的预测正确率。</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2021/05/Branch_prediction_2bit_saturating_counter-dia.svg" alt="" class="wp-image-722"/><figcaption>2位饱和计数器 Author: Afog CC BY-SA 3.0</figcaption></figure></div>



<p>这种预测器在交替出现跳和不跳的分支指令时表现不佳，于是人们又发明了n级自适应分支预测器，它的原理与2位饱和计数器类似，不过它能够记住过去n次的历史，在重复的跳转模式中表现优异。</p>



<p>不幸的是，分支预测是各个CPU厂商的核心竞争力之一，大多数优秀的分支预测技术也是重要的商业机密，于是在这个方面并没有太多可以深入的。Cloudflare最近发布了一篇<a href="https://blog.cloudflare.com/branch-predictor/" target="_blank" rel="noreferrer noopener" title="https://blog.cloudflare.com/branch-predictor/">博文</a>深入测试了x86和ARM的M1上分支预测器的特征，有兴趣的读者可以看看。</p>



<h2 class="wp-block-heading">去除分支语句</h2>



<p>由于分支这实在是处理器不喜欢的东西，于是人们便想要尽量减少分支语句的使用。而以下这种情况很常见，在求取最大最小值或者是条件赋值的时候经常被使用（第1，2行）：</p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">cmp a, 7       ; a > 7 ?
mov c, b       ; b = c
cmovle d, b    ; if le, then b = d</pre>



<p>于是人们就设计出了第3行这种指令。这样的指令是在特定条件下将d的值赋给b，而并不引入分支，只需要在条件不满足的时候不进行写回（commit/retire）就可以了。这种指令被称为<strong>条件转移指令</strong>，编译器中经常使用这种trick来避免进行跳转。</p>



<p>我们古老的x86架构一开始并不支持条件转移指令，MIPS、SPARC也不例外，而Alpha架构从设计之初就考虑了这类指令（RISC-V这样的新指令集当然也有）。ARM则是第一个采用全可预测指令的指令集，这一点很有趣，因为早期的ARM处理器通常采用很浅的流水线，分支预测的惩罚很小。</p>



<h2 class="wp-block-heading">指令调度、寄存器重命名和乱序执行</h2>



<p>如果分支和长延迟的指令会带来流水线气泡，那么能不能把这些气泡占据的处理器时间用来干有用的事情呢？为了达到这个目的，就需要引入<strong>乱序执行</strong>。乱序执行允许处理器将部分指令的顺序打乱，在执行长延迟指令的同时执行一些别的指令。</p>



<p>历史上有两种方式来达到乱序执行的目的：软件的和硬件的。</p>



<p>软件的途径很好理解，就是通过编译器与体系结构的强耦合，在编译阶段就生成好无相互依赖，易于处理器调度的指令。在编译阶段进行指令重排又被称为<strong>静态指令调度</strong>，优点是软件实现可以更灵活（众所周知，软件什么都能干），通常软件也可以有足够的存储空间来分析整个程序，因此可以获得更优的指令排布。当然缺点也是显而易见的，由于编译器需要深入地了解体系结构相关的信息，如指令延迟和分支预测惩罚等，对可移植性造成了很大的困难。因此现代处理器更加常用的是硬件方式。</p>



<p>硬件方式主要是通过<strong>寄存器重命名</strong>来消除读—读和写—写假依赖。寄存器重命名就是对不同指令调用的相同寄存器使用不同的物理硬件存储，在写回阶段再对这些指令和寄存器进行排序，这样这些假依赖就不再是产生流水线气泡的原因了。注意，写—读依赖是真正的数据依赖，虽然像前递这样的技术可以降低延迟，但是并没有能够解决这种依赖的办法。现代处理器中也并非仅仅只有如16个通用寄存器和32个浮点寄存器等等，通常都有成百上千的物理寄存器在CPU的片上。寄存器重命名的算法最有名的便是Tomasulo算法，有兴趣的读者可以搜索一下。</p>



<p>硬件方式的优点在于降低了编译器的体系结构耦合度，提高了软件编写的便捷性，通常硬件乱序执行的效果也不必软件的差。而缺点在于依赖分析和寄存器重命名都需要耗费宝贵的片上空间和电力，但对于性能的提升却没有相应的大。因此，在一些更加关注低功耗和成本的CPU中，会采用顺序执行，如ARM的低功耗产品线，Intel Atom等。</p>



<h2 class="wp-block-heading">多核和超线程</h2>



<p>我们之前讨论了各种指令集并行的方法，而很多时候它们的效果并不是很好，因为相当一部分的程序没有提供细粒度的并行。因此，制造更“宽”更“深”的处理器效果相当有限。</p>



<p>但是CPU的设计者又想了，如果本程序中没有足够并行的没有相互依赖的指令，那么不同的程序之间肯定是没有数据依赖的（指令级数据依赖），那么在同一个物理核心上同时运行两个线程，互相填补流水线的空缺，岂不美哉？这就叫做<strong>同步多线程（SMT）</strong>，它提供了线程级的并行化。这种技术对于CPU以外的世界来说是透明的，就仿佛真的CPU数量多了一倍似的，因此现在人们也常说虚拟核心。</p>



<p>从硬件角度来说，同步多线程的实现需要将所有与运行状态有关的结构数量都翻倍，比如寄存器，PC计数器，MMU和TLB等等。幸运的是，这些结构并不是CPU的主要部分，最复杂的译码和分发器，运算器和缓存都是在两个线程之间共享的。</p>



<p>当然，真实的性能不可能翻倍，理论上限还是取决于运算器的数量，同步多线程只是能够将运算器更好地利用而已。因此在例如游戏画面生成这样地并行度本来就很高地任务中，SMT几乎没有任何地效果，反而因为偶尔地线程切换而带来一定地性能损失。</p>



<p>SMT处理器的指令流看起来大概是这样的：</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" src="https://blog.eastonman.com/wp-content/uploads/2021/05/smt2.svg" alt="" class="wp-image-769"/><figcaption>SMT处理器的指令流</figcaption></figure></div>



<p>太好了！现在我们有了填满哪些流水线气泡的方法了，而且绝无任何风险。所以，<strong>30发射的处理器我们来啦</strong>！对吗？不幸的是，不对。</p>



<p>虽然IBM曾在它的产品中使用过8线程的核心，但是很快我们就会看到，现代处理器的瓶颈早已不单单是CPU本身了，访存延迟和带宽都成为了更加迫切需要解决的问题。而同时使用8个MMU，8个PC，8个TLB怎么看也不是一个缓存友好的做法。因此，现在已经很少听到有多于一个核心两个线程的处理器了。</p>



<h2 class="wp-block-heading">数据并行——SIMD指令集</h2>



<p>除了指令级并行和超线程，在现代处理器中还有一种并行化的设计——数据并行化。数据并行化的思想是将同一条指令不同数据进行并行化，而不是对不同的指令进行并行。所以使用数据并行化的指令集通常又称为<strong>SIMD指令集</strong>（单指令多数据），也有称为<strong>向量指令集</strong>的。</p>



<p>在超级计算机和高性能计算领域，SIMD指令集被大量的使用，因为通常科学计算会处理极多的数据而对于每个数据的操作并不复杂，而且基本没有相互依赖性。在现代的个人计算机中，SIMD指令集也大量的存在，哪怕是最为廉价的手机中，SIMD指令集也有它的身影。</p>



<p>SIMD指令集的工作原理就像下图所示的那样：</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://cdn.arstechnica.net/wp-content/uploads/archive/cpu/1q00/simd/figure6.gif" alt=""/><figcaption>SIMD指令原理</figcaption></figure></div>



<p>从硬件的角度来说，实现这样的并行化并不难，这就像每次都执行同一个指令的超标量处理器，CPU设计厂商唯一需要做的就是增大寄存器的容量而已。Intel在过去20年正在不断地增大可以并行的向量长度，从SSE的128bits到AVX512的512bits。而ARM从ARMv8a开始便从NEON的128bits飞跃到了SVE的2048bits长度，甚至还支持可变长度。</p>



<p>现代的x86-64处理器都支持SSE指令集，所以现在的编译器如果编译64位平台的目标文件，会自动的将SSE指令集加入用于优化。由于SIMD指令集发展迅速，不少指令的延迟甚至和传统的标量命令不相上下，而且SSE指令集也拥有操作单个操作数的指令，现代编译器在默认情况下对于单个浮点数的操作也会使用SSE指令集而不是使用传统的x87浮点指令。另外，几乎所有的体系结构都拥有自己的SIMD指令集。</p>



<p>像渲染画面或者科学计算这种简单而重复的任务很适合SIMD指令集，事实上，GPU的工作原理也与SIMD类似。但不幸的是，在大多数普通（没有经过特别的思考而写出的）代码中，SIMD指令集并不能被很好的应用。现代编译器全部都有不同程度的循环自动向量化（使用SIMD指令集），但是当程序的编写者没有很好地考虑数据的依赖性和内存布局（马上就会谈到）时，编译器往往不能对代码进行什么优化。而幸运的是，通常通过简单的改动，就可以编译器明白某些循环是可以被优化的，进而大幅的提升程序的运行速度。</p>



<h2 class="wp-block-heading">内存和内存墙</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>现代处理器实在是太快了，以至于它们大多数时候都在等待内存响应，而不是干正事。</p><cite>——佚名（忘记出处了）</cite></blockquote>



<p>自从计算机发明以来，处理器的发展速度远远超过存储的发展速度，以下是一个对比图：</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="https://blog.royalsloth.eu/posts/2021/compiler-will-optimize-that-away/cpuMemoryPerformance_min.png" alt="Performance of processors and memory through the years 1980-2010"/><figcaption>处理器和内存速度对比</figcaption></figure></div>



<p>对于现代处理器来说，内存访问非常的昂贵。</p>



<ul class="wp-block-list"><li>在1980年，CPU访问一次内存通常只需要一个周期。</li><li>在2021年，CPU访问内存大约需要300-500个周期。</li></ul>



<p>当我们考虑到，我们在CPU上使用了那么多种手段来压榨运算器，使IPC能够突破1，这样来看，内存就更慢了。以下是一个表格，展示了如果处理器周期看作是1秒，访问其它的存储器需要的时间。</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td>事件</td><td>延迟</td><td>等效延迟</td></tr><tr><td>CPU周期</td><td>0.2ns</td><td>1s</td></tr><tr><td>L1缓存访问</td><td>0.9ns</td><td>4s</td></tr><tr><td>L2缓存访问</td><td>3ns</td><td>15s</td></tr><tr><td>L3缓存访问</td><td>10ns</td><td>50s</td></tr><tr><td>内存访问</td><td>100ns</td><td>8分钟</td></tr><tr><td>固态硬盘访问</td><td>10-100us</td><td>15-150小时</td></tr><tr><td>机械硬盘访问</td><td>1-10ms</td><td>2-18月</td></tr></tbody></table><figcaption>等效延迟表</figcaption></figure>



<p>可以看到，现代CPU实在是太快了，程序编写者现在要比过去花费更多的精力来使他们的程序能够充分利用CPU的性能，而不是卡在内存操作上。</p>



<p>为了解决这个严重的问题，处理器设计者们也想出了办法，也就是上面的表格中已经出现了的缓存。80年代的CPU由于没有内存墙的问题，所以基本都没有设计缓存。而现代CPU通常而言都有高达三级的缓存（某些低功耗和移动端的CPU只有两级），理解这些缓存是怎么样工作的有利于程序设计者写出更快的程序。</p>



<h2 class="wp-block-heading">缓存</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Cache这个词读音和Cash（现金）一样，而不是kay-sh、ca-shay或者Cake！</p><cite>我</cite></blockquote>



<p>现代处理器为了解决内存墙，使用多级的缓存来避免内存延迟的影响。一个典型的缓存结构是这样的：</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><th>等级</th><th>大小</th><th>延迟</th><th>物理位置</th></tr><tr><td>L1 cache</td><td>32 KB</td><td>4 cycles</td><td>每个核心内部</td></tr><tr><td>L2 cache</td><td>256 KB</td><td>12 cycles</td><td>每个die或每个核心</td></tr><tr><td>L3 cache</td><td>6 MB</td><td>~21 cycles</td><td>整个处理器共享或每die共享</td></tr><tr><td>RAM</td><td>4+ GB</td><td>~117 cycles</td><td>主板上的内存条上</td></tr></tbody></table><figcaption>缓存等级</figcaption></figure>



<p>令人高兴的是，现代处理器的缓存机制出奇的有效，L1缓存的命中率在大多数时候高达90%，这说明在大多数情况下内存访问的代价仅仅是几个周期而已。</p>



<p>缓存能够取得这么好的效果主要是因为程序具有很好的<strong>局部性</strong>。分为空间局部性和时间局部性。<strong>时间局部性</strong>说的是当程序访问一块内存时，很有可能接下来连续访问这一块内存。<strong>空间局部性</strong>是说程序访问一块内存，那么它很可能也许要访问附近的内存。为了利用好这样的局部性，内存中的数据是一块一块地从内存条上复制到缓存中的，这些快被称为<strong>缓存行</strong>。</p>



<p>从硬件的角度来说，缓存的工作原理和键值对表很类似。Key就是内存的地址，而Value则是对应的数据。事实上Key并不一定是完整的地址，通常是地址的高位一部分，而低位被用来索引缓存本身。用物理地址和虚拟地址来作为Key都是可行的，也各有好坏（就像所有的事情一样）。使用虚拟地址的缺点是进程的上下文切换需要刷新缓存，这非常昂贵。使用物理地址的缺点则是每次查缓存都需要先查页表。因此现代处理器通常采用虚拟地址作为缓存索引，而使用物理地址作为缓存行的标记。这样的方法又被称作“<strong>虚拟索引——物理标记</strong>”缓存。</p>



<h2 class="wp-block-heading">缓存冲突和关联度</h2>



<p>理想状态下，缓存应当保存最近最常使用的数据，但是对于CPU上的硬件缓存来说，有效维护使用状态的算法不能满足严格的延迟要求（例如Linux内核页缓存使用的LRU，有兴趣可以看我前面的文章<a href="https://blog.eastonman.com/blog/2021/04/linux-multi-lru/" title="https://blog.eastonman.com/blog/2021/04/linux-multi-lru/">Linux内核页面置换算法</a>），也难以用硬件实现，所以通常处理器使用简单的方法：<strong>每一个缓存行直接对应内存的几个位置</strong>。由于对应的几个位置不太可能同时访问，因此缓存是有效的。</p>



<p>这样的做法非常快速（本来缓存的设计目的就是这样的），但是当程序的确不断地来回访问同一个缓存行对应的不同位置的时候，缓存控制单元不得不反复从内存中装载数据，非常耗时，这被称为<strong>缓存冲突</strong>。解决办法就是不限制每一个内存区域只对应一个缓存行，而是对应几个，这个几个就被称作是<strong>缓存关联度</strong>。</p>



<p>当然，最快的方法是每个内存区域对应一个缓存行，这被叫做<strong>直接映射缓存</strong>，而使用4个关联度的缓存被称作<strong>4通道关联缓存</strong>。内存可以装载到任意一个缓存行的缓存叫做<strong>全关联缓存</strong>。使用关联的缓存带来的好处是大大减少了缓存冲突而保持查询延迟在一个合理的范围内。这也是现代处理器通常使用的方法。</p>



<h2 class="wp-block-heading">致谢</h2>



<p><a href="http://www.lighterra.com/papers/modernmicroprocessors/">Modern Microprocessors: A 90-Minute Guide!</a> 2016 By <a href="http://www.lighterra.com/jason/">Jason Robert Carey Patterson</a></p>



<p><a href="https://blog.royalsloth.eu/posts/the-compiler-will-optimize-that-away/" target="_blank" rel="noreferrer noopener" title="https://blog.royalsloth.eu/posts/the-compiler-will-optimize-that-away/">The compiler will optimize that away</a> 2021 By <a target="_blank" href="https://www.royalsloth.eu/" rel="noreferrer noopener">RoyalSloth</a></p>



<p><a href="http://www.anandtech.com/show/9582/intel-skylake-mobile-desktop-launch-architecture-analysis">The Intel Skylake Mobile and Desktop Launch, with Architecture Analysis</a> 2015 By&nbsp;<a href="https://www.anandtech.com" target="_blank" rel="noreferrer noopener" title="https://www.anandtech.com">AnandTech</a></p>



<p><a href="https://en.wikichip.org/wiki/intel/microarchitectures/skylake_(client)" target="_blank" rel="noreferrer noopener" title="https://en.wikichip.org/wiki/intel/microarchitectures/skylake_(client)">Skylake(Client) Microarchitecture</a> 2020 By <a href="https://en.wikichip.org" target="_blank" rel="noreferrer noopener" title="https://en.wikichip.org">WikiChip</a></p>



<h2 class="wp-block-heading">深入阅读</h2>



<ul class="wp-block-list"><li>《深入理解计算机系统》（CSAPP)</li><li>《计算机体系结构：一种量化方法》</li><li><a href="https://www.bilibili.com/video/BV1Mo4y1Z7jb" target="_blank" rel="noreferrer noopener">吹牛还是真牛？苹果M1全网最硬核评测（上）</a> By 极客湾@bilibili.com</li></ul>



<p></p>The post <a href="https://blog.eastonman.com/blog/2021/05/modern-processor/">现代处理器结构</a> first appeared on <a href="https://blog.eastonman.com">Easton Man's Blog</a>.]]></content:encoded>
					
					<wfw:commentRss>https://blog.eastonman.com/blog/2021/05/modern-processor/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
	</channel>
</rss>
