为什么 CPUID + RDTSC 不可靠?

Why is CPUID + RDTSC unreliable?

我正在尝试分析代码在 x86-64 处理器上的执行时间。我指的是 this Intel white paper and also gone through other SO threads discussing the topic of using RDTSCP vs CPUID+RDTSC here and here.

在上面提到的白皮书中,使用CPUID+RDTSC的方法被称为不可靠,也被统计证明。

CPUID+RDTSC不可靠是什么原因?

另外,同一白皮书中的图 1(最小值行为图)和图 2(方差行为图)中的图形具有 "Square wave" 模式。 如何解释这种模式?

我认为他们发现测量间隔内的 CPUID 会导致总时间的额外可变性。他们在 3.2 Improvements Using RDTSCP Instruction 中提出的修复强调了这样一个事实,即当他们使用 CPUID / RDTSC 开始时,在时间间隔内没有 CPUID,并且 RDTSCP/CPUID停止。

也许他们可以在执行 CPUID 之前确保 EAX=0 或 EAX=1,以选择要读取的数据的 CPUID 叶子 (http://www.sandpile.org/x86/cpuid.htm#level_0000_0000h),以防 CPUID 所花费的时间取决于您进行的查询。除此之外,我不确定为什么会这样。

或者更好的是,使用 lfence 而不是 cpuid 来序列化 OoO exec 而不是完整的序列化操作。


请注意,英特尔白皮书中的内联汇编很糟糕:如果您使用 "=a"(low), "=d"(high) 等适当的输出约束,则不需要那些 mov 指令。有关更好的方法,请参阅 How to get the CPU cycle count in x86_64 from C++?

CPUID+RDTSC 不可靠的另一个原因是 VM 边信道攻击。

当VM内部的运行 CPUID指令会导致VM EXIT时,发生这种情况是因为VM会根据需要处理CPUID并操纵CPUID指令。
进行此操作会增加额外的时间,并且使用 RDTSC 将 return "high" 值,因为 "the entire VM CPUID manipulation" 是在那个时间执行的。
然后可以使用此值来检测我们在 VM 中 运行。

可以扩展或虚拟化 TSC 的 VM 可以阻止此行为,从而使 RDTSC 变得不可靠

Detecting VM Exit Overhead