- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发 low level routines for binary search in C和 x64 程序集,并尝试测量搜索未缓存数组(RAM 中的数据)的确切执行时间。根据分支预测的“幸运”程度,为不同的目标搜索相同的数组所花费的时间差异很大。我可以准确地测量最小和中值执行时间,但我发现很难测量最大执行时间。
问题是分支预测的最坏情况在时间上与平均情况加上处理器中断相当。最坏情况和中断都很少见,但我还没有想出一个好方法来区分一个罕见事件和另一个。标准方法是简单地过滤掉所有“异常”高的测量值,但这只有在两者之间有明确界限的情况下才有效。
所以问题就变成了,“我如何区分被中断的测量和合理地花费比其他时间长得多的测量?”
或者更一般地说,“如何在不预先假设硬最大值的情况下测量执行时间的完整分布?”
内核是否存储了任何我可以查询的关于是否发生中断的信息?我可以在测量前后查询的东西会告诉我测量是否中断?理想情况下,它会告诉我中断花费了多长时间的周期,但仅仅知道测量受到影响将是一个很好的开始。
也许除了(或代替)RDTSC,我可以使用 RDPMC 来读取一个计数器,该计数器测量在 Ring 0(内核)而不是 Ring 3(用户)中花费的周期数?是否已经设置了一个计数器来执行此操作,还是我需要设置自己的?我需要创建自己的内核模块来执行此操作,还是可以使用现有的 ioctl?
一些背景:
我主要在 Intel Skylake i7-6700 上运行 Ubuntu 14.03 Linux 4.2.0,但也在 Intel Sandy Bridge 和 Haswell 上进行测试。我已经尽力减少系统上的抖动。我用 CONFIG_NOHZ_FULL 重新编译了一个 tickless 内核,没有强制抢占,透明大页面支持关闭,定时器频率为 100 Hz。
我已经停止了大部分不必要的进程,并删除了大部分不必要的内核模块。我正在使用 cpuset / cset shield为单个进程保留一个 NoHZ 内核,并使用内核/调试/跟踪来验证我得到的中断很少。但我仍然得到足够的精确测量是困难的。也许更重要的是,我可以设想 future 的长尾情况(很少需要调整大小的哈希表),在这种情况下,能够区分有效和无效测量将非常有帮助
我正在使用 RDTSC/RDTSCP 使用 Intel suggests in their whitepaper 的技术测量执行时间,并且通常能达到我期望的准确度。我的测试涉及搜索 16 位值,并且我对长度可变的随机数组的 65536 次可能搜索中的每一次重复和单独计时。为了防止处理器学习正确的分支预测,每次都以不同的顺序重复搜索。每次使用“CLFLUSH”搜索后,搜索到的数组都会从缓存中删除。
这是一个研究项目,我的目标是了解这些问题。因此,我愿意采用在其他情况下可能被认为愚蠢和极端的方法。自定义内核模块、保护模式 x64 程序集、未经测试的内核修改和处理器特定功能都是公平的游戏。如果有办法摆脱剩余的少数中断,使所有测量都是“真实的”,那也可能是一个可行的解决方案。感谢您的建议!
最佳答案
我假设您已经在一定程度上屏蔽了基准测试线程可能的:
此外,你不应该从你的内部跳入内核空间基准代码路径:返回时,您的线程可能会被安排离开一段时间。
但是,您根本无法消除 CPU 内核上的所有中断:onLinux,本地APIC定时器中断,处理器间中断(IPI) 和其他用于内部目的,你根本不能摆脱他们!例如,定时器中断用于确保,线程最终得到调度。同样,IPI 用于触发其他核心的 Action ,例如 TLB 击落。
现在,多亏了 Linux 跟踪基础设施,可以从用户空间判断一个hardirq 在某个时间段内发生。
一个小问题是 Linux 处理两类中断在跟踪方面有所不同:
从处理器异步的意义上来说,两者都是硬中断将控制转移到中断服务例程 (ISR),由中断描述符表 (IDT)。
通常,在 Linux 中,ISR 只是一个用汇编语言编写的 stub ,它将控制转移到用 C 编写的高级处理程序。
有关详细信息,请参阅 Linux 内核源代码中的 arch/x86/entry_entry_64.S
。对于 Linux 内部中断,每个都定义了一个跟踪 stub 而对于外部中断,tracing留给高层中断处理程序。
这是每个内部中断都有一个跟踪事件的方式:
# sudo perf list | grep irq_vectors:
irq_vectors:call_function_entry [Tracepoint event]
irq_vectors:call_function_exit [Tracepoint event]
irq_vectors:call_function_single_entry [Tracepoint event]
irq_vectors:call_function_single_exit [Tracepoint event]
irq_vectors:deferred_error_apic_entry [Tracepoint event]
irq_vectors:deferred_error_apic_exit [Tracepoint event]
irq_vectors:error_apic_entry [Tracepoint event]
irq_vectors:error_apic_exit [Tracepoint event]
irq_vectors:irq_work_entry [Tracepoint event]
irq_vectors:irq_work_exit [Tracepoint event]
irq_vectors:local_timer_entry [Tracepoint event]
irq_vectors:local_timer_exit [Tracepoint event]
irq_vectors:reschedule_entry [Tracepoint event]
irq_vectors:reschedule_exit [Tracepoint event]
irq_vectors:spurious_apic_entry [Tracepoint event]
irq_vectors:spurious_apic_exit [Tracepoint event]
irq_vectors:thermal_apic_entry [Tracepoint event]
irq_vectors:thermal_apic_exit [Tracepoint event]
irq_vectors:threshold_apic_entry [Tracepoint event]
irq_vectors:threshold_apic_exit [Tracepoint event]
irq_vectors:x86_platform_ipi_entry [Tracepoint event]
irq_vectors:x86_platform_ipi_exit [Tracepoint event]
虽然外部中断只有一个通用跟踪事件:
# sudo perf list | grep irq:
irq:irq_handler_entry [Tracepoint event]
irq:irq_handler_exit [Tracepoint event]
irq:softirq_entry [Tracepoint event]
irq:softirq_exit [Tracepoint event]
irq:softirq_raise [Tracepoint event]
因此,在基准测试期间跟踪所有这些 IRQ *_entries
代码路径,你就知道你的基准测试样本是否中毒了是否通过 IRQ。
注意x86上还有第三种硬件中断方式:异常(exception)。至少,我也会检查页面错误。对于上面遗漏的 NMI(通过 nmi:nmi_handler
)。
为了您的方便,我整理了一份 little piece of code为了在基准测试代码路径中跟踪 IRQ。请参阅包含的 example.c
用法。请注意,需要访问 /sys/kernel/debug
才能确定跟踪点 ID。
关于linux-kernel - 使用 RDTSC 精确测量最大循环计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35191760/
我是一名优秀的程序员,十分优秀!