首页 > 解决方案 > rdtscp 的“半栅栏”行为是怎么回事?

问题描述

多年来,x86 CPU 都支持该rdtsc指令,该指令读取当前 CPU 的“时间戳计数器”。这个计数器的确切定义随着时间的推移而改变,但在最近的 CPU 上,它是一个相对于挂钟时间以固定频率递增的计数器,因此它作为快速、准确时钟或测量时间的构建块非常有用由小段代码占用。

rdtsc关于指令的一个重要事实并没有以任何特殊的方式与周围的代码一起排序。像大多数指令一样,它可以相对于与它没有依赖关系的其他指令自由地重新排序。这实际上是“正常的”,对于大多数指令来说,它只是一种使 CPU 更快的几乎不可见的方式(这只是说乱序执行的一种冗长的方式)。

因为rdtsc它很重要,因为这意味着您可能没有对您期望的代码进行计时。例如,给定以下序列1

rdtsc
mov ecx, eax
mov rdi, [rdi]
mov rdi, [rdi]
rdtsc

您可能希望rdtsc测量两个指针追逐加载负载的延迟mov rdi, [rdi]。然而,在实践中,即使这两个负载都需要查看时间(如果它们在缓存中未命中,则需要 100 秒的周期),您也会得到相当小的读数rdtsc。问题是第二个rdtsc不等待加载完成,它只是乱序执行,所以你没有计时你认为的间隔。也许这两rdtsc条指令实际上甚至在第一次加载甚至开始之前就执行了,这取决于rdi在此示例之前的代码中是如何计算的。

到目前为止,这听起来更像是一个没有人问过的问题的答案,而不是一个真正的问题,但我到了那里。

您有两个基本用例rdtsc

多年后,英特尔看不起我们这些可怜的程序员,并提出了一条新指令:rdtscp. 就像rdtsc它返回时间戳计数器的读数一样,这个家伙做了更多的事情:它使用时间戳读数原子地读取特定于内核的 MSR 值。在大多数操作系统上,这包含一个核心 ID 值。我认为这个想法是,该值可用于在每个内核可能具有不同 TSC 偏移的 CPU 上将返回值正确调整为实时。

伟大的。

rdtscp引入的另一件事是在乱序执行方面的半栅栏:

手册

RDTSCP 指令不是序列化指令,但它确实会等到所有先前的指令都已执行并且所有先前的加载都是全局可见的。1 但它不会等待先前的存储全局可见,并且后续指令可能在读取之前开始执行进行操作。

所以这就像在lfence之前放一个rdtscp,但不是在之后。这种半栅栏行为的意义何在?如果您想要一个通用时间戳并且不关心指令顺序,那么您想要的是不受限制的行为。如果您想将它用于计时短代码部分,半栅栏行为仅对第二次(最终)阅读有用,但不适用于初始阅读,因为栅栏位于“错误”一侧(实际上你想要两边都有栅栏,但把它们放在里面可能是最重要的)。

这种半栅栏有什么用?


1在这种情况下,我忽略了计数器的高 32 位。

标签: performanceassemblyx86microbenchmarkrdtsc

解决方案


推荐阅读