【问题标题】:MFENCE/SFENCE/etc "serialize memory but not instruction execution"?MFENCE/SFENCE/etc“序列化内存而不是指令执行”?
【发布时间】:2014-12-28 06:30:03
【问题描述】:

英特尔的系统编程指南,第 8.3 节,关于 MFENCE/SFENCE/LFENCE 的状态:

“以下指令是内存排序指令,不是序列化指令。这些指令会耗尽数据内存子系统。它们不会序列化指令执行流。

我试图弄清楚为什么这很重要。在多线程代码中,对内存的写入/读取正是需要以明确定义的顺序发生的事情。当然,I/O 发生的顺序可能很重要,但 I/O 指令无论如何都是“序列化指令”。 CPU 应该可以对指令进行重新排序,这些指令(例如)根据需要在寄存器中进行算术运算;我认为您没有任何理由要“序列化”此类操作。

是否存在真正需要完全序列化指令的情况,而 MFENCE 仅对加载和存储的序列化“不够”?

【问题讨论】:

    标签: assembly x86


    【解决方案1】:

    是否存在真正需要完全序列化指令的情况,而 MFENCE 仅对加载和存储的序列化“不够”?

    基准测试和代码分析。

    如果您尝试衡量代码序列的性能,尤其是在非常短的代码序列时,确保基准操作的某些部分不会在定时序列之外执行,这一点很重要。例如,如果您的代码看起来像这样的伪代码:

    start = RDTSC()
    do some stuff
    end = RDTSC()
    cycles = end - start
    

    确保中间的任何代码都不会在第一个 RDTSC 之前或第二个之后执行,这一点很重要。

    很高兴,有一个完美的指令可用:CPUID 正在完全序列化。

    【讨论】:

    • 没想到... +1
    【解决方案2】:

    英特尔手册的第 8.3 节包含被认为是完全序列化的指令的完整列表(另请参阅:How many memory barriers instructions does an x86 CPU have?):

    • 特权序列化指令 — INVD、INVEPT、INVLPG、INVVPID、LGDT、LIDT、LLDT、LTR、MOV(用于控制寄存器,使用 MOV CR8 3 )、MOV(到调试寄存器)、WBINVD 和 WRMSR 除外 4.

    • 非特权序列化指令 — CPUID、IRET 和 RSM。

    我认为除了CPUID 之外的所有这些指令都在序列化,因为指令的语义要求它是这样的。例如,如果WBINV 没有序列化,那么它可能会与访问内存的其他更早或更晚的操作重新排序,并且当指令退出时,缓存层次结构的状态将不清楚。

    CPUID 指令最初是在奔腾处理器中引入的,奔腾处理器是一种推测性的有序处理器。该指令的一个典型用途是检查当前处理器是否支持特定功能,然后如果支持,则跳转到使用该功能的一段代码(例如执行指令)。如果CPUID 没有进行序列化,我不确定会出现什么复杂情况。例如,如果它被用于检查处理器是否支持某些特定指令并且分支预测器错误地预测包含该指令的路径被采用,那么解码器会将其视为无效指令。这种情况可以使用与分支错误预测和无效指令相同的机制来处理。

    RDTSC 指令也是在奔腾处理器中首次引入的。但是,奔腾软件开发人员手册中的任何地方都没有提到您需要使用带有RDTSC 的序列化指令。这是有道理的,因为处理器是有序的且宽度为 2,因此 RDTSC 只能与它之前或之后的单个指令重叠。在 Pentium Pro 手册中,确实提到由于乱序执行,您需要使用序列化指令。这里的重点是CPUID 甚至在我们不需要RDTSC 的处理器上进行序列化。这意味着CPUID 进行序列化的最初原因是另外一回事。 Pentium 手册确实提到了需要使用序列化指令的两种情况。

    15.4。 I/O 的顺序

    因此,使用内存映射 I/O 会产生以下可能性: I/O 读取可能在前一个内存写入之前执行 操作说明。要在 Intel486 CPU 上消除这种可能性,请使用 用于读取的 I/O 指令。为了消除这种可能性 奔腾处理器,插入其中一条序列化指令,如 CPUID,在操作之间。

    18.2.3。自修改代码

    因为写入的线性地址是对照线性检查的 已预取指令的地址,特别注意 必须采取自修改代码正常工作时 指令的物理地址和写入的数据是 相同,但线性地址不同。在这种情况下,有必要 在写入之后和之前执行序列化操作 执行修改后的指令。

    CPUID 之外的所有序列化指令都不适合用于通用序列化,因为它们要么是特权的,要么会显着影响性能,要么改变程序的控制流,要么改变段描述符表。 CPUID 也不完美,因为它改变了一些架构寄存器的值。所以我认为英特尔可以选择引入一个新的通用序列化指令,该指令只对流水线进行序列化,或者将CPUID 设为序列化指令。也可能是CPUID 本身出于某种原因需要序列化。不管怎样,他们似乎已经决定让CPUID 扮演通用序列化指令的角色。考虑到CPUID 修改四个寄存器对性能的影响与序列化的影响相比可以忽略不计,这是有道理的。

    后来,在 Pentium Pro 处理器中,Intel 建议使用手册中的CPUIDRDTSC 进行精确测量(参见:Get CPU cycle count?)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-20
      • 2015-02-22
      • 2013-09-30
      • 2012-08-17
      • 1970-01-01
      • 1970-01-01
      • 2018-07-12
      相关资源
      最近更新 更多