问题标签 [rdtsc]
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.
c - 在 C 编程中将 CPU 周期转换为秒
我试图弄清楚是否有任何简单的方法可以将使用 rdtsc() 函数在 C 中获得的 CPU 周期转换为以秒为单位的时间。
前任:-
有没有可能的方法将其转换为以秒为单位的时间?
linux - rdtsc 的 VMX 性能问题(没有 rdtsc 退出,使用 rdtsc 偏移)
我正在使用 Linux 内核模块 (VMM) 来测试 Intel VMX,运行自制 VM(VM 以实模式启动,然后切换到启用分页的 32 位保护模式)。
VMM 配置为不使用 rdtsc 退出,并使用 rdtsc 偏移。
然后,VM 运行 rdtsc 来检查性能,如下所示。
输出是这样的,
另一方面,我制作了一个主机应用程序来做同样的事情,就像上面一样
它输出以下内容,
在 40 次迭代中运行以上两个代码以获得平均值如下,
在 VM 和主机中运行代码时,性能差异不容忽视。这不是预期的。
我的理解是,使用 TSC 偏移 + 没有 RDTSC 退出,rdtsc 应该没有什么区别,运行在 VM 和主机上。
这是 VMCS 字段,
在 EPT PTE 的最后一级,bit[5:3] = 6(回写),bit[6] = 1。EPTP[2:0] = 6(回写)
我在裸机和 VMware 中进行了测试,我得到了类似的结果。
我想知道在这种情况下我是否遗漏了什么。
c - clock_gettime 和 getrusage 有什么关系?
我试图了解如何在 Linux 中计算 CPU 时间。特别是,我想把注意力集中在这个问题clock_gettime
上getrusage
。我想知道这两个是什么关系。
- 一个人打电话给另一个人吗?
- 它们是否都依赖于相同的底层机制?
- 如果是这样,他们最大的共同点是什么,我的意思是,他们都调用linux 内核中的什么函数来获得时间?
- 如果他们不依赖相同的底层机制,那为什么?
linux - 确定 Linux 上的 TSC 频率
给定一个具有恒定 TSC的 x86,这对于测量实时很有用,如何使用 Linux 在启动时计算的 TSC 校准因子在TSC 参考周期的“单位”和正常的人类实时单位(如纳秒)之间进行转换?
CLOCK_MONOTONIC
也就是说,当然可以通过在某个间隔的两端进行 TSC 和时钟测量(例如,用在内部使用 TSC 来帮助计时。
例如,您可以使用以下命令查看内核的结果dmesg | grep tsc
:
在更糟糕的情况下,我想你可以尝试dmesg
在运行时 grep 结果,但坦率地说,这看起来很糟糕,脆弱和各种糟糕的0。
使用内核确定的校准时间的优点有很多:
- 您不必自己编写 TSC 校准例程,而且您可以确定 Linux 是同类最佳的。
- 当新内核使用您现有的二进制文件出现时,您会自动在 TSC 校准中采用新技术(例如,最近芯片开始使用
cpuid
叶子 0x15 宣传其 TSC 频率,因此并不总是需要校准)。 - TSC 校准不会减慢您的启动速度。
- 您在每次运行进程时使用相同的 TSC 值(至少在重新启动之前)。
- 您的 TSC 频率在某种程度上与操作系统计时功能使用的 TSC 频率“一致”,例如
gettimeofday
andclock_gettime
1。 - 内核能够在启动时很早就在内核模式下进行 TSC 校准,不受中断和其他进程的影响,并且能够访问底层硬件定时器方向作为其校准源。
但这并不全是肉汁,使用 Linux 的 TSC 校准的一些缺点包括:
- 它不会在每个 Linux 安装(例如,可能不使用 tsc 时钟源的那些)或其他操作系统上都有效,因此您可能仍然无法编写回退校准方法。
- 有理由相信“最近”的校准可能比旧的校准更准确,尤其是在启动后立即进行的校准:晶体行为可能会发生变化,尤其是随着温度的变化,因此您可以通过这样做获得更准确的频率手动靠近您将使用它的位置。
0例如:系统可能尚未dmesg
安装,您可能无法以普通用户身份运行它,累积的输出可能已经回绕,因此这些行不再存在,您的 grep 可能会出现误报,内核消息是英文散文,可能会发生变化,可能很难启动子流程等。
1这是否重要有些争议 - 但如果您将rdtsc
调用与也使用操作系统计时的代码混合,它可能会提高精度。
c - 使用时间戳计数器和 clock_gettime 进行缓存未命中
作为本主题的后续,为了计算内存未命中延迟,我使用_mm_clflush
,__rdtsc
和_mm_lfence
(基于此问题/答案中的代码)编写了以下代码。
正如您在代码中看到的,我首先将数组加载到缓存中。然后我刷新一个元素,因此缓存行从所有缓存级别中逐出。我_mm_lfence
为了在-O3
.
接下来,我使用时间戳计数器来计算延迟或读数array[0]
。正如您在两个时间戳之间看到的,有三个指令:二lfence
和一read
。所以,我必须减去lfence
开销。代码的最后一部分计算了该开销。
在代码的最后,会打印开销和未命中延迟。但是,结果无效!
但是,输出无效
任何想法?
接下来,我尝试clock_gettime
了函数来计算未命中延迟,如下所示
输出是miss elapsed time = 578 nanoseconds
。那可靠吗?
更新1:
感谢彼得和哈迪,总结到目前为止的反应,我发现
1- 在优化阶段省略了未使用的变量,这就是我在输出中看到的奇怪值的原因。感谢彼得的回复,有一些方法可以解决这个问题。
2-
clock_gettime
不适合这种分辨率,该功能用于更大的延迟。
作为一种解决方法,我尝试将数组放入缓存中,然后刷新所有元素以确保所有元素都从所有缓存级别中逐出。然后我测量了然后的array[0]
延迟array[20]
。由于每个元素是 4 字节,因此距离是 80 字节。我希望得到两次缓存未命中。但是,延迟array[20]
类似于缓存命中。一个安全的猜测是高速缓存行不是 80 字节。因此,可能array[20]
是由硬件预取的。并非总是如此,但我也再次看到一些奇怪的结果
输出是
“硬件预取器带来其他块”的说法大约有 80% 正确。那是怎么回事?还有更准确的说法吗?
performance - rdtscp 的“半栅栏”行为是怎么回事?
多年来,x86 CPU 都支持该rdtsc
指令,该指令读取当前 CPU 的“时间戳计数器”。这个计数器的确切定义随着时间的推移而改变,但在最近的 CPU 上,它是一个相对于挂钟时间以固定频率递增的计数器,因此它作为快速、准确时钟或测量时间的构建块非常有用由小段代码占用。
rdtsc
关于指令的一个重要事实并没有以任何特殊的方式与周围的代码一起排序。像大多数指令一样,它可以相对于与它没有依赖关系的其他指令自由地重新排序。这实际上是“正常的”,对于大多数指令来说,它只是一种使 CPU 更快的几乎不可见的方式(这只是说乱序执行的一种冗长的方式)。
因为rdtsc
它很重要,因为这意味着您可能没有对您期望的代码进行计时。例如,给定以下序列1:
您可能希望rdtsc
测量两个指针追逐加载负载的延迟mov rdi, [rdi]
。然而,在实践中,即使这两个负载都需要查看时间(如果它们在缓存中未命中,则需要 100 秒的周期),您也会得到相当小的读数rdtsc
。问题是第二个rdtsc
不等待加载完成,它只是乱序执行,所以你没有计时你认为的间隔。也许这两rdtsc
条指令实际上甚至在第一次加载甚至开始之前就执行了,这取决于rdi
在此示例之前的代码中是如何计算的。
到目前为止,这听起来更像是一个没有人问过的问题的答案,而不是一个真正的问题,但我到了那里。
您有两个基本用例rdtsc
:
- 作为一个快速的时间戳,你通常不关心它如何与周围的代码重新排序,因为无论如何你可能没有关于应该在哪里获取时间戳的指令级概念。
作为一种精确的计时机制,例如在微基准测试中。在这种情况下,您通常会
rdtsc
根据说明保护您免于重新订购lfence
。对于上面的示例,您可能会执行以下操作:确保定时指令
...
(测量)。
多年后,英特尔看不起我们这些可怜的程序员,并提出了一条新指令:rdtscp
. 就像rdtsc
它返回时间戳计数器的读数一样,这个家伙做了更多的事情:它使用时间戳读数原子地读取特定于内核的 MSR 值。在大多数操作系统上,这包含一个核心 ID 值。我认为这个想法是,该值可用于在每个内核可能具有不同 TSC 偏移的 CPU 上将返回值正确调整为实时。
伟大的。
rdtscp
引入的另一件事是在乱序执行方面的半栅栏:
从手册:
RDTSCP 指令不是序列化指令,但它确实会等到所有先前的指令都已执行并且所有先前的加载都是全局可见的。1 但它不会等待先前的存储全局可见,并且后续指令可能在读取之前开始执行进行操作。
所以这就像在lfence
之前放一个rdtscp
,但不是在之后。这种半栅栏行为的意义何在?如果您想要一个通用时间戳并且不关心指令顺序,那么您想要的是不受限制的行为。如果您想将它用于计时短代码部分,半栅栏行为仅对第二次(最终)阅读有用,但不适用于初始阅读,因为栅栏位于“错误”一侧(实际上你想要两边都有栅栏,但把它们放在里面可能是最重要的)。
这种半栅栏有什么用?
1在这种情况下,我忽略了计数器的高 32 位。
assembly - 使用 rdtsc 对英特尔进行汇编程序基准测试给出了奇怪的答案,为什么?
前段时间,我问了一个关于堆栈溢出的问题,并展示了如何在 C++ 中执行 rdtsc 操作码。我最近使用 rdtsc 创建了一个基准函数,如下所示:
因此,我假设如果我对一个函数进行基准测试,我将(大致)拥有它执行所需的时钟周期数。我还假设如果我想减去进入或退出函数所需的时钟周期数,我应该对一个空函数进行基准测试,然后在里面编写一个包含所需代码的函数。
这是一个示例:
代码是使用编译的
我可以理解是否由于中断或其他条件而出现错误,但鉴于这些例程很短,并且 n 被选择得很小,我假设我可以看到实数。但令我惊讶的是,这是连续两次运行的输出
始终如一的空函数表明它需要的方式比它应该的要多。
毕竟,进出函数只涉及几条指令。真正的工作是在循环中完成的。不要介意差异巨大的事实。在第二次运行中,空函数声称需要 357 个时钟周期,而总和需要更少,这很荒谬。
怎么了?
x86 - 使用 rdtscp 指令测量延迟时如何获得 0 个时钟滴答?
我想测量一小段代码的延迟。因此,我添加了代码以在其前后添加 rdtscp 指令。问题是,我使用它测量的延迟结果是 0。
该进程固定在一个特定的核心上,因此不同 CPU 的不同步 tsc 寄存器不会成为问题。我知道我没有使用像 cpuid 这样的序列化指令,因此 rdtscp 指令可以在乱序 CPU 中重新排列。但是,这些应该仍然是两个不同的指令。据我所知, tsc 寄存器每个时钟周期都会更新。所以两条指令读取的值一定不一样!
我能想到的唯一可能原因是超线程 CPU 会同时发出两条指令。这是对的吗?
assembly - 动态监控 rdtsc 的性能
有没有办法使用 perf 动态地“实时”监控汇编指令?我已经看到,如果我使用 perf record /perf top 然后单击记录的函数,我会看到汇编指令,但我可以直接监控特定的汇编指令,例如 rdtsc 或 clflush 例如,它们被内部进程调用的频率特定时期使用性能?
我在 Skylake 和 Haswell 上使用 Debian 9。
返回 command not found
。
任何帮助/想法表示赞赏。