x86-64使用LFENCE

Cha*_*ngi 6 assembly x86-64 cpu-architecture atomicity

我正在尝试了解使用RDTSC/RDTSCP测量时间时使用栅栏的正确方法.关于与此相关的SO的几个问题已经得到了精心解答.我经历了其中一些.我也经历过关于同一主题的非常有用的文章:http: //www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-基准-代码执行-paper.pdf

但是,在另一个在线博客中,有一个在x86上使用LFENCE而不是CPUID的例子.我想知道LFENCE如何阻止早期商店污染RDTSC测量.例如

<Instr A>
LFENCE/CPUID
RDTSC
<Code to be benchmarked>
LFENCE/CPUID
RDTSC 
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,LFENCE确保它之前完成的所有早期加载(因为SDM说:LFENCE指令不能通过先前的读取.).但是早期的商店呢(比如,Instr A是商店)?我理解为什么CPUID有效,因为它是一个序列化指令,但LFENCE不是.

我发现的一个解释是在英特尔SDM VOL 3A第8.3节中,以下脚注:

LFENCE确实为指令排序提供了一些保证.它在本地完成所有先前指令之前不会执行,并且在LFENCE完成之前不会再执行指令.

所以LFENCE本质上就像一个MFENCE.在那种情况下,为什么我们需要两个单独的指令LFENCE和MFENCE?

我可能错过了一些东西.

提前致谢.

Mar*_*oom 8

关键点在于引用句子中的本地副词" 它直到所有先前的指令都在本地完成后才执行 ".

我无法在整套英特尔手册中找到"完全本地"的明确定义,我的推测在下面解释.


为了在本地完成,指令必须使其输出计算并且可用于其依赖链中的其他指令.此外,该指令的任何副作用必须在核心内部可见.

为了在全局范围内完成,指令必须使其副作用对其他系统组件(如其他CPU)可见.

如果我们没有限定那种"完整性"我们正在谈论它通常意味着它不关心或它隐含在上下文中.


对于在本地和全球范围内完成的大量指令,它是相同的.例如,
对于负载,为了在本地完成,必须从内存或高速缓存中获取一些数据.这与全局完成相同,因为如果我们不首先从内存层次结构中读取,则无法标记负载完成.

然而,对于商店而言,情况则不同.

英特尔处理器有一个存储缓冲区来处理内存写入,从手册3的第11.10章开始:

Intel 64和IA-32处理器将每个写入(存储)临时存储在存储缓冲区中的内存中.存储缓冲区通过允许处理器继续执行指令而不必等到对存储器和/或高速缓存的写入完成来提高处理器性能.它还允许延迟写入以更有效地使用存储器访问总线周期.

因此,存储可以通过放入存储缓冲区在本地完成,从核心角度来看,写入就像它已经一直到存储器一样.
在特定情况下,来自商店的同一核心的负载甚至可以读回该值(这称为存储转发).

要在全球范围内完成,商店需要从商店缓冲区中排出.

最后必须添加通过序列化指令排出存储缓冲区:

在以下情况下,存储缓冲区的内容总是耗尽到内存中:
•(仅限P6和更新的处理器系列)执行序列化指令时.
•(Pentium III,仅限更新的处理器系列)使用SFENCE指令订购存储时.
•(仅限Pentium 4和更新的处理器系列)使用MFENCE指令订购商店时.


引进正在做的,让我们来看看lfence,mfencesfence做到:

LFENCE不会执行,直到所有先前的指令在本地完成,并且在LFENCE完成之前没有后续指令开始执行.

MFENCE对在MFENCE指令之前发出的所有内存加载和存储到内存指令执行序列化操作.MFENCE不会序列化指令流.

SFENCE对SFENCE指令之前发出的所有存储器到存储器指令执行串行化操作.

因此lfence,较弱的序列化形式不会耗尽存储缓冲区,因为它有效地在本地序列化指令,所有负载必须在它完成之前完成.

sfence仅序列化商店,它基本上不允许进程在sfence退役之前再执行任何商店.它还会耗尽Store缓冲区.

mfence不是二者的简单组合,因为它不是传统意义上的序列化,这是一个sfence还防止将来负载执行.


首先sfence引入的可能是值得的,之后的其他两个则可以实现对内存排序的更精细控制.

最后,我习惯于rdtsc在两条指令之间关闭一条指令lfence,以确保不会重新排序"向后"和"向前".
不过我确信这种技术合理.