1. Out-of-order Execution

引言

在同一个线程中,如果指令之间没有前后的依赖关系,那么这些指令则在可能会会乱序执行。当时指令之间存在依赖关系,比如读写时,我们需要重点关注,因为这些指令如果乱序就会对代码逻辑产生可见的影响。

  • 比如下的代码可能以任何数据执行,他们的执行顺序无法预测,因为他们之间没有依赖
ini 复制代码
int x = 1;
int y = 2;
int z = 3;
  • 但是下面的代码将被串行执行,因为他们y的赋值依赖x的值,z 的值依赖 x 和 y
ini 复制代码
int x = fun1();
int y = fun2(x);
int z = x + y;

乱序的原因

产生指令执行顺序被打乱的原因有很多,比如编译器的优化、cpu 指令流水线的优化、cpu 数据核内写缓存cache 一致性限制、多个cpu间网络数据传递等。总之乱序的原因是为了提高程序执行的效率。这了我们要有一个概念 假设cpu 执行一条指令需要一个时钟周期,那么cpu 写L1存储也是一个周期,写L2大概10个周期,写内存大概需要100个周期,因此为了解决各个系统层级之间的读写速度差,cpu会出现大量的优化措施,这些就会导致指令的乱序执行。

编译器

编译优化手段有很多。比如常量折叠,无用代码消除等。 比如下面的例子

ini 复制代码
int main() {
  int x = 1;
  int y = 2;
  int z = 3;
  int a = y + z;
  int b = a + 1;
  return b;
}

如果采用 -O0 编译 gcc -g -c -O0 ./a.c && objdump -d -M intel -S ./a.o 生成汇编如下

ini 复制代码
int main() {
   0:	f3 0f 1e fa          	endbr64
   4:	55                   	push   rbp
   5:	48 89 e5             	mov    rbp,rsp
  int x = 1;
   8:	c7 45 ec 01 00 00 00 	mov    DWORD PTR [rbp-0x14],0x1
  int y = 2;
   f:	c7 45 f0 02 00 00 00 	mov    DWORD PTR [rbp-0x10],0x2
  int z = 3;
  16:	c7 45 f4 03 00 00 00 	mov    DWORD PTR [rbp-0xc],0x3
  int a = y + z;
  1d:	8b 55 f0             	mov    edx,DWORD PTR [rbp-0x10]
  20:	8b 45 f4             	mov    eax,DWORD PTR [rbp-0xc]
  23:	01 d0                	add    eax,edx
  25:	89 45 f8             	mov    DWORD PTR [rbp-0x8],eax
  int b = a + 1;
  28:	8b 45 f8             	mov    eax,DWORD PTR [rbp-0x8]
  2b:	83 c0 01             	add    eax,0x1
  2e:	89 45 fc             	mov    DWORD PTR [rbp-0x4],eax
  return b;
  31:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]
}
  34:	5d                   	pop    rbp
  35:	c3                   	ret

但是如果采用-O2编译 gcc -g -c -O2 ./a.c && objdump -d -M intel -S ./a.o 则会生成如下汇编

ini 复制代码
int main() {
   0:	f3 0f 1e fa          	endbr64
  int y = 2;
  int z = 3;
  int a = y + z;
  int b = a + 1;
  return b;
}
   4:	b8 06 00 00 00       	mov    eax,0x6
   9:	c3                   	ret

这个代码等价于

csharp 复制代码
int main() {
  return 6;
}
csharp 复制代码
int main() {
   0:	f3 0f 1e fa          	endbr64
  return 6;
}
   4:	b8 06 00 00 00       	mov    eax,0x6
   9:	c3                   	ret

可以看到不但代码顺序是可以打乱的,甚至连无用代码都可删除,常量可以在编译期计算。 编译优化有个前提,就是假设程序是单线程执行的,果编译器希望对程序指令的执行顺序做出改变,只要这些改变不影响该程序在单线程情况下的运行结果,那么这些改变就是允许的。 比如下面的代码 第6行和第7行的执行顺序就是无法保证的

c 复制代码
#include <iostream>

int main() {
    int a = 10, b = 20, c = 30, d = 40, e = 50, f = 60, g = 70, h = 80;

    int x = a + b + c + d;
    int y = e + f + g + h;
    
    std::cout << "x = " << x << ", y = " << y << std::endl;
    return 0;
}

再比如

ini 复制代码
for (i = 0; i < cols; ++i) {
    for (j = 0; j < rows; ++j) {
        a[j * cols + i] += 2;
    }
}

这个代码可能会被改写为

ini 复制代码
for (j = 0; j < rows; ++j) {
    for (i = 0; i < cols; ++i) {
        a[j * cols + i] += 2;
    }
}

处理器

今天的处理器为了提高性能,都允许乱序执行

to be continue...

处理器写缓存

to be continue...

On-Chip-Network

to be continue...

相关推荐
yodala32 分钟前
C++中的内存管理(二)
开发语言·c++
艾莉丝努力练剑2 小时前
【C语言16天强化训练】从基础入门到进阶:Day 5
c语言·c++·学习·算法
重启的码农4 小时前
llama.cpp 分布式推理介绍(5) RPC 通信协议
c++·人工智能·神经网络
liulilittle4 小时前
UTF-8 编解码可视化分析
c++·字符串·unicode·string·字符·char·utf8
重启的码农4 小时前
llama.cpp 分布式推理介绍(6) 张量序列化 (rpc_tensor)
c++·人工智能·神经网络
让我们一起加油好吗5 小时前
【C++】模板(进阶)
c++·visualstudio·模板·泛型编程
雨落倾城夏未凉5 小时前
2.如何创建自己的类
c++·后端
Ustinian_3106 小时前
【C/C++】For 循环展开与性能优化【附代码讲解】
c语言·开发语言·c++
MZ_ZXD00112 小时前
springboot汽车租赁服务管理系统-计算机毕业设计源码58196
java·c++·spring boot·python·django·flask·php
岁忧14 小时前
(nice!!!)(LeetCode 每日一题) 679. 24 点游戏 (深度优先搜索)
java·c++·leetcode·游戏·go·深度优先