1. Outoforder Execution

2023年 8月 13日 42.2k 0

引言

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

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

乱序的原因

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

编译器

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

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 生成汇编如下

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 则会生成如下汇编

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

这个代码等价于

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

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

#include 

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

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论