Linux perf报告缓存未命中意外指令
我正在尝试将一些性能工程技术应用于Dijkstra算法的实现.为了找到(天真的和未经优化的)程序中的瓶颈,我使用perf命令来记录缓存未命中的数量.相关的代码片段如下,它找到距离最小的未访问节点:
for (int i = 0; i < count; i++) { if (!visited[i]) { if (tmp == -1 || dist[i] < dist[tmp]) { tmp = i; } } } 对于LLC-load-misses指标,perf报告显示程序集的以下注释: │ for (int i = 0; i < count; i++) { ? 1.19 │ ff: add $0x1,%eax ? 0.03 │102: cmp 0x20(%rsp),%eax ? │ ↓ jge 135 ? │ if (!visited[i]) { ? 0.07 │ movslq %eax,%rdx ? │ mov 0x18(%rsp),%rdi ◆ 0.70 │ cmpb $0x0,(%rdi,%rdx,1) ? 0.53 │ ↑ jne ff ? │ if (tmp == -1 || dist[i] < dist[tmp]) { ? 0.07 │ cmp $0xffffffff,%r13d ? │ ↑ je fc ? 0.96 │ mov 0x40(%rsp),%rcx ? 0.08 │ movslq %r13d,%rsi ? │ movsd (%rcx,%rsi,8),%xmm0 ? 0.13 │ ucomis (%rcx,%xmm0 ? 57.99 │ ↑ jbe ff ? │ tmp = i; ? │ mov %eax,%r13d ? │ ↑ jmp ff ? │ } ? │ } ? │ } 我的问题是:为什么jbe指令会产生如此多的缓存未命中?如果我没有弄错的话,该指令根本不必从内存中检索任何内容.我认为它可能与指令缓存未命中有关,但即使仅使用L1-dcache-load-miss测量L1数据缓存未命中,也表明该指令中存在大量缓存未命中. 这有点让我感到困惑.谁能解释这个(在我看来)奇怪的结果?先感谢您. 解决方法
关于你的例子:
高柜台前和高柜台有几条指令: │ movsd (%rcx,%xmm0 0.13 │ ucomis (%rcx,%xmm0 57.99 │ ↑ jbe ff “movsd”将来自(%rcx,%rsi,8)(某些数组访问)的字加载到xmm0寄存器中,“ucomis”从(%rcx,%rdx,8)加载另一个字,并将其与xmm0中刚刚加载的值进行比较寄存器. “jbe”是条件跳跃,取决于比较结果. 许多现代的英特尔CPU(以及AMD可能也是)可以并且将一些操作组合(组合)(realworldtech.com/nehalem/5“融合到单个uop,CMP JCC”)中,并且cmp条件跳转非常常见的指令组合到融合(您可以使用Intel IACA模拟工具进行检查,对您的CPU使用ver 2.1).可以在perf / PMU / PEBS中错误地报告融合对,其中大多数事件偏向两个指令之一. 此代码可能意味着表达式“dist [i]< dist [tmp]”生成两次内存访问,并且这两个值都在ucomis指令中使用,该指令与jbe条件跳转(部分?)融合. dist [i]或dist [tmp]或两个表达式都会产生大量未命中.任何这样的未命中将阻止ucomis生成结果并阻塞jbe以给出下一条指令以执行(或退出预测的指令).因此,jbe可能会获得高计数器的所有声誉,而不是真正的内存访问指令(对于像“缓存”响应这样的“远”事件,最后一个被阻止的指令存在一些偏差). 您可以尝试将visited [N]和dist [N]数组合并到struct {int visited;的数组[N]中.当你访问array [i] .visited时,浮动dist}强制预取array [i] .dist,或者你可以尝试改变顶点访问的顺序,或重新编号图顶点,或为下一个或多个元素做一些软件预取( ?) 关于名称问题和可能的非核心倾斜的通用perf事件. Linux中的perf(perf_events)工具在被称为perf list时使用预定义的事件集,并且某些列出的硬件事件可能无法实现;其他映射到当前的CPU功能(并且一些映射不完全正确).有关真实PMU的一些基本信息在您的https://software.intel.com/sites/products/collateral/hpc/vtune/performance_analysis_guide.pdf中(但它有相关Nehalem-EP变体的更多详细信息). 对于Nehalem(Intel Core i5 750,L3缓存为8MB,没有多CPU /多插槽/ NUMA支持),perf会将标准(“Generic cache events”)LLC-load-miss事件映射为..“OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS”as写在perf事件映射(唯一的)的最佳文档中 – 内核源代码 http://elixir.free-electrons.com/linux/v4.8/source/arch/x86/events/intel/core.c#L1103 u64 nehalem_hw_cache_event_ids ... [ C(LL ) ] = { [ C(OP_READ) ] = { /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ [ C(RESULT_ACCESS) ] = 0x01b7,/* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ [ C(RESULT_MISS) ] = 0x01b7,... /* * Nehalem/Westmere MSR_OFFCORE_RESPONSE bits; * See IA32 SDM Vol 3B 30.6.1.3 */ #define NHM_DMND_DATA_RD (1 << 0) #define NHM_DMND_READ (NHM_DMND_DATA_RD) #define NHM_L3_MISS (NHM_NON_DRAM|NHM_LOCAL_DRAM|NHM_REMOTE_DRAM|NHM_REMOTE_CACHE_FWD) ... u64 nehalem_hw_cache_extra_regs .. [ C(LL ) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_L3_ACCESS,[ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_L3_MISS, 我认为这个事件并不精确:cpu管道会将(无序)加载请求发布到缓存层次结构并执行其他指令.经过一段时间(around 10 cycles到达并得到L2和40 cycles to reach L3的响应),在相应的(offcore?)PMU中会有miss标志响应增加计数器.在此计数器溢出时,将从此PMU生成分析中断.在几个cpu时钟周期中,它将到达管道中断它,perf_events子系统的处理程序将通过注册当前(中断的)EIP / RIP指令指针并将PMU计数器重置为某个负值来处理此问题(例如,-100000以获得每个中断)计算100000 L3未命中;使用perf记录-e LLC-load-miss -c 100000设置精确计数或perf将自动调谐限制以获得一些默认频率).注册的EIP / RIP不是加载命令的IP,也可能不是要使用加载数据的命令的EIP / RIP. 但是如果你的CPU是系统中唯一的插槽并且你访问普通内存(而不是一些映射的PCI-express空间),那么L3 miss实际上将被实现为本地内存访问,并且有一些计数器用于此…(https://software.intel.com/en-us/node/596851 – “此处丢失的任何内存请求必须由本地或远程DRAM提供服务”). 您的CPU有一些PMU事件列表: >英特尔官方“英特尔?64和IA-32架构软件开发人员手册”(SDM):https://software.intel.com/en-us/articles/intel-sdm,第3卷,附录A > 3B:https://software.intel.com/sites/default/files/managed/7c/f1/253669-sdm-vol-3b.pdf“18.8基于英特尔?微观结构代码名称NEHALEM的处理器性能监测”,第213页“第3B卷18-35” >来自oprofile http://oprofile.sourceforge.net/docs/intel-corei7-events.php 应该有一些关于ANY_LLC_MISS offcore PMU事件实现的信息和Nhm的PEBS事件列表,但我现在找不到它. 我建议您使用https://github.com/andikleen/pmu-tools的ocperf和CPU的任何PMU事件,而无需手动编码.您的CPU中有一些PEBS事件,并且存在用于某种内存访问分析的延迟分析/ 我也建议你尝试使用kcachegrind GUI来查看valgrind程序的 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |