假设有两个顺序指令,如下所示:
instruction A
instruction B
由于CPU流水线操作,B将在A完成之前启动。
A完成后是否存在确保B启动的机制?
更新
对不起,我没有准确地描述这个问题。我的意思是,这两个指令具有应用程序级别的顺序依赖,但没有危险。例如,在事务系统中,第一条指令将日志刷新到持久存储,第二条指令通知客户端有关事务提交。所以我们不能执行第二条指令直到第一次结束。如何提供此执行顺序?
答案 0 :(得分:4)
由于CPU流水线操作,B将在A完成之前启动。
所以?为什么这是一个问题?
在基本pipelined architecture中,指令A将在第一个周期开始执行,然后指令B将在下一个周期开始执行。
以basic 5-stage RISC pipeline为例,它看起来像这样:
Clock Cycle | 1 | 2 | 3 | 4 | 5 | 6 |
--------------|---------------------------------------------------------------------------
Instruction A | Fetch | Decode | Execute | Mem. Access | Writeback |
Instruction B | | Fetch | Decode | Execute | Mem. Access | Writeback |
处理器将在第一个时钟周期开始获取指令A.在第二个时钟周期,它将开始解码指令A,同时它同时获取指令B.依此类推,在管道上。
这种方法运行良好的原因是指令获取单元是与指令解码单元完全独立的硬件(尽管两者都可以在同一块硅片,所以保持每个单元同时占用是有意义的。这是实现instruction-level parallelism (ILP)的一种机制。
最终,您可以看到指令A将在第5周期完成,而指令B将在第6周期完成。但是,这比第5周期完成的指令A和指令B不能更好开始直到第6周期,将其完成推迟到第11周。
处理器内部的逻辑处理指令依赖性,因此如果指令B以某种方式依赖于指令A的结果,则处理器的解码器将能够检测到并且将停止指令B的执行,直到它的数据是可用的(即,直到指令A在管道中足够远,其结果准备就绪)。这一切都是为您无缝处理的,但确实会引入性能成本(pipeline bubbles),因此您希望尽可能避免使用它。这意味着编写代码,以便具有依赖关系的指令尽可能地彼此分散,并在其间插入独立的指令。
在A完成后是否存在确保B启动的机制?
是的,这种机制通常存在,但您通常不希望使用它们,因为它们会通过破坏管道的整个优势来减慢执行速度。
这些机制被称为序列化指令(或有时称为“障碍”),因为它们构成了一个障碍,导致执行在特定点被序列化。
例如,在x86架构上,CPUID
指令是序列化指令(实际上是one of several)。所以你可以这样做:
Instruction A
CPUID
Instruction B
这将确保指令B在指令A完成执行后之前不会启动。
来自英特尔架构手册:
CPUID
可以在任何权限级别执行,以序列化指令执行。序列化指令执行保证在获取和执行下一条指令之前完成对先前指令的标志,寄存器和存储器的任何修改。另见IA-32 Intel Architecture Software Developer's Manual, Volume 3 AP-485,英特尔处理器识别和CPUID指令第7章中的“序列化指令”。
从技术上讲,这并不能保证指令B不会启动管道。例如,处理器可能在完成执行指令A之前解码并获取指令B.但是,从程序员的角度来看(即可观察行为),就好像指令B只在之后启动指令A已经完成。
答案 1 :(得分:0)
有几种类型的序列化,仅供参考,您需要序列化/记分板指令,以防止年轻人在上一次提交之前进入OOO机器。 CPUID
这样做但很重。其他一些说明也可以这样做(见下文)。
然后,有一些面向内存的机制来确保加载或存储缓冲区被耗尽,主要用于内存排序purporses。 LFENCE
和SFENCE
分别保证MFENCE
和ALL PRIVILEGES
。重要的是要注意这与指令序列化有些正交,而英特尔的开发人员手册例如说明:
MFENCE指令针对所有加载和存储进行排序 说明,其他MFENCE指令,任何LFENCE和SFENCE 说明和任何序列化指令(例如CPUID 指令)。 MFENCE不会序列化指令流
可能存在组合,例如我认为锁定操作(例如,锁定的inc)保证指令和存储器序列化(第一个由于存储器排序,后者由于原子性)。另见第3A卷第8章 - https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
最后,您需要的还有一种方法可以确保将数据写入内存(或者在某些情况下使用持久存储。存储缓冲区耗尽仍然意味着数据可以驻留在本地缓存中。在正常的WB内存上就足够了,因为任何其他观察者都必须窥探它并获得更新,但在某些情况下,您希望确保数据不会因为某些崩溃而丢失。为此您可以使用CLFLUSH / CLFLUSHOPT或PCOMMIT( deprecated on some systems)/ CLWB
再次 - 以上所有内容都有不同的含义,具体取决于您的需求。