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

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

先读后写(Write After Read)

这次我们先计算 a = b + a,然后再计算 b = a + b。

int main() {
  int a = 1;
  int b = 2;
  a = b + a;
  b = a + b;
}
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 = b + a;
  12:   8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
  15:   01 45 fc                add    DWORD PTR [rbp-0x4],eax
   b = a + b;
  18:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  1b:   01 45 f8                add    DWORD PTR [rbp-0x8],eax
}
  1e:   5d                      pop    rbp
  1f:   c3                      ret       

内存地址为15的汇编指令里,要把 eax 寄存器值读出,加到 rbp-0x4 的内存地址里。

在内存地址为18的汇编指令里,再写入更新 eax 寄存器里面。


如果在内存地址18的eax的写入先完成了,在内存地址为15的代码里面取出 eax 才发生,程序计算就错。这里,我们同样要保障对于eax的先读后写的操作顺序。


这个先读后写的依赖,一般被叫作反依赖,Anti-Dependency。

写后再写(Write After Write)

先设置变量 a = 1,再设置变量 a = 2。

int main() {
  int a = 1;
  a = 2;
}
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
  a = 2;
   b:   c7 45 fc 02 00 00 00    mov    DWORD PTR [rbp-0x4],0x2
}

内存地址4所在的指令和内存地址b所在的指令,都是将对应的数据写入到 rbp-0x4 的内存地址里面。

如果内存地址b的指令在内存地址4的指令之后写入。那么这些指令完成之后,rbp-0x4 里的数据就是错误的。这就会导致后续需要使用这个内存地址里的数据指令,没有办法拿到正确的值。

所以,也需要保障内存地址4的指令的写入,在内存地址b的指令的写入之前完成。


这个写后再写的依赖,叫输出依赖,Output Dependency。

流水线停顿

除了读之后再进行读,对同一寄存器或内存地址的操作,都有明确强制顺序。而这个顺序操作的要求,也为使用流水线带来挑战。

因为流水线架构的核心,就是在前一个指令还没有结束时,后面的指令就要开始执行。


所以,需要有解决这些数据冒险的办法。

最简单也是最笨的就是流水线停顿(Pipeline Stall),或流水线冒泡(Pipeline Bubbling)。


若发现后面执行的指令,会对前面执行的指令有数据层面的依赖关系,就“再等等”。

进行指令译码时,会拿到对应指令所需访问的寄存器和内存地址,这时就能判断这个指令是否会触发数据冒险。

会触发,就能决定让整个流水线停顿一或者多周期。

image.png

时钟信号会不停地在0、1之间自动切换。所以,其实没法真停顿,流水线的每个操作步骤必须要干点事。

所以,实际上并非让流水线真停下来,而是在执行后续操作步骤前,插入一个NOP操作,即执行一个只负责摸鱼的操作。

image.png

这插入的指令,就好像一个水管(Pipeline)里进了个空气泡。在水流经过时,并没有真的传送水到下一个步骤,而是给了个啥都没有的空气泡,因此得名流水线冒泡(Pipeline Bubble)。

总结

  • 可通过增加资源解决结构冒险问题。
    现代CPU体系结构,也是在冯·诺依曼体系结构下,借鉴哈佛结构的一个混合结构解决方案。内存虽然没有按功能拆分,但在高速缓存层面拆分成指令缓存和数据缓存,从硬件层面,使得同一个时钟下对于相同资源的竞争不再发生。
  • 也可通过“等待”,即插入NOP操作解决冒险问题,即流水线停顿。
    不过,流水线停顿这样的解决方案要牺牲CPU性能。因为,实际上在最差的情况下,我们的流水线架构的CPU,又会退化成单指令周期的CPU。



参考

  • 《计算机组成与设计:硬件/软件接口》的第4.5~4.7章
目录
相关文章
|
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相关技术、使用技巧和最佳实践的资源。
|
缓存 C语言
CPU是如何解决冒险问题的?(上)
CPU是如何解决冒险问题的?
406 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:

热门文章

最新文章