- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在汇编和处理器的学习中,有一件事让我很困惑,指令是如何完成的:
add mem, 1
在我看来,处理器无法在同一条指令中和加载内存值并处理算术运算。所以我认为它是这样发生的:
mov reg, mem
add reg, 1
mov mem, reg
如果我考虑带有 RISC Pipeline 的处理器,我们可以观察到一些摊位。对于像 i++
这样简单的指令来说,这是令人惊讶的:
| Fetch | Decode | Exec | Memory | WriteB |
| Fetch | | | Decode | Exec | Memory | WriteB |
| Fetch | | | | Decode | Exec | Memory | WriteB |
(正如我在 Patterson 的书Computer Architecture: A Quantative Approach 中读到的那样,寄存器在解码 uOp 中读取,在内存 uOp 中存储/加载,我们允许自己在 Memory uOp 处取一个寄存器的值。)
我说的对吗?或者现代处理器有特定的方法来更有效地做到这一点?
最佳答案
你是对的,现代 x86 将解码 add dword [mem], 1
到 3 uops:加载、ALU 添加和存储。 (这实际上是对各种事物的简化,包括英特尔的微融合以及 AMD 如何始终在管道的某些部分将负载 + ALU 保持在一起......)
这 3 个相关操作不能同时发生,因为后面的必须等待前面的结果。
但是独立指令的执行可能会重叠,现代 CPU 非常积极地寻找并利用“指令级并行性”来以每时钟 1 uop 的速度运行您的代码。参见 this answer for an intro to what a single CPU core can do in parallel ,带有指向更多内容的链接,例如 Agner Fog's x86 microarch guide ,以及 David Kanter 的文章 Sandybridge和 Bulldozer .
但如果您查看 Intel 的 P6 和 Sandybridge 微体系结构系列,就会发现存储实际上是独立的存储地址和存储数据微指令。 store-address 微指令不依赖于负载或 ALU 微指令,可以将存储地址写入 store buffer。随时。 (Intel 的优化手册将其称为 Memory Order Buffer)。
为了提高前端吞吐量,存储地址和存储数据微指令可以解码为微融合对。对于 add
,load+alu 操作也可以,因此 Intel CPU 可以将 add dword [rdi], 1
解码为 2 个融合域微指令。 (相同的 load+add 微融合适用于解码 add eax, [rdi]
到单个 uop,因此任何“简单”解码器都可以解码它,而不仅仅是可以解码的“复杂”解码器处理多 uop 指令。这减少了前端瓶颈)。
这就是为什么 add [mem], 1
在 Intel CPU 上比 inc [mem]
更有效,即使 inc reg
是与 add reg,1
一样高效(但更小)。 (inc
不能对其 load+inc 进行微融合,它设置标志的方式与 add
不同)。 INC instruction vs ADD 1: Does it matter?
但这只是帮助前端更快地将uops送入调度器;负载仍然必须与添加分开运行。
但是微熔断负载不必等待整个指令输入的其余部分就绪。考虑像 add [rdi], eax
这样的指令,其中 RDI 和 EAX 都是指令的输入,但在 ALU add uop 之前不需要 EAX。一旦加载地址准备好并且有一个空闲的加载执行单元(AGU + 缓存访问),加载就可以执行。另见 How are x86 uops scheduled, exactly? .
registers are read in Decode uOp, Store/Load in Memory uOp and we allow ourselves to take the value of a register at the Memory uOp
所有当前的 x86 微架构都使用寄存器重命名(Tomasulo 算法)的乱序执行。指令被重命名并发布到核心的乱序部分(ROB 和调度程序)。
只有一条指令从调度程序“分派(dispatch)”到执行单元时,才会读取物理寄存器文件。 (或者对于最近生成的输入,从其他 uops 转发。)
独立指令可以与它们的执行重叠。例如,Skylake CPU 可以维持每个时钟 4 个融合域/7 个非融合域微指令的吞吐量,包括 2 个加载 + 1 个存储,in a carefully crafted loop :
.loop: ; HSW: 1.12c / iter. SKL: 1.0001c
add edx, [rsp] ; 1 fused-domain uop: micro-fused load+add
mov [rax], edi : 1 fused-domain uop: micro-fused store-address+store-data
blsi ebx, [rdi] : 1 fused-domain uop: micro-fused load+bit-manip
dec ecx
jnz .loop ; 1 fused-domain uop: macro-fused dec+branch runs on port 6
Sandybridge 系列 CPU 有一个 L1d 缓存,每个时钟能够进行 2 次读取 + 1 次写入。 (不过,在 Haswell 之前,只有 256 位向量可以解决 AGU 吞吐量限制。参见 How can cache be that fast?。)
Sandybridge 系列的前端吞吐量是每个时钟 4 个融合域微指令,它们在后端有很多执行单元来处理各种指令混合。 (Haswell 和后来的有 4 个整数 ALU、2 个加载端口、一个存储数据端口和一个用于简单存储寻址模式的专用存储 AGU。因此它们通常可以在缓存未命中停止执行后快速“ catch ”,快速在乱序窗口中找到更多工作要做。)
关于assembly - 处理器是否可以同时进行存储和算术运算?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51102525/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!