我认为,你应该检查一下MACHINE_CLEARS.SMC
性能计数器(一部分MACHINE_CLEARS
CPU 的事件(在 Sandy Bridge 中可用)1,用于您的 Air powerbook;也可在 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)。
检测到的自修改代码机器清除数。
Intel也谈到了smchttp://software.intel.com/en-us/forums/topic/345561(链接自英特尔性能瓶颈分析器的分类
当检测到自修改代码时会触发此事件。这通常可以被进行二进制编辑的人用来强制它采取特定的路径(例如黑客)。此事件计算程序写入代码段的次数。自修改代码会在所有 Intel 64 和 IA-32 处理器中造成严重损失。修改后的高速缓存行被写回L2 和LLC 高速缓存。此外,还需要重新加载指令,从而导致性能损失。
我想,你会看到一些这样的事件。如果是,那么 CPU 能够检测到自我修改代码的行为,并引发“机器清除”——完全重新启动管道。第一个阶段是 Fetch,它们会向 L2 缓存请求新的操作码。我对每次执行代码时的 SMC 事件的确切计数非常感兴趣 - 这将为我们提供一些有关延迟的估计。(SMC 以某些单位进行计数,其中 1 个单位假定为 1.5 个 cpu 周期 - B.6.2。 intel优化手册6)
我们可以看到英特尔说“从最后一条退役指令后重新启动”,所以我认为最后一条退役指令将是mov
;并且您的 nops 已经在准备中了。但 SMC 将在 mov 退休时筹集,这将杀死管道中的所有内容,包括 nop。
SMC 引起的管道重启并不便宜,Agner 在优化装配.pdf- “17.10 自修改代码(所有处理器)”(我认为任何 Core2/CoreiX 都像这里的 PM):
修改一段代码后立即执行的损失约为 P1 19 个时钟,PMMX 31 个时钟,PPro、P2、P3、PM 150-300 个时钟。自修改代码后,P4 将清除整个跟踪缓存。 80486 和更早的处理器需要在修改代码和修改后的代码之间跳转,以便刷新代码缓存。
...
自修改代码不被认为是良好的编程实践。仅当以下情况时才应使用它
速度的提升是巨大的,并且修改后的代码被执行了很多次,以至于
使用自修改代码的好处胜过坏处。
这里建议使用不同的线性地址来使 SMC 检测器失败:https://stackoverflow.com/a/10994728/196561- 我会尝试找到实际的英特尔文档...现在无法真正回答您真正的问题。
这里可能有一些提示:优化手册,248966-026,2012 年 4 月“3.6.9 混合代码和数据”:
将可写数据放在代码段中可能无法区分
来自自修改代码。代码段中的可写数据可能会受到影响
与自修改代码相同的性能损失。
和下一节
软件应避免写入同一 1 KB 子页中的代码页
正在执行或在正在执行的同一 2 KB 子页中获取代码
书面。此外,共享包含直接或推测执行的页面
使用另一个处理器作为数据页的代码可以触发 SMC 条件,从而导致
机器的整个管道和跟踪缓存都被清除。这是由于
自修改代码条件。
因此,可能存在一些控制可写子页面和可执行子页面的交集的原理图。
您可以尝试从其他线程进行修改(交叉修改代码)——但是需要非常小心的线程同步和管道刷新(您可能希望在编写器线程中包含一些强制延迟;同步后的 CPUID是所需要的)。但你应该知道他们已经使用“修复了这个问题”nukes“ - 查看US6857064 patent.
我对提到 P6 和 Pentium 处理器的手册有点困惑
如果您已获取、解码并执行了英特尔说明手册的某些过时版本,则这是可能的。您可以重置管道并检查此版本:订单号: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 的详细信息,以及 SMC 检测器中悬挂的 Xeon 和 Pentium 的许多勘误表:
http://www.google.com/patents/US6237088用于跟踪管道中的飞行指令的系统和方法 @ 2001
DOI 10.1535/itj.1203.03(谷歌搜索,citeseerx.ist.psu.edu 有免费版本) - 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这样我们就有了一级缓存返回数据时的物理地址。我认为intel可能会在SMC检测逻辑中使用物理地址。
更,http://www.google.com/patents/US6594734@ 1999(2003 年发表,请记住 CPU 设计周期约为 3-5 年)在“摘要”部分中说 SMC 现在位于 TLB 中并使用物理地址(或者换句话说 - 请不要试图愚弄SMC检测器):
使用翻译后备缓冲区检测自修改代码..[其中]存储有物理页地址snoops可以使用执行存储的物理内存地址进入记忆。 ...为了提供比地址页更细的粒度,高速缓存中的每个条目都包含 FINE HIT 位,将高速缓存中的信息与内存中页面的部分相关联。
(页面的一部分,在专利 US6594734 中称为象限,听起来像 1K 子页面,不是吗?)
然后他们说
所以监听,由将指令存储到内存中触发,可以通过将存储在指令高速缓存内的所有指令的物理地址与存储在关联的一个或多个存储器页面内的所有指令的地址进行比较来执行SMC检测。如果地址匹配,则表明内存位置已被修改。在地址匹配的情况下,指示SMC条件,指令高速缓存和指令流水线被引退单元刷新,并且从存储器取出新指令以存储到指令高速缓存中。
由于用于 SMC 检测的监听是物理的,并且 ITLB 通常接受线性地址作为输入以转换为物理地址,因此 ITLB 额外形成为物理地址上的内容可寻址存储器,并包括附加的输入比较端口(称为作为监听端口或反向转换端口)
- 因此,为了检测 SMC,它们强制存储通过探听将物理地址转发回指令缓冲区(类似的探听将从其他内核/CPU 或从 DMA 写入我们的缓存传递......),如果探听的 phys.地址与存储在指令缓冲区中的高速缓存行发生冲突,我们将通过从 iTLB 传递到退休单元的 SMC 信号来重新启动管道。可以想象在这样的从 dTLB 经 iTLB 到退休单元的监听循环中会浪费多少 cpu 时钟(它不能退休下一个“nop”指令,尽管它比 mov 更早执行并且没有副作用)。但是WAT? ITLB有物理地址输入和第二个CAM(又大又热)只是为了支持和防御疯狂和作弊的自修改代码。
PS:如果我们使用大页面(4M 或可能是 1G)怎么办? L1TLB 具有巨大的页面条目,对于 4 MB 页面的 1/4 可能存在大量错误 SMC 检测...
PPS:有一个变体,即对具有不同线性地址的 SMC 的错误处理仅出现在早期的 P6/Ppro/P2 中......