问题标签 [intel-pmu]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
assembly - 特定处理器上的性能监控计数器 (RDPMC)
我正在尝试使用RDPMC
指令来计算已停用的指令,并且正如英特尔软件开发人员手册第 3 卷附录 A(在性能监控部分)提到的那样:
• Instructions Retired — 事件选择 C0H,Umask 00H 此事件计算退出时的指令数。对于由多个微操作组成的指令,此事件计算指令的最后一个微操作的退出。带有 REP 前缀的指令算作一条指令(不是每次迭代)。多操作指令的最后一个微操作退出之前的故障不计算在内。
我使用此处的答案来启用 Linux Kernel-Mode 模块的性能计数器。
从这里可以看到(描述RDPMC
):
将 ECX 寄存器中指定的 40 位性能监控计数器的内容加载到寄存器 EDX:EAX 中。EDX 寄存器加载计数器的高 8 位,EAX 寄存器加载低 32 位。Pentium® Pro 处理器有两个性能监控计数器(0 和 1),它们分别通过在 ECX 寄存器中放置 0000H 或 0001H 来指定。
之后,我将 0 放入RAX
并执行RDPMC
(在用户模式下),但RDPMC
多次执行后EDX:EAX
仍然为零。
所以我的问题是:
- 如何在用户模式下计算特定进程的退休指令?
Event select C0H
和和有什么区别?Umask 00H
我想知道如何使用C0H
和00H
?
linux-kernel - What restriction is perf_event_paranoid == 1 actually putting on x86 perf?
Newer Linux kernels have a sysfs tunable /proc/sys/kernel/perf_event_paranoid
which allows the user to adjust the available functionality of perf_events
for non-root users, with higher numbers being more secure (offering correspondingly less functionality):
From the kernel documenation we have the following behavior for the various values:
perf_event_paranoid:
Controls use of the performance events system by unprivileged users (without CAP_SYS_ADMIN). The default value is 2.
-1: Allow use of (almost) all events by all users Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK
>=0: Disallow ftrace function tracepoint by users without CAP_SYS_ADMIN Disallow raw tracepoint access by users without CAP_SYS_ADMIN
>=1: Disallow CPU event access by users without CAP_SYS_ADMIN
>=2: Disallow kernel profiling by users without CAP_SYS_ADMIN
I have 1
in my perf_event_paranoid
file which should "Disallow CPU event access" - but what does that mean exactly?
A plain reading would imply no access to CPU performance counter events (such as Intel PMU events), but it seems I can access those just fine. For example:
Here, many of the events are CPU PMU events (cycles
, instructions
, branches
, branch-misses
, cache-misses
).
If these aren't the CPU events being referred to, what are they?
assembly - LSD 能否从检测到的循环的下一次迭代中发出 uOP?
我正在研究我的 Haswell 端口 0 上的分支单元的功能,从一个非常简单的循环开始:
使用perf
我们看到循环以 1c/iter 运行
我对这些结果的解释是:
- D 和 J 都是并行调度的。
- J 具有 1 个周期的倒数吞吐量。
- D 和 J 都被最优调度。
但是,我们也可以看到 RS 永远不会被填满。
它最多可以以 2 uOPs/c 的速率调度 uOP,但理论上可以得到 4 uOPs/c,从而在大约 30 c 内实现完整的 RS(对于大小为 60 个融合域条目的 RS)。
据我了解,分支错误预测应该很少,uOP 应该都来自 LSD。
所以我看了一下FE:
这确认了 FE 是从 LSD 1发出的。
但是,LSD 从不发出 4 uOPs/c:
我的解释是 LSD 不能从下一次迭代2发出 uOP,因此每个周期只能将 DJ 对发送到 BE。
我的解释正确吗?
源代码在这个存储库中。
1存在一些差异,我认为这是由于允许某些上下文切换的大量迭代。
2在电路深度有限的硬件中,这听起来相当复杂。
linux - 硬件缓存事件和性能
当我运行时,perf list
我看到一堆Hardware Cache Events,如下所示:
这些事件似乎大多根据测试返回合理的值,但我想知道如何确定将这些事件映射到我系统上的硬件性能计数器事件?
也就是说,这些事件肯定是使用我的 Skylake CPU 上的一个或多个底层 x86 PMU 计数器来实现的——但是我怎么知道哪些呢?
您可以查看/sys/devices/cpu/events
其他硬件事件,但不能查看“硬件缓存事件”。
linux - 为什么 Linux perf 在 x86 上使用事件 l1d.replacement 来表示“L1 dcache 未命中”?
在 Intel x86 上,Linux 使用事件l1d.replacements
来实现其L1-dcache-load-misses
事件。
该事件定义如下:
计算 L1D 数据线替换,包括机会替换,以及需要停止替换或替换块的替换。
也许天真地,我本来希望perf
使用类似 的东西mem_load_retired.l1_miss
,它支持 PEBS 并被定义为:
计算在 L1 缓存中丢失的至少一个 uop 的已停用加载指令。(支持PEBS)
事件值通常不是非常接近,有时它们变化很大。例如:
与 相比,“L1 未命中”的数量超过 17倍。相反,您还可以找到比计数器高得多的示例。mem_load_retired.l1_miss
l1d.replacement
l1d.replacement
mem_load_retired
究竟l1d.replacement
测量的是什么,为什么在内核中选择它,它是否比 L1 d-cache 未命中更好mem_load_retired.l1_miss
?
assembly - 为什么每次迭代的微指令数会随着流加载的步幅而增加?
考虑以下循环:
其中OFFSET
是一些非负整数,并rsi
包含指向该bss
部分中定义的缓冲区的指针。这个循环是代码中唯一的循环。也就是说,它在循环之前没有被初始化或触摸。据推测,在 Linux 上,缓冲区的所有 4K 虚拟页面将按需映射到同一个物理页面。因此,缓冲区大小的唯一限制是虚拟页面的数量。所以我们可以很容易地试验非常大的缓冲区。
该循环由 4 条指令组成。在 Haswell 的融合和非融合域中,每条指令都被解码为单个 uop。的连续实例之间也存在循环携带的依赖关系add rsi, OFFSET
。因此,在负载总是在 L1D 中命中的空闲条件下,循环应该以每次迭代大约 1 个周期执行。对于小的偏移量(步幅),这要归功于基于 IP 的 L1 流式预取器和 L2 流式预取器。但是,两个预取器都只能在 4K 页面内进行预取,并且 L1 预取器支持的最大步幅为 2K。因此,对于小步幅,每 4K 页面应该有大约 1 个 L1 未命中。随着步幅的增加,L1 未命中和 TLB 未命中的总数会增加,性能也会相应下降。
下图显示了步幅在 0 到 128 之间的各种有趣的性能计数器(每次迭代)。请注意,所有实验的迭代次数都是恒定的。只有缓冲区大小会更改以适应指定的步幅。此外,仅计算用户模式性能事件。
这里唯一奇怪的是退休的微指令的数量随着步伐的增加而增加。对于步幅 128,它从每次迭代 3 微秒(如预期)到 11 微秒。这是为什么呢?
如下图所示,步幅越大,事情就越奇怪。在此图中,步幅范围从 32 到 8192,增量为 32 字节。首先,退出指令的数量以 4096 字节的步幅从 4 线性增加到 5,之后它保持不变。加载 uops 的数量从 1 增加到 3,并且每次迭代的 L1D 加载命中数保持 1。对于我来说,只有 L1D 加载未命中的数量才有意义。
较大步幅的两个明显影响是:
- 执行时间增加,因此会发生更多的硬件中断。但是,我正在计算用户模式事件,因此中断不应干扰我的测量。我还用
taskset
or重复了所有实验,nice
并得到了相同的结果。 - 页面遍历和页面错误的数量增加。(我已经验证了这一点,但为了简洁起见,我将省略这些图表。)页面错误由内核在内核模式下处理。根据这个答案,页面遍历是使用专用硬件(在 Haswell 上?)实现的。尽管答案所基于的链接已失效。
为了进一步研究,下图显示了微码辅助的微指令数。与其他性能事件一样,每次迭代的微码辅助 uops 数量会增加,直到在步幅 4096 处达到最大值。对于所有步幅,每个 4K 虚拟页面的微码辅助 uops 数量为 506。“额外的 uops”线绘制了退役 uops 的数量减去 3(每次迭代的预期 uops 数量)。
该图显示,对于所有步幅,额外微码数略大于微码辅助微码数的一半。我不知道这意味着什么,但它可能与页面漫游有关,并且可能是观察到扰动的原因。
为什么即使每次迭代的静态指令数量相同,每次迭代的退役指令和微指令的数量也会随着更大的步幅而增加?干扰来自哪里?
下图绘制了每次迭代的周期数与每次迭代不同步幅的退役微指令数。周期数的增加速度远快于退役的微指令数。通过使用线性回归,我发现:
取两个函数的导数:
这意味着周期数增加 0.1773,退休的微指令数增加 0.0672,步幅每增加 1 个字节。如果中断和页面错误确实是扰动的(唯一)原因,那么这两种速率不应该非常接近吗?
performance - 在 Skylake (SKL) 上,为什么在超过 L3 大小的只读工作负载中存在 L2 写回?
考虑以下简单代码:
这会分配一个字节数组size
(在命令行中传入,以 KiB 为单位),将所有字节设置为相同的值(memset
调用),最后以只读方式循环遍历数组,跨越一个缓存行(64 字节),并重复这 64 次,以便每个字节被访问一次。
如果我们关闭预取1size
,如果适合缓存,我们希望它在给定级别的缓存中达到 100%,否则在该级别大部分都未命中。
我对两个事件l2_lines_out.silent
和l2_lines_out.non_silent
(以及l2_trans.l2_wb
- 但值最终与 相同non_silent
)感兴趣,它计算从 l2 静默删除的行,而不是。
如果我们从 16 KiB 到 1 GiB 运行它,并l2_lines_in.all
仅在最后一个循环中测量这两个事件(加号),我们得到:
这里的 y 轴是事件的数量,归一化为循环中的访问次数。例如,16 KiB 测试分配了一个 16 KiB 区域,并对该区域进行了 16,384 次访问,因此值 0.5 意味着每次访问平均发生 0.5 个给定事件计数。
的l2_lines_in.all
行为几乎与我们预期的一样。它从零开始,当大小超过 L2 大小时,它会上升到 1.0 并保持在那里:每次访问都会产生一行。
另外两条线的行为很奇怪。在测试适合 L3 的区域(但不在 L2 中),驱逐几乎都是无声的。但是,一旦该区域移入主内存,驱逐都是非静默的。
什么解释了这种行为?很难理解为什么L2的驱逐取决于底层区域是否适合主内存。
如果您执行存储而不是加载,几乎所有内容都是预期的非静默写回,因为更新值必须传播到外部缓存:
mem_inst_retired.l1_hit
我们还可以使用和 相关事件查看访问所访问的缓存级别:
如果您忽略 L1 命中计数器,它在几个点上看起来高得不可思议(每次访问超过 1 个 L1 命中?),结果看起来或多或少符合预期:当该区域完全适合 L2 时,主要是 L2 命中,大多数情况下L3 命中 L3 区域(在我的 CPU 上高达 6 MiB),然后错过了 DRAM。
你可以在 GitHub 上找到代码。有关构建和运行的详细信息可以在 README 文件中找到。
我在我的 Skylake 客户端 i7-6700HQ CPU 上观察到了这种行为。Haswell 2上似乎不存在相同的效果。在 Skylake-X 上,行为完全不同,正如预期的那样,因为 L3 缓存设计已更改为类似于 L2 的受害者缓存。
1您可以在最近的 Intel 上使用wrmsr -a 0x1a4 "$((2#1111))"
. 事实上,预取打开时的图表几乎完全相同,所以关闭它主要是为了消除一个混淆因素。
2有关更多详细信息,请参阅评论l2_lines_out.(non_)silent
,但那里暂时不存在,但l2_lines_out.demand_(clean|dirty)
似乎有类似的定义。更重要的是,在 Skylake 上l2_trans.l2_wb
大部分镜像non_silent
的也存在于 Haswell 上,并且似乎是镜像demand_dirty
,并且在 Haswell 上也没有表现出效果。
x86 - 是什么导致 DTLB_LOAD_MISSES.WALK_* 性能事件发生?
考虑以下循环:
其中STRIDE
是一些非负整数,并rsi
包含指向该bss
部分中定义的缓冲区的指针。这个循环是代码中唯一的循环。也就是说,它在循环之前没有被初始化或触摸。在 Linux 上,缓冲区的所有 4K 虚拟页面将按需映射到同一个物理页面。
我已经针对 0-8192 范围内的所有可能步幅运行了此代码。测量的次要页面错误数和主要页面错误数分别恰好是每个访问页面的 1 和 0。我还测量了Haswell 上的所有以下性能事件,以了解该范围内的所有进展。
DTLB_LOAD_MISSES.MISS_CAUSES_A_WALK:导致任何页面大小的页面遍历的所有 TLB 级别中的未命中。
DTLB_LOAD_MISSES.WALK_COMPLETED_4K:由于需求加载未命中导致任何 TLB 级别中的 4K 页面遍历而完成的页面遍历。
DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M:由于需求加载未命中导致任何 TLB 级别中的 2M/4M 页面遍历而完成的页面遍历。
DTLB_LOAD_MISSES.WALK_COMPLETED_1G:所有 TLB 级别的加载未命中会导致页面遍历完成。(1G)。
DTLB_LOAD_MISSES.WALK_COMPLETED:由于需求加载未命中,已完成的页面遍历任何页面大小的任何 TLB
对于所有步幅,大页面的两个计数器都为零。其他三个计数器很有趣,如下图所示。
对于大多数跨步,MISS_CAUSES_A_WALK
事件在每个访问的页面发生 5 次,WALK_COMPLETED_4K
andWALK_COMPLETED
事件在每个访问的页面发生 4 次。这意味着所有完成的页面遍历都是针对 4K 页面的。但是,有一个第五页没有完成。为什么每页有这么多的页面遍历?是什么导致这些页面走动?也许当一个page walk触发一个page fault时,在处理完这个fault之后,还会有另一个page walk,所以这可能算作两次完成的page walk。但是为什么有 4 个完成的页面遍历和一个明显取消的遍历?请注意,Haswell 上有一个单页漫游器(与 Broadwell 上的两个相比)。
我意识到有一个 TLB 预取器似乎只能预取下一页,如本线程中所述。根据该线程,预取器步行似乎没有被视为事件MISS_CAUSES_A_WALK
,WALK_COMPLETED_4K
我同意这一点。
这些似乎是这些高事件计数的两个原因:(1)页面错误导致指令被重新执行,这导致同一页面的第二个页面遍历,以及(2)在 TLB 中错过的多个并发访问. 否则,通过使用加载指令分配内存并在加载指令之后MAP_POPULATE
添加指令,每页发生一个事件和一个事件。没有,每页的计数会稍大一些。LFENCE
MISS_CAUSES_A_WALK
WALK_COMPLETED_4K
LFENCE
我尝试让每个负载访问无效的内存位置。在这种情况下,页面错误处理程序会引发一个 SIGSEGV 信号,我处理该信号以允许程序继续执行。使用该LFENCE
指令,我得到两个MISS_CAUSES_A_WALK
事件和每个 accessWALK_COMPLETED_4K
两个事件。没有,每次访问的计数会稍大一些。LFENCE
我还尝试过在循环中使用预取指令而不是按需加载。页面错误情况的结果与无效内存位置情况相同(这是有道理的,因为预取在两种情况下都失败):一个MISS_CAUSES_A_WALK
事件和一个WALK_COMPLETED_4K
事件每次预取。否则,如果预取是到具有有效内存转换的位置,则每页MISS_CAUSES_A_WALK
发生一个事件和一个WALK_COMPLETED_4K
事件。没有,每页的计数会稍大一些。LFENCE
所有实验都在同一个核心上运行。该内核上发生的 TLB 击落中断的数量几乎为零,因此它们对结果没有影响。我找不到一种简单的方法来衡量操作系统对内核的 TLB 驱逐次数,但我认为这不是一个相关因素。
尖刺
同样如上图所示,小步幅有一种特殊的模式。此外,在步幅 220 附近有一个非常奇怪的模式(尖峰)。我能够多次重现这些模式。下图放大了那个奇怪的图案,所以你可以清楚地看到它。我认为这种模式的原因是操作系统活动,而不是性能事件的工作方式或一些微架构效应,但我不确定。
循环展开的影响
@BeeOnRope 建议将其放入LFENCE
循环中并将其展开零次或多次,以更好地了解推测性、无序执行对事件计数的影响。下图显示了结果。当循环展开 0-63 次(单次迭代中的 1-64 个添加/加载指令对)时,每一行代表一个特定的加载步幅。y 轴按每页归一化。访问的页面数量与次要页面错误的数量相同。
我也运行了没有LFENCE
但有不同展开度的实验。我没有为这些制作图表,但我将在下面讨论主要区别。
我们可以得出以下结论:
- 当负载步幅小于 128 字节时,
MISS_CAUSES_A_WALK
在WALK_COMPLETED_4K
不同的展开度上表现出更高的变化。较大的步幅具有平滑的曲线,MISS_CAUSES_A_WALK
收敛到 3 或 5 并WALK_COMPLETED_4K
收敛到 3 或 4。 LFENCE
只有当展开度恰好为零时(即每次迭代有一个负载),似乎才会有所不同。如果没有LFENCE
,结果(如上所述)是每页5 个MISS_CAUSES_A_WALK
和 4 个事件。WALK_COMPLETED_4K
使用LFENCE
,它们都变为每页 3 个。对于较大的展开度,事件计数平均逐渐增加。当展开度至少为 1(即每次迭代至少有两个负载)时,LFENCE
基本上没有区别。这意味着上面的两个新图表对于没有负载的情况是相同的,LFENCE
除非每次迭代有一个负载。顺便说一句,只有在展开度为零且没有LFENCE
.- 一般来说,展开循环会减少触发和完成的行走次数,尤其是当展开程度较小时,无论负载步幅如何。无需展开,
LFENCE
可用于基本上获得相同的效果。展开后,无需使用LFENCE
. 无论如何,执行时间LFENCE
要长得多。因此,使用它来减少页面遍历将显着降低性能,而不是提高性能。
performance - RESOURCE_STALLS.RS 事件是否可能在 RS 未完全充满时发生?
RESOURCE_STALLS.RS
Intel Broadwell 的硬件性能事件描述如下:
此事件计算由于保留站 (RS) 中没有符合条件的条目而导致的停顿周期。这可能是由于 RS 溢出,或者由于 RS 数组写入端口分配方案而导致的 RS 释放(每个 RS 条目有两个写入端口而不是四个。因此,尽管 RS 并未真正满,但无法使用空条目) . 这会计算管道后端阻止来自前端的 uop 传递的周期。
这基本上说有两种情况会发生 RS 停顿事件:
- 当 RS 的所有符合条件的条目都被占用并且分配器没有停止时。
- 当“RS 释放”发生时,因为只有两个写端口,并且分配器没有停止。
在第一种情况下,“合格”是什么意思?这是否意味着并非所有条目都可以被各种微指令占用?因为我的理解是,在现代微架构中,任何条目都可以被任何类型的 uop 使用。还有什么是 RS 阵列写端口分配方案,即使不是所有条目都被占用,它如何导致 RS 停顿?这是否意味着 Haswell 中有四个写入端口,但现在 Broadwell 中只有两个?即使手册没有明确说明,这两种情况是否适用于 Skylake 或 Haswell?
performance - 矛盾的 VTune Amplifier 微架构探索结果
我正在尝试优化 sin/cos 逼近函数。它的核心是一个简单的霍纳方案,由一堆乘法和加法组成。编译器是 VS2017 的 MSVC,处理器是 Intel Xeon E5-1650,超线程开启(但如果关闭,观察结果基本相同)。
使用 Intel 的 VTune Amplifier 2019,我在随机双精度数(-2 pi 和 2 pi 之间)上运行函数超过 1 分钟获得了分析结果(当然是发布版本),在所示部分中花费了约 40% 的时钟下面(剩下的是范围缩小+测试工具)。但是,我无法理解 VTune 呈现给我的微架构指标:
以下是内联的相应 C++ 代码:
显然,汇编列表只有一个连续的指令块。没有跳转(也没有跳转目标),根本没有分支或条件执行。然而,这里有多个指标,我无法通过 VTune 的内置或在线帮助提供的信息来理解它们的值。
具体问题:
代码的后半部分几乎没有归属、时钟和所有内容。为什么?
上半年CPI不断攀升。好的,也许这点和前一点是由于归因出了问题,但我不明白。
指标表明存在不好的猜测。但是在扩展该列时,它既没有显示分支错误预测,也没有显示机器清除: 这应该告诉我什么?CPU在这里推测的容量是多少?
抢先注意事项:
重新实现这一点的目的是保证跨多个平台(来自同一个二进制文件)的一致性。内置的 sin/cos 函数可能会因机器之间的几个 ULP 而异,这可能会破坏结果的可重复性。
是的,我知道 FMA,但并不是每个必须运行这个(单个)二进制文件的平台都提供它们。目前我不打算进行运行时调度。