CPU是如何解决冒险问题的?(上)

简介: CPU是如何解决冒险问题的?

想通过流水线设计来提升CPU的吞吐率,我们需要冒哪些风险。

流水线设计需解决的三大冒险:

  • 结构冒险(Structural Hazard)
  • 数据冒险(Data Hazard)
  • 控制冒险(Control Hazard)


CPU流水线设计里,会遇到各种“危险”,使得流水线的下一条指令不能正常运行。但还是通过“抢跑”,“冒险”拿到一个提升指令吞吐率的机会。

流水线架构的CPU,是主动进行的冒险选择。期望能够通过冒险带来更高回报,所以,这不是无奈之下的应对之举,自然也算不上什么危机。


对于各种冒险可能造成的问题,其实都准备好了应对方案。

结构冒险

本质上是一个硬件层面的资源竞争问题,即一个硬件电路层面的问题。

CPU在同一个时钟周期,同时在运行两条计算机指令的不同阶段。但是这两个不同的阶段,可能会用到同样的硬件电路。

最典型的例子就是内存数据访问。

  • 同一个时钟周期,两个不同指令访问同一个资源(5级流水线的示意图)

image.png

第1条指令执行到访存(MEM)时,流水线第4条指令,在执行取指令(Fetch)的操作。访存和取指令,都要进行内存数据的读取。内存只有一个地址译码器的作为地址输入,那就只能在一个时钟周期里读取一条数据,没法同时执行第1条指令的读取内存数据和第4条指令的读取指令代码。


类似的资源冲突最常见的就是薄膜键盘“锁键”。

薄膜键盘不是每一个按键背后都有独立线路,而是多个键共用一个线路。如果在同一时间,按下两个共用一个线路的按键,这两个按键信号就没法都传输出去。

重度键盘用户,都要买机械键盘或电容键盘。因为按键都有独立传输线路,“全键无冲”,大量写文章、写程序,还是打游戏,都不会按下键却没生效。


“全键无冲”本质就是增加资源。同样可用在CPU结构冒险。

对访问内存数据和取指令的冲突,把我们的内存分成两部分,各有各的地址译码器。这两部分分别是存放指令的程序内存和存放数据的数据内存。


这样把内存拆成两部分的解决方案,在计算机体系结构里叫作哈佛架构(Harvard Architecture)。

冯·诺依曼体系结构,又叫作普林斯顿架构(Princeton Architecture)。


如今的CPU仍是冯·诺依曼体系结构,并未将内存拆成程序内存、数据内存。

因为那样拆分,对程序指令和数据需要的内存空间,就无法根据实际应用去动态分配。虽然解决了资源冲突,但也失去灵活性。

1.png

现代CPU架构,借鉴了哈佛架构,在高速缓存层面拆分成指令缓存和数据缓存

不过,借鉴了哈佛结构的思路,现代的CPU虽然没有在内存层面进行对应的拆分,却在CPU内部的高速缓存部分进行了区分,把高速缓存分成了指令缓存(Instruction Cache)和数据缓存(Data Cache)两部分。


内存的访问速度远比CPU的速度要慢,所以现代的CPU并不会直接读取主内存。它会从主内存把指令和数据加载到高速缓存中,这样后续的访问都是访问高速缓存。而指令缓存和数据缓存的拆分,使得我们的CPU在进行数据访问和取指令的时候,不会再发生资源冲突的问题了。


结构冒险是一个硬件层面的问题,我们可以靠增加硬件资源的方式来解决。然而还有很多冒险问题,是程序逻辑层面的事儿。其中,最常见的就是数据冒险。


数据冒险:三种不同的依赖关系

同时在执行的多个指令之间,有数据依赖。

这些数据依赖,可分成三类:

  • 先写后读(Read After Write,RAW)
  • 先读后写(Write After Read,WAR)
  • 写后再写(Write After Write,WAW)

先写后读(Read After Write)

C语言代码编译出来的汇编指令。

int main() {
  int a = 1;
  int b = 2;
  a = a + 2;
  b = a + 3;
}
int main() {
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
  int a = 1;
   4:   c7 45 fc 01 00 00 00    mov    DWORD PTR [rbp-0x4],0x1
  int b = 2;
   b:   c7 45 f8 02 00 00 00    mov    DWORD PTR [rbp-0x8],0x2
  a = a + 2;
  12:   83 45 fc 02             add    DWORD PTR [rbp-0x4],0x2
  b = a + 3;
  16:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  19:   83 c0 03                add    eax,0x3
  1c:   89 45 f8                mov    DWORD PTR [rbp-0x8],eax
}
  1f:   5d                      pop    rbp
  20:   c3                      ret  
  • 内存地址为12的机器码,把0x2添加到 rbp-0x4 对应内存地址
  • 内存地址为16的机器码,又要从rbp-0x4内存地址,把数据写入eax寄存器。


所以,需要保证内存地址为16的指令读取rbp-0x4的值前,内存地址12的指令写入到rbp-0x4的操作必须完成。

这就是先写后读所面临的数据依赖。这顺序保证不了,程序就是错的!


这种先写后读的依赖关系称为数据依赖,Data Dependency。

目录
相关文章
|
6月前
|
SQL 数据管理 网络安全
数据管理DMS操作报错合集之DMS的CPU使用率达到100%,如何解决
数据管理DMS(Data Management Service)是阿里云提供的数据库管理和运维服务,它支持多种数据库类型,包括RDS、PolarDB、MongoDB等。在使用DMS进行数据库操作时,可能会遇到各种报错情况。以下是一些常见的DMS操作报错及其可能的原因与解决措施的合集。
|
6月前
|
Kubernetes Java 测试技术
ChaosBlade常见问题之在K8s环境下演练cpu满载报错如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
303 0
|
6月前
|
JSON Java Serverless
nacos常见问题之cpu和内存占用高如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
1766 0
|
6月前
|
关系型数据库 MySQL Java
实时计算 Flink版操作报错之整内存和cpu分配之后启动报错如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
6月前
|
SQL 关系型数据库 分布式数据库
PolarDB常见问题之PolarDB的CPU跑高如何解决
PolarDB是阿里云推出的下一代关系型数据库,具有高性能、高可用性和弹性伸缩能力,适用于大规模数据处理场景。本汇总囊括了PolarDB使用中用户可能遭遇的一系列常见问题及解答,旨在为数据库管理员和开发者提供全面的问题指导,确保数据库平稳运行和优化使用体验。
|
6月前
|
缓存 网络协议 Java
PTS cpu问题之cpu过高如何解决
PTS(Performance Testing Service)是一项面向网站、应用等提供的压力测试服务,用于模拟不同场景下的用户访问,评估系统的性能表现;在进行PTS压测时,可能会出现一些异常或报错,本合集将PTS压测中频繁出现的问题及其解决办法进行汇编,旨在帮助用户更有效地进行性能测试和问题定位。
|
6月前
|
PyTorch 算法框架/工具 Docker
ModelScope问题之odelScope使用了官方的docker的cpu镜像如何解决
ModelScope镜像是指用于在ModelScope平台上创建和管理的容器镜像,这些镜像包含用于模型训练和推理的环境和依赖;本合集将说明如何使用ModelScope镜像以及管理镜像的技巧和注意事项。
244 0
|
6月前
|
SQL Java Apache
Flink CPU问题之CPU较低如何解决
Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎。本合集提供有关Apache Flink相关技术、使用技巧和最佳实践的资源。
|
缓存
CPU是如何解决冒险问题的?(下)
CPU是如何解决冒险问题的?
295 0
CPU是如何解决冒险问题的?(下)
|
9天前
|
弹性计算 Kubernetes Perl
k8s 设置pod 的cpu 和内存
在 Kubernetes (k8s) 中,设置 Pod 的 CPU 和内存资源限制和请求是非常重要的,因为这有助于确保集群资源的合理分配和有效利用。你可以通过定义 Pod 的 `resources` 字段来设置这些限制。 以下是一个示例 YAML 文件,展示了如何为一个 Pod 设置 CPU 和内存资源请求(requests)和限制(limits): ```yaml apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image:

热门文章

最新文章