我正在研究RDTSC,并了解它是如何虚拟化虚拟机,如VirtualBox和VMWare.为什么Intel/AMD会遇到虚拟化此指令的麻烦?
我觉得它可以很容易地用陷阱来模拟它并不是一个超常用的指令(我测试过并且在禁用硬件RDTSC虚拟化的虚拟机中一般用法没有明显的减速).
但是,我知道英特尔/ AMD不会把这个指令添加到虚拟化硬件中,除非能够非常快速地执行它是很重要的.
有谁知道为什么?
在我的E8200机箱上,这不会发生,但是在我的Atom N450上网本(都运行OpenSuse 11.2)上,每当我读取CPU的TSC时,返回的值是mod 10 == 0,即没有被10整除的余数.我正在使用RDTSC用于测量有趣代码片段的时间的值,但为了演示的目的,我已经编写了这个小程序:
.text
.global _start
_start: xorl %ebx,%ebx
xorl %ecx,%ecx
xorl %r14d,%r14d
movb $10,%cl
loop: xchgq %rcx,%r15 # save to reg
cpuid
rdtsc
shlq $32,%rdx
xorq %rax,%rdx # full 64 bit of RDTSC
movq %r14,%r13 # save the old value
movq %rdx,%r14 # copy current
movq %r14,%rsi # argv[1] of printf()
subq %r13,%rdx # argv[2] (delta)
leaq format(%rip),%rdi # argv[0]
xorl %eax,%eax # no stack varargs
call printf
xchgq %rcx,%r15
loop loop
0: xorl …Run Code Online (Sandbox Code Playgroud) 我试图确定读取元素所需的时间,以确保它是缓存命中或缓存未命中.为了阅读顺序我使用_mm_lfence()函数.我得到了意想不到的结果,经过检查后,我看到lfence函数的开销不确定.所以我正在执行程序,在例如100 000次迭代的循环中测量这种开销.我得到一次迭代的超过1000个时钟周期的结果,下次是200.这可能是lfence函数开销之间存在这种差异的原因,如果它是如此不可靠,我怎样才能正确判断缓存命中和缓存未命中的延迟?我试图使用与此帖相同的方法:使用时间戳计数器进行内存延迟测量
给出不可靠结果的代码是这样的:
for(int i=0; i < arr_size; i++){
_mm_mfence();
_mm_lfence();
t1 = __rdtsc();
_mm_lfence();
_mm_lfence();
t2 = __rdtsc();
_mm_lfence();
arr[i] = t2-t1;
}
Run Code Online (Sandbox Code Playgroud)
arr中的值在不同的范围内变化,arr_size为100 000.
似乎AMD和英特尔最新的CPU都将rdtsc作为恒定速率计数器实现,避免了TurboBoost或省电设置等因频率变化引起的问题.
由于rdtsc比QueryPerformanceCounter更适合性能测量,因为它的开销要低得多,我想尽可能地使用它.
如果rdtsc是一个恒定速率计数器,我如何可靠地检测?
有人可以帮我理解https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html中给出的汇编程序
它是这样的:
uint64_t msr;
asm volatile ( "rdtsc\n\t" // Returns the time in EDX:EAX.
"shl $32, %%rdx\n\t" // Shift the upper bits left.
"or %%rdx, %0" // 'Or' in the lower bits.
: "=a" (msr)
:
: "rdx");
Run Code Online (Sandbox Code Playgroud)
它与以下内容有何不同:
uint64_t msr;
asm volatile ( "rdtsc\n\t"
: "=a" (msr));
Run Code Online (Sandbox Code Playgroud)
为什么我们需要转移和/或操作以及rdx到底有什么作用?
编辑:添加了原始问题尚不清楚的内容.
回顾一下.第一行加载寄存器eax和edx中的时间戳.第二行将eax中的值移位并存储在rdx中.第三行将edx中的值与rdx中的值一起使用,并将其保存在rdx中.第四行将rdx中的值赋给我的变量.最后一行将rdx设置为0.
再次感谢!:)
EDIT2:回答了我的一些问题......
我读过 RDTSC 可以给出错误的读数,不应依赖。
这是真的吗?如果是这样,可以做些什么?
假设我的CPU中的所有内核具有相同的频率,从技术上讲,我可以每毫秒左右为每个内核同步系统时间和时间戳计数器对.然后根据我正在运行的当前核心,我可以获取当前rdtsc值并使用tick delta除以核心频率,我能够估计自上次同步系统时间和时间戳计数器对后经过的时间.推断当前系统时间而没有来自当前线程的系统调用开销(假设不需要锁来检索上述数据).这在理论上很有效,但在实践中我发现有时我会得到更多的滴答,然后我会期望,也就是说,如果我的核心频率为1GHz并且我花了1毫秒的系统时间和时间戳计数器对,我希望看到一个delta在大约10 ^ 6个刻度的刻度线中,但实际上我发现它可以在10 ^ 6到10 ^ 7之间的任何位置.我不确定有什么问题,有人可以分享他对如何计算系统时间的看法rdtsc吗?我的主要目标是避免每次我想知道系统时间时执行系统调用的需要,并且能够在用户空间中执行计算,这将给我一个很好的估计(目前我定义了一个很好的估计结果)与实际系统时间间隔为10微秒.
不同的进程可以同时运行RDTSC吗?还是这是只有一个内核可以同时运行的资源?TSC位于每个内核中(至少您可以针对每个内核分别进行调整),因此应该可行。但是超级跑步呢?
我该如何测试?
我使用 C++ 对多个 NOP 指令和单个 NOP 指令进行计时rdtsc。但是,我没有发现执行 NOP 所需的周期数与执行的 NOP 数成正比。我很困惑为什么会出现这种情况。我的 CPU 是 Intel Core i7-5600U @ 2.60Ghz。
这是代码:
#include <stdio.h>
int main() {
unsigned long long t;
t = __rdtsc();
asm volatile("nop");
t = __rdtsc() - t;
printf("rdtsc for one NOP: %llu\n", t);
t = __rdtsc();
asm volatile("nop; nop; nop; nop; nop; nop; nop;");
t = __rdtsc() - t;
printf("rdtsc for seven NOPs: %llu\n", t);
}
Run Code Online (Sandbox Code Playgroud)
我得到的值如下:
rdtsc for one NOP: 78
rdtsc for seven NOPs: 91 …Run Code Online (Sandbox Code Playgroud) 我曾经使用 rdtsc 对 Linux 系统调用进行基准测试,以获取系统调用前后的计数器差异。我将结果解释为挂钟定时器,因为 TSC 以恒定速率递增,并且在进入暂停状态时不会停止。
不变 TSC 概念描述为
不变的 TSC 将在所有 ACPI P-、C- 中以恒定速率运行。和 T 状态。
当状态从 C0(运行)更改为 C1(停止)时,恒定非不变tsc 是否可以改变频率?
我当前的观点是,它不能仅在性能(P)状态下更改频率。因此,在使用非不变 tsc 时,应用 rdtsc 获取用于系统调用的挂钟计时器并不可靠。
我在 my 中没有找到不变的 tsc 标志/proc/cpuinfo,只是constant_tsc意味着它没有必要不变。
混乱的根源是英特尔系统编程手册中的一句话:
较新处理器中的时间戳计数器可能支持增强功能,称为不变 TSC。
所以有些芯片(包括我的)具有恒定的 tsc,但不是不变的。