我认为你应该检查CPU的MACHINE_CLEARS.SMC
性能计数器(它是MACHINE_CLEARS
事件的一部分),它可用于你的Air powerbook中使用的Sandy Bridge 1,也可用于你的Xeon中使用的Nehalem 2-搜索“smc”。您可以使用oprofile
、perf
或英特尔的Vtune
来查找其值:
http://software.intel.com/sites/products/documentation/doclib/iss/2013/amplifier/lin/ug_docs/GUID-F0FD7660-58B5-4B5D-AA9A-E1AF21DDCA0E.htm
机器清除
描述
某些事件需要清除整个流水线并从上一个已退休指令后重新启动。该度量衡三种此类事件:内存排序违规、自修改代码和加载非法地址范围的某些内容。
可能出现的问题
执行时间的重要部分花费在处理机器清除上。检查MACHINE_CLEARS事件以确定具体原因。
SMC: http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/amplifierxe/win/win_reference/snb/events/machine_clears.html|机器清除
MACHINE_CLEARS事件代码:0xC3
SMC掩码:0x04
检测到自修改代码(SMC)。
检测到自修改代码机器清除的数量。
英特尔还提到了关于smc http://software.intel.com/en-us/forums/topic/345561(链接自Intel Performance Bottleneck Analyzer's taxonomy)
当检测到自修改代码时,此事件将触发。这通常可以被二进制编辑人员用来强制程序走特定的路径(例如黑客)。该事件计算程序写入代码段的次数。自修改代码会在所有Intel 64和IA-32处理器中造成严重的惩罚。修改后的高速缓存行被写回L2和LLC高速缓存。此外,指令需要重新加载,从而导致性能下降。
我认为你将看到一些这样的事件。如果是这样的话,那么 CPU 就能够检测到代码自修改的动作,并触发“机器清除”——流水线的完全重启。第一个阶段是取指令,它们将会向 L2 缓存请求新的操作码。我非常想知道每次执行你的代码时 SMC 事件的确切数量——这将为我们提供有关延迟的估计。(SMC 在某些单位中计算,其中 1 个单位被假定为 1.5 个 CPU 周期,参见英特尔优化手册 B.6.2.6 节)
我们可以看到 Intel 表示“从最后一个退役指令之后重新启动”,所以我认为最后一个退役指令将是 mov;而你的 nops 已经在流水线中了。但是,在 mov 退役时,SMC 将被触发并杀死流水线中的所有内容,包括 nops。
这种由 SMC 引起的流水线重启不便宜,Agner 在
Optimizing_assembly.pdf 中进行了一些测量——“17.10 自修改代码(所有处理器)”(我认为任何 Core2/CoreiX 处理器在此方面都类似于 PM)。
执行修改后的代码会带来一定的惩罚,对于P1处理器约为19个时钟周期,PMMX约为31个时钟周期,而对于PPro、P2、P3和PM处理器则在150-300个时钟周期之间。自修改代码后,P4将清除整个跟踪缓存。在80486及更早版本的处理器中,必须在修改代码和被修改代码之间进行跳转,以清除代码缓存。自修改代码不被认为是良好的编程实践。只有当提速效果显著且修改后的代码被执行很多次以至于优势大于使用自修改代码的惩罚时,才应该使用自修改代码。
本文推荐了使用不同的线性地址来使SMC检测失败:https://dev59.com/3Wgu5IYBdhLWcg3w6bGo#10994728 - 我会试着找到实际的英特尔文献...现在无法回答你的真正问题。
这里可能有一些提示:优化手册,248966-026,2012年4月“3.6.9混合代码和数据”:
将可写数据放置在代码段中可能无法与自修改代码区分开来。在代码段中的可写数据可能会遭受与自修改代码相同的性能惩罚。
下一节内容:
软件应避免在正在执行的1-KB子页面中写入代码页或在同样正在被写入的2-KB子页面中获取代码。此外,与其他处理器共享包含直接或推测执行代码的页面作为数据页面可以触发SMC条件,导致整个机器的流水线和跟踪缓存被清除。这是由于自修改代码条件造成的。
因此,可能有一些原理图控制可写和可执行子页面的交叉点。
您可以尝试从其他线程进行修改(交叉修改代码)-但需要非常小心的线程同步和管道刷新(您可能希望在编写器线程中包含一些延迟的强制执行;在同步后进行CPUID是想要的)。但是你应该知道他们已经使用“核武器”来修复了这个问题-请检查
US6857064专利。
我有点困惑手册提到了P6和奔腾处理器
如果您获取、解码并执行了英特尔指令手册的旧版本,这是可能的。您可以重置流水线并检查此版本:
订单号码:325462-047US,2013年6月“11.6自修改代码”。这个版本仍然没有提到新的CPU,但是提醒当您使用不同的虚拟地址进行修改时,在微架构之间可能会出现不兼容的情况(在您的Nehalem / Sandy Bridge上可能有效,在Skymont上可能无效)。
11.6 自修改代码
在代码段中写入内存位置时,会导致处理器缓存的相关高速缓存行(或多个行)无效。此检查基于指令的物理地址。此外,P6系列和Pentium处理器会检查对代码段的写入是否可以修改已预取以执行的指令。如果写入影响了预取指令,则无效化预取队列。后一项检查是基于指令的线性地址。对于Pentium 4和Intel Xeon处理器,在代码段中的指令进行写入或嗅探时,目标指令已被解码并驻留在跟踪缓存中,将使整个跟踪缓存失效。这种行为意味着自修改代码的程序在Pentium 4和Intel Xeon处理器上运行时,可能会导致性能严重下降。
实际上,线性地址的检查不应该在IA-32处理器之间造成兼容性问题。包括自修改代码的应用程序使用相同的线性地址来修改和获取指令。
系统软件(例如调试器),可能使用与获取指令时不同的线性地址来修改指令,在修改指令执行之前会执行序列化操作,如CPUID指令,这将自动重新同步指令缓存和预取队列。(有关使用自修改代码的更多信息,请参见8.1.3节,“处理自修改和交叉修改代码”)。
对于Intel486处理器,在缓存中写入指令将在缓存和内存中修改它,但如果在写入之前预取指令,则可能执行旧版本的指令。为了防止执行旧指令,应编写跳转指令,并在任何修改指令后立即进行以刷新指令预取单元。
实时更新,在谷歌上搜索"SMC检测"(带引号),有一些关于现代Core2/Core iX如何检测SMC以及许多与Xeons和Pentiums挂钩的errata列表的详细信息:
http://www.google.com/patents/US6237088 管道中跟踪飞行指令的系统和方法 @ 2001
DOI 10.1535/itj.1203.03 (在citeseerx.ist.psu.edu上有免费版本,请在Google上搜索)- "INCLUSION FILTER"被添加到Penryn中以降低假SMC检测数量;图9显示了“现有的包含检测机制”
http://www.google.com/patents/US6405307 - SMC检测逻辑的旧专利
根据专利US6237088(图5,摘要),存在“线路地址缓冲区”(具有许多线性地址,每个已获取的指令对应一个地址 - 或者换句话说,缓冲区充满了以高速缓存线为精度的获取IP)。每个存储器或更确切地说,每个存储器的“存储器地址”阶段将被馈入并行比较器中进行检查,检查存储器是否与当前正在执行的任何指令相交。
这两项专利都没有明确说明,在SMC逻辑中它们将使用物理还是逻辑地址... Sandy Bridge中的L1i是VIPT(虚拟索引,物理标记,索引使用虚拟地址,标记使用物理地址。)根据http://nick-black.com/dankwiki/index.php/Sandy_Bridge,因此当L1缓存返回数据时,我们拥有物理地址。 我认为英特尔可能在SMC检测逻辑中使用物理地址。
更进一步,
http://www.google.com/patents/US6594734 @1999(2003年发表,只需要记住CPU设计周期大约为3-5年)在“摘要”部分中指出SMC现在位于TLB中并使用物理地址(或者换句话说 - 请不要试图欺骗SMC检测器):
自修改代码通过翻译后备缓存来检测..[其中]已存储有物理页面地址,可以使用存储到内存的物理内存地址进行监视。...为了提供比网页地址更好的粒度,每个高速缓存条目中都包括FINE HIT位,将缓存中的信息与内存中页面的部分相关联。
(页面部分,在专利US6594734中称为象限,听起来像是1K子页面,不是吗?)
然后他们说
因此,由存储指令到内存的触发器引起的窥视者可以通过将指令高速缓存中所有指令的物理地址与相应页面或内存页中存储的所有指令的地址进行比较,执行SMC检测。如果存在地址匹配,则表示某个内存位置已被修改。在地址匹配的情况下(表示SMC条件),退役单元会清除指令高速缓存和指令流水线,并从内存中获取新的指令以存储到指令高速缓存中。
由于SMC检测的窥视者是物理的,ITLB通常接受线性地址作为输入并将其转换为物理地址,因此ITLB还形成了一个基于物理地址的内容可寻址存储器,并包括一个额外的输入比较端口(称为窥视端口或反向转换端口)。
-- 因此,为了检测SMC,他们强制将存储器通过嗅探(其他内核/ CPU或DMA写入我们的高速缓存中也会传递类似的嗅探信号)将物理地址返回到指令缓冲区,如果嗅探的物理地址与存储在指令缓冲区中的缓存行发生冲突,则会通过从iTLB到退役单元传递的SMC信号重新启动流水线。可以想象,在这种从dTLB通过iTLB到退役单元的嗅探循环中将浪费多少CPU时钟(尽管早于mov并且没有副作用,但它无法退役下一个“nop”指令)。但是,ITLB具有物理地址输入和第二个CAM(大而热),只是为了支持和防御疯狂和欺骗性的自修改代码。
PS:如果我们使用大页面(4M或者可能是1G),会怎样呢?L1TLB具有大页面条目,并且对于4MB页面的1/4来说,可能会出现许多错误的SMC检测...
PPS:有一种变体,即不同线性地址的SMC的误处理仅存在于早期的P6 / Ppro / P2...