计算机体系结构-CPU性能提升简述

前言 随着半导体工艺的升级和芯片技术的发展,为了充分提升CPU的性能出现了各种各样的方式,提升工艺能够极大提升CPU性能,但随着摩尔定律的逐渐失效,优化CPU结构成为了提升CPU性能中越来越重要的一部分。 局部性原理 在CPU频率不断提升的情况下,和CPU进行频繁数据交换的内存速度却提升不够。在这种情况下,CPU直接通过内存进行数据交换时,由于需要等待内存的回应,往往会降低CPU的性能。通过在CPU和内存中间插入Cache作为缓存,能够一定程度上弥补这个问题。 Cache的实现原理 Cache利用空间局部性和时间局部性原理,通过在内存和CPU中间插入一块速度更快的存储器,进而CPU可以先尝试和Cache进行交互,减少CPU访问内存的次数,进而提高系统的整体性能。当Cache中不存在相应数据时,我们会将存储器中的数据和相邻的数据“搬运”到Cache中,这样只需一次访问内存我们可以减少n次内存的访问。 多级Cache 如果都用最先进的工艺实现一个Cache,价格和功耗都是我们无法接受的(当Cache过大时,会由于复杂逻辑产生大的功耗)。因此我们采用多级Cache的方案,不同Cache满足不同需求,如Icache和Dcache代表的L1cache独立于每个Core,L2cache可能在由几个Core进行共用,L3cache可能是整个CPU共用一份。 L1cache分解成Icache和Dcache由于流水线的存在。 无Cache Cache虽好,但并不是所有的处理器都提供了Cache,原因主要有三点: Cache会增加功耗和成本,占用过大的芯片面积和发热了。 一些处理器本身工作频率不高,没有需要Cache解决的带宽问题。 Cache无法保证实时性,缓存命中和未命中需要不同的CPU处理时间,无法满足实时控制场景的需要。 流水线 CPU工作的执行单调而乏味,正如流水线上的工人一样,因此我们也可以通过流水线的方式提升CPU的处理速度。 流水线的原理 流水线通过将一个任务分成多个部分,利用类似并行的处理方式提高了模块的利用率,提升了吞吐量。 想象一个拧螺丝的工作需要4步,每一步需要2s中,那我们从开始到最后完成任务需要8s,也就是说这个时候我们8s可以生产一个产品,但无论我们执行那一步,其他的三步(材料+设备)都是空闲的,因此我们可以通过流水线的方式进行全速前进,这样在全速前进下我们2s就可以生产一个产品(虽然第一个产品生产完成其实也需要8s等待)。 流水线并不是越深越好 首先流水线的分级需要插入寄存器;流水线间如果出现依赖可能还需要增加逻辑进行判断;同时一旦发生了跳转,我们需要丢弃掉前面所有的预取指令。 更多情况下,我们的流水线需要在性能和功耗间做一个平衡。 乱序执行 不同指令间的执行在流水线中可能存在冒险,这种情况下我们可能需要停顿流水线或添加更多逻辑,而还有一种方式就是通过乱序执行,让数据相关的指令不在一起执行,从而根本上避免了冒险的产生。 SISD和SIMD 我们学习过的大多数指令都是SISD(Single Instruction Single Data),这种数据操作会通过一个指令完成一次数据的运算。而SIMD(Single Instruction Multiple Data)可以完成单指令多数据的操作,一次性读取所有的操作数并进行运算。 为什么要有SIMD 数据密集型计算的大规模出现:图像、视频、音频等数据的处理需求催生了SIMD类型指令的出现。如果说之前处理一帧数据需要无数条SISD的指令,那么可能只需要一条SIMD指令,极大提升了这类处理需求的指令效率。 单发射与多发射 多发射可以在一个时钟周期执行多条指令,这是由于处理器内部多个执行单元的存在(又增加了处理器核的面积和逻辑复杂性)。让我们的处理器可以达到指令级的并行。 静态多发射 静态多发射依赖于编译阶段对可以并行的指令打包,合并成一个长指令,这种实现实现又叫做VLIW(Very long Instruction Wrod),这种实现简单,所需硬件也简单,但由于指令集的兼容问题,这种实现并不能完全被主流处理器支持。 动态多发射 动态多发射在硬件上完成了指令的并行话操作,实现这种方式的处理器又被叫做超标量处理器(SuperScalar)。需要在处理器硬件上实现增加很多逻辑。 EPIC EPIC结合了VILW和SuperScaler的优点,通过在指令中显式提供bit位表达两条指令间有没有相关性,简化了硬件处理两条指令相关的逻辑。 多核 提升芯片面积(如多发射或其他类似的设计增加)在提升CPU性能的同时也带来了更高的功耗,更昂贵的成本。因此对单核的提升出现了一个瓶颈:无法在提升性能的同时降低功耗(相同工艺)。针对于此,多核的时代来临了。 多核处理器能够让多个任务级做到真正并行,而单核处理器只能称之为并发,现代计算机一般都是多个任务同时运行,在这种情况下多核的优势开始显现出来。 片上多核互联 正如在单核流水线和多发射中存在的冲突问题,多核处理器也不能每个核心单独存在,需要进行互联通信。CPU的通信经历了星型连接,总线连接,交叉开关(Crossbar)连接,到达RingBus阶段。RingBus结合总线型和开关型的优点,在成本功耗与通信效率间达到了平衡。但当处理器核心进一步增多时,RingBus的延迟又成为了新的瓶颈。因此面向众核处理器领域又出现了新的片上互联技术:片上网络(Net On Chip, Noc)。 大小核 当工作任务均匀分配到每个核心,并且实现大的负载时,多核处理器的性能得到了充分的发挥。但当工作任务不是很多时,往往会造成处理器中某些核心的空转,白白浪费了电费。为了解决这个问题,不同的厂商都提出了他们的大小核架构方案。可以通过操作系统的调度实现处理器的均匀负载,针对不同的人物,可以使用不同的核心处理问题。 不同厂商的实现不同,比如ARM的big.LITTLE架构中小核心的存在就是为了低功耗,而Intel的E-Core则提供了多线程处理能力,提供的更多是“能效”方面的提升。 超线程(Hyper-Threading) 在主流的X86处理器中,主流CPU都提供了超线程技术,通过增加一定的控制逻辑电路将一个物理处理器当作两个逻辑处理器使用,更大限度提升CPU的资源利用率。 实现原理 通过增加控制逻辑电路,保存各个线程的状态,使不同线程共享一个核心的资源,通过进行上下文切换进行线程转移。 在超线程处理器上这种线程切换可能只需要一个时钟周期,而对于一般处理器的线程切换可能需要上万个时钟周期。...

December 19, 2022 · yunlang

新硬盘挂载后发现 df 查询大小和实际大小不一致--Linux的保留空间

前言 在 Linux 上安装一块新硬盘后,发现硬盘大小和挂载后查询到的大小不一致,大概差了 5% 左右,一种可能就是硬盘分区是留下的保留空间造成的。 细节 mkfs.ext4 的man page提供了一部分的解释。 Specify the percentage of the filesystem blocks reserved for the super-user. This avoids fragmentation, and allows root-owned daemons, such as syslogd(8), to continue to function correctly after non-privileged processes are prevented from writing to the filesystem. The default percentage is 5%. 简单来说,ext文件系统为了保证在硬盘百分百利用下还能够写入 root 用户的关键日志等信息,默认预留了5%的磁盘空间。 但我们可以发现,这部分空间很多时候是不需要预留的,尤其是在大的磁盘分区下(例如NAS),可能会导致大量空间的浪费。 解决 针对这个问题,也已经有了解决方案。可以通过tune2fs命令查看保留空间大小和设置保留空间。 tune2fs -l /dev/sde1 | egrep "Block size:|Reserved block count" # Reserved block count: 36628312 # Block size: 4096 # set the reserved space 1% tune2fs -m 1 /dev/sde1 Reference Decrease Reserve Space...

December 8, 2022 · yunlang

路径规划算法:DFS,BFS,Dijkstra, GBFS 和 A*

介绍 图搜索算法中最常见的一个应用就是路径规划,常见的针对无权图的搜索有DFS和BFS,引入权重后,Dijkstra算法解决了单源最短路径问题,而启发式搜索的存在(GBFS,A*)则能够通过启发函数来提高搜索效率。 DFS 深度优先搜索(Depth First Search)通过维护栈这一数据结构,能够实现对全部路径的搜索,但他会沿着一条路走到最后,在没有结果时才会回头选择另一条路,因此无法保证找到的路径是最优路径。 def dfs(start_point, goal_point): path = [] seen = set() stack = [] seen.add(start_point) stack.append(goal_point) while len(stack) > 0: current = stack.pop() path.append(current) if current == goal_point: break if not graph.neighbors(current): path.pop() continue for next in graph.neighbors(current): if next not in seen: stack.append(next) seen.add(next) return path BFS 广度优先搜索(Breadth First Search)在搜索无权图最短路径时很有用,他会优先探索当前位置的所有方向而不是向着一个方向探索到最后,这也是广度的由来。实现BFS通常依靠队列。同时,我们通过字典来保存我们走过的路径,并通过从终点开始的反向遍历得到路径。 def bfs(start_point, goal_point): frontier = Queue() frontier.put(start_point) came_from = dict() came_from[start_point] = None seen = set() seen....

November 25, 2022 · yunlang

Rust: 泛型,特征与特征对象

Rust: 泛型,特征与特征对象 最近在学习 Rust 的一些概念思想,记录一下自己对 Rust 中泛型,特征与特征对象的理解。 泛型 泛型与 CPP 中的模版类似,可以减少代码的重复。泛型会在编译时实现单态化(monomorphization),会将通用代码转换为特定代码,因此不会出现运行时开销。 可以理解为编译器帮你把写的泛型代码重新转换为写了具体类型的代码。 泛型可以用在结构体,枚举,函数乃至方法中,其中枚举和方法可以多讲一下。 泛型在枚举中的实现 泛型在枚举中的实现本身没有要讲的,不过标准库实现的Option<T>和Result<T, E>很想讲一下。 Option 标准库中的泛型定义 pub enum Option<T> { None, Some(T), } 简约而又简单,rust 中并不存在空指针,通过 None 进行替代,Option常使用在返回值中。当返回值可能为一个结果,也有可能失败或缺值时,可以通过模式匹配进行处理。这里的 T 就是泛型说明 Result<T, E> 标准库中的泛型定义 pub enum Result<T, E> { Ok(T), Err(E), } 除了Option可以在结果失败时传递 None,但有时我们想要知道具体的失败信息,Result 实现了这一点。Result<T, E> 拥有两个泛型 T 和 E,在不同的场景下你可以将他们作为不同的类型。 泛型在方法中 泛型在方法中需要在impl后面声明<T>,这里是为了告诉 Rust 类型后面的 T 是一个泛型而不是具体类型,注意这里impl后面提供的泛型声明只与后面具体类型要实现的泛型有关。 与之相应的,你也可以为一个泛型实现他具体类型的方法。 // 对一个泛型实现具体方法,其中方法中又提供了更多的泛型声明 struct Mix<T, U> { x: T, y: U, } impl<T, U> Mix<T, U> { fn mixup<V, W>(self, other: Mix<V, W>) -> Mix<T, W> { //这里提供了另外两个泛型: V和W, 代表other的类型参数 Mix { x: self....

November 14, 2022 · yunlang

Verilog 实现双边沿触发器Dual Edge_triggered_flip Flop

Verilog 实现双边沿触发器Dual Edge_triggered_flip Flop 在做HDLbits时,有一道很有趣的双边沿触发器问题 ,这里记录一下相关内容和解答方式。 问题描述 实现一个双边沿触发器,即在时钟的上升沿和下降沿都被触发。 module top_module ( input clk, input d, output q ); 问题 无法直接通过always @(posedge clk or negedge clk)直接创建双边沿触发器,FPGA 中只能存在单边沿触发器。 但是你可以创建两个触发器,分别是上升沿和下降沿。 解决方案(1) 虽然我们无法直接创建双边沿触发器,但是可以通过使用两个触发器和一个多路选择器实现相同的功能。 module top_module ( input clk, input d, output q ); reg q1; reg q2; always @(posedge clk) begin q1 <= d; end always @(negedge clk) begin q2 <= d; end assign q = clk ? q1 : q2; endmodule 注意,你可能想要两个触发器内都填写q <= d,这在思维上是合理的,但是在实现中会引入多驱问题。...

November 11, 2022 · yunlang